add tests for a thumb program calling arm code

and in particular for naked functions in that scenario
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 939160d..70bed5a 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 @@ 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 {
diff --git a/tests/run-make/thumb-interworking/link.ld b/tests/run-make/thumb-interworking/link.ld
new file mode 100644
index 0000000..b08e0fb
--- /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 0000000..86099b0
--- /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: <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",);
+}
diff --git a/tests/run-make/thumb-interworking/rmake.rs b/tests/run-make/thumb-interworking/rmake.rs
new file mode 100644
index 0000000..1aa39ed
--- /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();
+}