From 2d8ceee19c664cd6f8fef55b4fd08ffd943365fc Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 26 Feb 2026 21:25:30 +0100 Subject: [PATCH] add tests for a thumb program calling arm code and in particular for naked functions in that scenario --- .../src/external_deps/llvm.rs | 12 ++ tests/run-make/thumb-interworking/link.ld | 1 + tests/run-make/thumb-interworking/main.rs | 118 ++++++++++++++++++ tests/run-make/thumb-interworking/rmake.rs | 52 ++++++++ 4 files changed, 183 insertions(+) create mode 100644 tests/run-make/thumb-interworking/link.ld create mode 100644 tests/run-make/thumb-interworking/main.rs create mode 100644 tests/run-make/thumb-interworking/rmake.rs diff --git a/src/tools/run-make-support/src/external_deps/llvm.rs b/src/tools/run-make-support/src/external_deps/llvm.rs index 939160d9f41d8..70bed5a471ab2 100644 --- a/src/tools/run-make-support/src/external_deps/llvm.rs +++ b/src/tools/run-make-support/src/external_deps/llvm.rs @@ -302,6 +302,12 @@ impl LlvmFilecheck { self.cmd.arg(input_file.as_ref()); self } + + /// Set a single `--check-prefix`. + pub fn check_prefix>(&mut self, prefix: S) -> &mut Self { + self.cmd.arg(format!("--check-prefix={}", prefix.as_ref())); + self + } } impl LlvmObjdump { @@ -324,6 +330,12 @@ impl LlvmObjdump { self.cmd.arg("-d"); self } + + /// Demangle symbols. + pub fn demangle(&mut self) -> &mut Self { + self.cmd.arg("--demangle"); + self + } } impl LlvmAr { diff --git a/tests/run-make/thumb-interworking/link.ld b/tests/run-make/thumb-interworking/link.ld new file mode 100644 index 0000000000000..b08e0fb66eaf0 --- /dev/null +++ b/tests/run-make/thumb-interworking/link.ld @@ -0,0 +1 @@ +ENTRY(entry); diff --git a/tests/run-make/thumb-interworking/main.rs b/tests/run-make/thumb-interworking/main.rs new file mode 100644 index 0000000000000..86099b0ee63fc --- /dev/null +++ b/tests/run-make/thumb-interworking/main.rs @@ -0,0 +1,118 @@ +#![feature(no_core)] +#![no_core] +#![no_main] + +extern crate minicore; +use minicore::*; + +#[unsafe(no_mangle)] +fn entry() { + arm(); + thumb(); +} + +#[unsafe(no_mangle)] +pub fn arm() { + // thumbv5te-LABEL: : + // thumbv5te: blx {{0x[0-9a-f]+}} + // thumbv5te: blx {{0x[0-9a-f]+}} + // thumbv5te: blx {{0x[0-9a-f]+}} + + // thumbv4t-LABEL: : + // thumbv4t: bl {{0x[0-9a-f]+}} <__Thumbv4ABSLongBXThunk__{{.*}}arm_normalfn> + // thumbv4t: bl {{0x[0-9a-f]+}} <__Thumbv4ABSLongBXThunk_arm_globalfn> + // thumbv4t: bl {{0x[0-9a-f]+}} <__Thumbv4ABSLongBXThunk__{{.*}}arm_nakedfn> + + // armv5te-LABEL: : + // armv5te: bl {{0x[0-9a-f]+}} + // armv5te: bl {{0x[0-9a-f]+}} + // armv5te: bl {{0x[0-9a-f]+}} + + // armv4t-LABEL: : + // armv4t: bl {{0x[0-9a-f]+}} + // armv4t: bl {{0x[0-9a-f]+}} + // armv4t: bl {{0x[0-9a-f]+}} + arm_normalfn(); + arm_globalfn(); + arm_nakedfn(); +} + +#[unsafe(no_mangle)] +pub fn thumb() { + // thumbv5te-LABEL: : + // thumbv5te: bl {{0x[0-9a-f]+}} + // thumbv5te: bl {{0x[0-9a-f]+}} + // thumbv5te: bl {{0x[0-9a-f]+}} + + // thumbv4t-LABEL: : + // thumbv4t: bl {{0x[0-9a-f]+}} + // thumbv4t: bl {{0x[0-9a-f]+}} + // thumbv4t: bl {{0x[0-9a-f]+}} + + // armv5te-LABEL: : + // armv5te: blx {{0x[0-9a-f]+}} + // armv5te: blx {{0x[0-9a-f]+}} + // armv5te: blx {{0x[0-9a-f]+}} + + // armv4t-LABEL: : + // armv4t: bl {{0x[0-9a-f]+}} <__ARMv4ABSLongBXThunk__{{.*}}thumb_normalfn> + // armv4t: bl {{0x[0-9a-f]+}} <__ARMv4ABSLongBXThunk_thumb_globalfn> + // armv4t: bl {{0x[0-9a-f]+}} <__ARMv4ABSLongBXThunk__{{.*}}thumb_nakedfn> + thumb_normalfn(); + thumb_globalfn(); + thumb_nakedfn(); +} + +#[instruction_set(arm::t32)] +extern "C" fn thumb_normalfn() { + unsafe { asm!("nop") } +} + +unsafe extern "C" { + safe fn thumb_globalfn(); +} + +global_asm!( + r#" + .thumb + .global thumb_globalfn + .type thumb_globalfn, %function + thumb_globalfn: + nop + bx lr + .size thumb_globalfn, . - thumb_globalfn +"# +); + +#[unsafe(naked)] +#[instruction_set(arm::t32)] +extern "C" fn thumb_nakedfn() { + naked_asm!("nop", "bx lr",); +} + +#[instruction_set(arm::a32)] +extern "C" fn arm_normalfn() { + unsafe { asm!("nop") } +} + +unsafe extern "C" { + safe fn arm_globalfn(); +} + +global_asm!( + r#" + .arm + .global arm_globalfn + .type arm_globalfn, %function + arm_globalfn: + nop + bx lr + .size arm_globalfn, . - arm_globalfn +"# +); + +#[unsafe(naked)] +#[instruction_set(arm::a32)] +extern "C" fn arm_nakedfn() { + naked_asm!("nop", "bx lr",); +} diff --git a/tests/run-make/thumb-interworking/rmake.rs b/tests/run-make/thumb-interworking/rmake.rs new file mode 100644 index 0000000000000..1aa39ed1ce159 --- /dev/null +++ b/tests/run-make/thumb-interworking/rmake.rs @@ -0,0 +1,52 @@ +//@ needs-llvm-components: arm +//@ needs-rust-lld +use run_make_support::{llvm_filecheck, llvm_objdump, path, rfs, run, rustc, source_root}; + +// Test a thumb target calling arm functions. Doing so requires switching from thumb mode to arm +// mode, calling the arm code, then switching back to thumb mode. Depending on the thumb version, +// this happens using a special calling instruction, or by calling a generated thunk that performs +// the mode switching. +// +// In particular this tests that naked functions behave like normal functions. Before LLVM 22, a +// bug in LLVM caused thumb mode to be used unconditonally when symbols were .hidden, miscompiling +// calls to arm functions. +// +// - https://github.com/llvm/llvm-project/pull/181156 +// - https://github.com/rust-lang/rust/issues/151946 + +fn main() { + // Thumb calling thumb and arm. + helper("thumbv5te", "thumbv5te-none-eabi"); + helper("thumbv4t", "thumbv4t-none-eabi"); + + // Arm calling thumb and arm. + helper("armv5te", "armv5te-none-eabi"); + helper("armv4t", "armv4t-none-eabi"); +} + +fn helper(prefix: &str, target: &str) { + rustc() + .input(source_root().join("tests/auxiliary/minicore.rs")) + .crate_name("minicore") + .crate_type("rlib") + .target(target) + .output("libminicore.rlib") + .run(); + let minicore = path("libminicore.rlib"); + + rustc() + .input("main.rs") + .panic("abort") + .link_arg("-Tlink.ld") + .arg("--extern") + .arg(format!("minicore={}", minicore.display())) + .target(target) + .output(prefix) + .run(); + + let dump = llvm_objdump().disassemble().demangle().input(path(prefix)).run(); + + eprintln!("{}", str::from_utf8(&dump.stdout()).unwrap()); + + llvm_filecheck().patterns("main.rs").check_prefix(prefix).stdin_buf(dump.stdout()).run(); +}