add tests for a thumb program calling arm code

and in particular for naked functions in that scenario
This commit is contained in:
Folkert de Vries
2026-02-26 21:25:30 +01:00
parent bb779a9156
commit 2d8ceee19c
4 changed files with 183 additions and 0 deletions
@@ -302,6 +302,12 @@ pub fn input_file<P: AsRef<Path>>(&mut self, input_file: P) -> &mut Self {
self.cmd.arg(input_file.as_ref());
self
}
/// Set a single `--check-prefix`.
pub fn check_prefix<S: AsRef<str>>(&mut self, prefix: S) -> &mut Self {
self.cmd.arg(format!("--check-prefix={}", prefix.as_ref()));
self
}
}
impl LlvmObjdump {
@@ -324,6 +330,12 @@ pub fn disassemble(&mut self) -> &mut Self {
self.cmd.arg("-d");
self
}
/// Demangle symbols.
pub fn demangle(&mut self) -> &mut Self {
self.cmd.arg("--demangle");
self
}
}
impl LlvmAr {
@@ -0,0 +1 @@
ENTRY(entry);
+118
View File
@@ -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: <arm>:
// thumbv5te: blx {{0x[0-9a-f]+}} <main::arm_normalfn>
// thumbv5te: blx {{0x[0-9a-f]+}} <arm_globalfn>
// thumbv5te: blx {{0x[0-9a-f]+}} <main::arm_nakedfn>
// thumbv4t-LABEL: <arm>:
// 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: <arm>:
// armv5te: bl {{0x[0-9a-f]+}} <main::arm_normalfn>
// armv5te: bl {{0x[0-9a-f]+}} <arm_globalfn>
// armv5te: bl {{0x[0-9a-f]+}} <main::arm_nakedfn>
// armv4t-LABEL: <arm>:
// armv4t: bl {{0x[0-9a-f]+}} <main::arm_normalfn>
// armv4t: bl {{0x[0-9a-f]+}} <arm_globalfn>
// armv4t: bl {{0x[0-9a-f]+}} <main::arm_nakedfn>
arm_normalfn();
arm_globalfn();
arm_nakedfn();
}
#[unsafe(no_mangle)]
pub fn thumb() {
// thumbv5te-LABEL: <thumb>:
// thumbv5te: bl {{0x[0-9a-f]+}} <main::thumb_normalfn>
// thumbv5te: bl {{0x[0-9a-f]+}} <thumb_globalfn>
// thumbv5te: bl {{0x[0-9a-f]+}} <main::thumb_nakedfn>
// thumbv4t-LABEL: <thumb>:
// thumbv4t: bl {{0x[0-9a-f]+}} <main::thumb_normalfn>
// thumbv4t: bl {{0x[0-9a-f]+}} <thumb_globalfn>
// thumbv4t: bl {{0x[0-9a-f]+}} <main::thumb_nakedfn>
// armv5te-LABEL: <thumb>:
// armv5te: blx {{0x[0-9a-f]+}} <main::thumb_normalfn>
// armv5te: blx {{0x[0-9a-f]+}} <thumb_globalfn>
// armv5te: blx {{0x[0-9a-f]+}} <main::thumb_nakedfn>
// armv4t-LABEL: <thumb>:
// 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",);
}
@@ -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();
}