From e8ce9fac85ed952f8d4ce01102a3b2dcfab218a5 Mon Sep 17 00:00:00 2001
From: Damian Heaton <damian.heaton@arm.com>
Date: Thu, 13 Jun 2024 10:55:59 +0000
Subject: [PATCH 1/7] Add tests to ensure MTE tags are preserved across FFI
 boundaries

Added run-make tests to verify that, between a Rust-C FFI boundary in both directions,
any MTE tags included in a pointer are preserved for the following pointer types, as
well as any information stored using TBI:
- int
- float
- string
- function
---
 tests/run-make/mte-ffi/bar.h           | 43 +++++++++++++++++++++++
 tests/run-make/mte-ffi/bar_float.c     | 44 +++++++++++++++++++++++
 tests/run-make/mte-ffi/bar_function.c  | 39 +++++++++++++++++++++
 tests/run-make/mte-ffi/bar_int.c       | 47 +++++++++++++++++++++++++
 tests/run-make/mte-ffi/bar_string.c    | 48 ++++++++++++++++++++++++++
 tests/run-make/mte-ffi/foo_float.rs    | 19 ++++++++++
 tests/run-make/mte-ffi/foo_function.rs | 17 +++++++++
 tests/run-make/mte-ffi/foo_int.rs      | 19 ++++++++++
 tests/run-make/mte-ffi/foo_string.rs   | 27 +++++++++++++++
 tests/run-make/mte-ffi/rmake.rs        | 39 +++++++++++++++++++++
 10 files changed, 342 insertions(+)
 create mode 100644 tests/run-make/mte-ffi/bar.h
 create mode 100644 tests/run-make/mte-ffi/bar_float.c
 create mode 100644 tests/run-make/mte-ffi/bar_function.c
 create mode 100644 tests/run-make/mte-ffi/bar_int.c
 create mode 100644 tests/run-make/mte-ffi/bar_string.c
 create mode 100644 tests/run-make/mte-ffi/foo_float.rs
 create mode 100644 tests/run-make/mte-ffi/foo_function.rs
 create mode 100644 tests/run-make/mte-ffi/foo_int.rs
 create mode 100644 tests/run-make/mte-ffi/foo_string.rs
 create mode 100644 tests/run-make/mte-ffi/rmake.rs

diff --git a/tests/run-make/mte-ffi/bar.h b/tests/run-make/mte-ffi/bar.h
new file mode 100644
index 0000000000000..a2292ae02a308
--- /dev/null
+++ b/tests/run-make/mte-ffi/bar.h
@@ -0,0 +1,43 @@
+#ifndef __BAR_H
+#define __BAR_H
+
+#include <sys/mman.h>
+#include <sys/auxv.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+#include <stdio.h>
+
+// Set the allocation tag on the destination address using the STG instruction.
+#define set_tag(tagged_addr) do {                                      \
+    asm volatile("stg %0, [%0]" : : "r" (tagged_addr) : "memory"); \
+} while (0)
+
+int mte_enabled() {
+    return (getauxval(AT_HWCAP2)) & HWCAP2_MTE;
+}
+
+void *alloc_page() {
+    // Enable MTE with synchronous checking
+    if (prctl(PR_SET_TAGGED_ADDR_CTRL,
+              PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC | (0xfffe << PR_MTE_TAG_SHIFT),
+              0, 0, 0))
+    {
+        perror("prctl() failed");
+    }
+
+    // Using `mmap` allows us to ensure that, on systems which support MTE, the allocated
+    // memory is 16-byte aligned for MTE.
+    // This also allows us to explicitly specify whether the region should be protected by
+    // MTE or not.
+    if (mte_enabled()) {
+        void *ptr = mmap(NULL, sysconf(_SC_PAGESIZE),
+                         PROT_READ | PROT_WRITE | PROT_MTE, MAP_PRIVATE | MAP_ANONYMOUS,
+                         -1, 0);
+    } else {
+        void *ptr = mmap(NULL, sysconf(_SC_PAGESIZE),
+                         PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS,
+                         -1, 0);
+    }
+}
+
+#endif // __BAR_H
diff --git a/tests/run-make/mte-ffi/bar_float.c b/tests/run-make/mte-ffi/bar_float.c
new file mode 100644
index 0000000000000..a1590f62765a6
--- /dev/null
+++ b/tests/run-make/mte-ffi/bar_float.c
@@ -0,0 +1,44 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "bar.h"
+
+extern void foo(char*);
+
+void bar(char *ptr) {
+    if (((uintptr_t)ptr >> 56) != 0x1f) {
+        fprintf(stderr, "Top byte corrupted on Rust -> C FFI boundary!\n");
+        exit(1);
+    }
+}
+
+int main(void)
+{
+    float *ptr = alloc_page();
+    if (ptr == MAP_FAILED)
+    {
+        perror("mmap() failed");
+        return EXIT_FAILURE;
+    }
+
+    // Store an arbitrary tag in bits 56-59 of the pointer (where an MTE tag may be),
+    // and a different value in the ignored top 4 bits.
+    ptr = (float *)((uintptr_t)ptr | 0x1fl << 56);
+
+    if (mte_enabled()) {
+        set_tag(ptr);
+    }
+
+    ptr[0] = 2.0f;
+    ptr[1] = 1.5f;
+
+    foo(ptr); // should change the contents of the page and call `bar`
+
+    if (ptr[0] != 0.5f || ptr[1] != 0.2f) {
+        fprintf(stderr, "invalid data in memory; expected '0.5 0.2', got '%f %f'\n",
+                ptr[0], ptr[1]);
+        return EXIT_FAILURE;
+    }
+
+    return 0;
+}
diff --git a/tests/run-make/mte-ffi/bar_function.c b/tests/run-make/mte-ffi/bar_function.c
new file mode 100644
index 0000000000000..1fa48d32a0c88
--- /dev/null
+++ b/tests/run-make/mte-ffi/bar_function.c
@@ -0,0 +1,39 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "bar.h"
+
+typedef void (*fp)(int (*)());
+
+extern void foo(fp);
+
+void bar(int (*ptr)()) {
+    if (((uintptr_t)ptr >> 56) != 0x2f) {
+        fprintf(stderr, "Top byte corrupted on Rust -> C FFI boundary!\n");
+        exit(1);
+    }
+
+    int r = (*ptr)();
+    if (r != 32) {
+        fprintf(stderr, "invalid return value; expected 32, got '%d'\n", r);
+        exit(1);
+    }
+}
+
+int main(void)
+{
+    fp ptr = alloc_page();
+    if (ptr == MAP_FAILED)
+    {
+        perror("mmap() failed");
+        return EXIT_FAILURE;
+    }
+
+    // Store an arbitrary tag in bits 56-59 of the pointer (where an MTE tag may be),
+    // and a different value in the ignored top 4 bits.
+    ptr = (fp)((uintptr_t)&bar | 0x1fl << 56);
+
+    foo(ptr);
+
+    return 0;
+}
diff --git a/tests/run-make/mte-ffi/bar_int.c b/tests/run-make/mte-ffi/bar_int.c
new file mode 100644
index 0000000000000..d1c79e95dc9cb
--- /dev/null
+++ b/tests/run-make/mte-ffi/bar_int.c
@@ -0,0 +1,47 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "bar.h"
+
+extern void foo(unsigned int *);
+
+void bar(char *ptr) {
+    if (((uintptr_t)ptr >> 56) != 0x1f) {
+        fprintf(stderr, "Top byte corrupted on Rust -> C FFI boundary!\n");
+        exit(1);
+    }
+}
+
+int main(void)
+{
+    // Construct a pointer with an arbitrary tag in bits 56-59, simulating an MTE tag.
+    // It's only necessary that the tag is preserved across FFI bounds for this test.
+    unsigned int *ptr;
+
+    ptr = alloc_page();
+    if (ptr == MAP_FAILED)
+    {
+        perror("mmap() failed");
+        return EXIT_FAILURE;
+    }
+
+    // Store an arbitrary tag in bits 56-59 of the pointer (where an MTE tag may be),
+    // and a different value in the ignored top 4 bits.
+    ptr = (unsigned int *)((uintptr_t)ptr | 0x1fl << 56);
+
+    if (mte_enabled()) {
+        set_tag(ptr);
+    }
+
+    ptr[0] = 61;
+    ptr[1] = 62;
+
+    foo(ptr); // should change the contents of the page to start with 0x63 0x64 and call `bar`
+
+    if (ptr[0] != 0x63 || ptr[1] != 0x64) {
+        fprintf(stderr, "invalid data in memory; expected '63 64', got '%d %d'\n", ptr[0], ptr[1]);
+        return EXIT_FAILURE;
+    }
+
+    return 0;
+}
diff --git a/tests/run-make/mte-ffi/bar_string.c b/tests/run-make/mte-ffi/bar_string.c
new file mode 100644
index 0000000000000..5669ffd6695e7
--- /dev/null
+++ b/tests/run-make/mte-ffi/bar_string.c
@@ -0,0 +1,48 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "bar.h"
+
+extern void foo(char*);
+
+void bar(char *ptr) {
+    if (((uintptr_t)ptr >> 56) != 0x2f) {
+        fprintf(stderr, "Top byte corrupted on Rust -> C FFI boundary!\n");
+        exit(1);
+    }
+
+    if (strcmp(ptr, "cd")) {
+        fprintf(stderr, "invalid data in memory; expected 'cd', got '%s'\n", ptr);
+        exit(1);
+    }
+}
+
+int main(void)
+{
+    // Construct a pointer with an arbitrary tag in bits 56-59, simulating an MTE tag.
+    // It's only necessary that the tag is preserved across FFI bounds for this test.
+    char *ptr;
+
+    ptr = alloc_page();
+    if (ptr == MAP_FAILED)
+    {
+        perror("mmap() failed");
+        return EXIT_FAILURE;
+    }
+
+    // Store an arbitrary tag in bits 56-59 of the pointer (where an MTE tag may be),
+    // and a different value in the ignored top 4 bits.
+    ptr = (unsigned int *)((uintptr_t)ptr | 0x1fl << 56);
+
+    if (mte_enabled()) {
+        set_tag(ptr);
+    }
+
+    ptr[0] = 'a';
+    ptr[1] = 'b';
+    ptr[2] = '\0';
+
+    foo(ptr);
+
+    return 0;
+}
diff --git a/tests/run-make/mte-ffi/foo_float.rs b/tests/run-make/mte-ffi/foo_float.rs
new file mode 100644
index 0000000000000..c1bedd5249459
--- /dev/null
+++ b/tests/run-make/mte-ffi/foo_float.rs
@@ -0,0 +1,19 @@
+#![crate_type = "cdylib"]
+#![crate_name = "foo"]
+
+use std::os::raw::c_float;
+
+extern "C" {
+    fn bar(ptr: *const c_float);
+}
+
+#[no_mangle]
+pub extern "C" fn foo(ptr: *mut c_float) {
+    assert_eq!((ptr as usize) >> 56, 0x1f);
+
+    unsafe {
+        *ptr = 0.5;
+        *ptr.wrapping_add(1) = 0.2;
+        bar(ptr);
+    }
+}
diff --git a/tests/run-make/mte-ffi/foo_function.rs b/tests/run-make/mte-ffi/foo_function.rs
new file mode 100644
index 0000000000000..2c8e0b2623851
--- /dev/null
+++ b/tests/run-make/mte-ffi/foo_function.rs
@@ -0,0 +1,17 @@
+#![crate_type = "cdylib"]
+#![crate_name = "foo"]
+
+extern "C" fn ret32() -> i32 {
+    32
+}
+
+#[no_mangle]
+pub extern "C" fn foo(ptr: extern "C" fn(extern "C" fn() -> i32)) {
+    assert_eq!((ptr as usize) >> 56, 0x1f);
+
+    // Store an arbitrary tag in the tag bits, and convert back to the correct pointer type.
+    let p = ((ret32 as usize) | (0x2f << 56)) as *const ();
+    let p: extern "C" fn() -> i32 = unsafe { std::mem::transmute(p) };
+
+    unsafe { ptr(p) }
+}
diff --git a/tests/run-make/mte-ffi/foo_int.rs b/tests/run-make/mte-ffi/foo_int.rs
new file mode 100644
index 0000000000000..106d863cb8127
--- /dev/null
+++ b/tests/run-make/mte-ffi/foo_int.rs
@@ -0,0 +1,19 @@
+#![crate_type = "cdylib"]
+#![crate_name = "foo"]
+
+use std::os::raw::c_uint;
+
+extern "C" {
+    fn bar(ptr: *const c_uint);
+}
+
+#[no_mangle]
+pub extern "C" fn foo(ptr: *mut c_uint) {
+    assert_eq!((ptr as usize) >> 56, 0x1f);
+
+    unsafe {
+        *ptr = 0x63;
+        *ptr.wrapping_add(1) = 0x64;
+        bar(ptr);
+    }
+}
diff --git a/tests/run-make/mte-ffi/foo_string.rs b/tests/run-make/mte-ffi/foo_string.rs
new file mode 100644
index 0000000000000..5474480244892
--- /dev/null
+++ b/tests/run-make/mte-ffi/foo_string.rs
@@ -0,0 +1,27 @@
+#![crate_type = "cdylib"]
+#![crate_name = "foo"]
+
+use std::arch::asm;
+use std::ffi::{CStr, CString};
+use std::os::raw::c_char;
+
+extern "C" {
+    fn bar(ptr: *const c_char);
+}
+
+#[no_mangle]
+pub extern "C" fn foo(ptr: *const c_char) {
+    assert_eq!((ptr as usize) >> 56, 0x1f);
+
+    let s = unsafe { CStr::from_ptr(ptr) };
+    assert_eq!(s.to_str().unwrap(), "ab");
+
+    let s = CString::from_vec_with_nul("cd\0".into()).unwrap();
+    let mut p = ((s.as_ptr() as usize) | (0x2f << 56)) as *const c_char;
+    unsafe {
+        #[cfg(target_feature = "mte")]
+        asm!("stg {p}, [{p}]", p = inout(reg) p);
+
+        bar(p);
+    }
+}
diff --git a/tests/run-make/mte-ffi/rmake.rs b/tests/run-make/mte-ffi/rmake.rs
new file mode 100644
index 0000000000000..132c12aa7f073
--- /dev/null
+++ b/tests/run-make/mte-ffi/rmake.rs
@@ -0,0 +1,39 @@
+// Tests that MTE tags and values stored in the top byte of a pointer (TBI) are
+// preserved across FFI boundaries (C <-> Rust).
+// This test does not require MTE: whilst the test will use MTE if available, if it is not,
+// arbitrary tag bits are set using TBI.
+
+//@ only-aarch64
+//@ only-linux
+//@ only-gnu
+//@ run-pass
+
+use run_make_support::{cc, dynamic_lib_name, extra_c_flags, run, rustc, target};
+
+fn main() {
+    run_test("int");
+    run_test("float");
+    run_test("string");
+    run_test("function");
+}
+
+fn run_test(variant: &str) {
+    let flags = {
+        let mut flags = extra_c_flags();
+        flags.push("-march=armv8.5-a+memtag");
+        flags
+    };
+    print!("{variant} test...");
+    rustc()
+        .input(format!("foo_{variant}.rs"))
+        .target(target())
+        .linker("aarch64-linux-gnu-gcc")
+        .run();
+    cc().input(format!("bar_{variant}.c"))
+        .input(dynamic_lib_name("foo"))
+        .out_exe("test")
+        .args(&flags)
+        .run();
+    run("test");
+    println!("\tpassed");
+}

From 732037c75b901ea29bef2964b1e337ec325cdf51 Mon Sep 17 00:00:00 2001
From: dheaton-arm <Damian.Heaton@arm.com>
Date: Wed, 31 Jul 2024 09:27:48 +0100
Subject: [PATCH 2/7] Remove redundant information and simplify `only`
 condition

---
 src/tools/compiletest/src/command-list.rs |  1 +
 tests/run-make/mte-ffi/rmake.rs           | 11 +++++------
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/tools/compiletest/src/command-list.rs b/src/tools/compiletest/src/command-list.rs
index c356f4266f016..825f23e0582a9 100644
--- a/src/tools/compiletest/src/command-list.rs
+++ b/src/tools/compiletest/src/command-list.rs
@@ -168,6 +168,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "only-32bit",
     "only-64bit",
     "only-aarch64",
+    "only-aarch64-unknown-linux-gnu",
     "only-apple",
     "only-arm",
     "only-avr",
diff --git a/tests/run-make/mte-ffi/rmake.rs b/tests/run-make/mte-ffi/rmake.rs
index 132c12aa7f073..f4fafb796e3c5 100644
--- a/tests/run-make/mte-ffi/rmake.rs
+++ b/tests/run-make/mte-ffi/rmake.rs
@@ -3,10 +3,10 @@
 // This test does not require MTE: whilst the test will use MTE if available, if it is not,
 // arbitrary tag bits are set using TBI.
 
-//@ only-aarch64
-//@ only-linux
-//@ only-gnu
-//@ run-pass
+// This test is only valid for AArch64.
+// The linker must be explicitly specified when cross-compiling, so it is limited to
+// `aarch64-unknown-linux-gnu`.
+//@ only-aarch64-unknown-linux-gnu
 
 use run_make_support::{cc, dynamic_lib_name, extra_c_flags, run, rustc, target};
 
@@ -23,7 +23,7 @@ fn run_test(variant: &str) {
         flags.push("-march=armv8.5-a+memtag");
         flags
     };
-    print!("{variant} test...");
+    println!("{variant} test...");
     rustc()
         .input(format!("foo_{variant}.rs"))
         .target(target())
@@ -35,5 +35,4 @@ fn run_test(variant: &str) {
         .args(&flags)
         .run();
     run("test");
-    println!("\tpassed");
 }

From f71a627073ce6da5d764625b83b0e42bd6171e8d Mon Sep 17 00:00:00 2001
From: Folkert <folkert@folkertdev.nl>
Date: Sun, 4 Aug 2024 12:57:58 +0200
Subject: [PATCH 3/7] migrate `thumb-none-cortex-m` to rmake

---
 .../tidy/src/allowed_run_make_makefiles.txt   |  1 -
 tests/run-make/thumb-none-cortex-m/Makefile   | 38 ------------
 tests/run-make/thumb-none-cortex-m/rmake.rs   | 58 +++++++++++++++++++
 3 files changed, 58 insertions(+), 39 deletions(-)
 delete mode 100644 tests/run-make/thumb-none-cortex-m/Makefile
 create mode 100644 tests/run-make/thumb-none-cortex-m/rmake.rs

diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt
index 8747a6265c61e..e863b36c2bf13 100644
--- a/src/tools/tidy/src/allowed_run_make_makefiles.txt
+++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt
@@ -50,7 +50,6 @@ run-make/stable-symbol-names/Makefile
 run-make/staticlib-dylib-linkage/Makefile
 run-make/symbol-mangling-hashed/Makefile
 run-make/sysroot-crates-are-unstable/Makefile
-run-make/thumb-none-cortex-m/Makefile
 run-make/thumb-none-qemu/Makefile
 run-make/translation/Makefile
 run-make/x86_64-fortanix-unknown-sgx-lvi/Makefile
diff --git a/tests/run-make/thumb-none-cortex-m/Makefile b/tests/run-make/thumb-none-cortex-m/Makefile
deleted file mode 100644
index e941fc4a78e1c..0000000000000
--- a/tests/run-make/thumb-none-cortex-m/Makefile
+++ /dev/null
@@ -1,38 +0,0 @@
-include ../tools.mk
-
-# How to run this
-# $ ./x.py clean
-# $ ./x.py test --target thumbv6m-none-eabi,thumbv7m-none-eabi tests/run-make
-
-# Supported targets:
-# - thumbv6m-none-eabi (Bare Cortex-M0, M0+, M1)
-# - thumbv7em-none-eabi (Bare Cortex-M4, M7)
-# - thumbv7em-none-eabihf (Bare Cortex-M4F, M7F, FPU, hardfloat)
-# - thumbv7m-none-eabi (Bare Cortex-M3)
-
-# only-thumb
-
-# For cargo setting
-RUSTC := $(RUSTC_ORIGINAL)
-LD_LIBRARY_PATH := $(HOST_RPATH_DIR)
-# We need to be outside of 'src' dir in order to run cargo
-WORK_DIR := $(TMPDIR)
-
-HERE := $(shell pwd)
-
-CRATE := cortex-m
-CRATE_URL := https://github.com/rust-embedded/cortex-m
-CRATE_SHA1 := a448e9156e2cb1e556e5441fd65426952ef4b927 # 0.5.0
-
-# Don't make lints fatal, but they need to at least warn or they break Cargo's target info parsing.
-export RUSTFLAGS := --cap-lints=warn
-
-all:
-	env
-	mkdir -p $(WORK_DIR)
-	-cd $(WORK_DIR) && rm -rf $(CRATE)
-	cd $(WORK_DIR) && bash -x $(HERE)/../git_clone_sha1.sh $(CRATE) $(CRATE_URL) $(CRATE_SHA1)
-	# HACK(eddyb) sets `RUSTC_BOOTSTRAP=1` so Cargo can accept nightly features.
-	# These come from the top-level Rust workspace, that this crate is not a
-	# member of, but Cargo tries to load the workspace `Cargo.toml` anyway.
-	cd $(WORK_DIR) && cd $(CRATE) && env RUSTC_BOOTSTRAP=1 $(BOOTSTRAP_CARGO) build --target $(TARGET) -v
diff --git a/tests/run-make/thumb-none-cortex-m/rmake.rs b/tests/run-make/thumb-none-cortex-m/rmake.rs
new file mode 100644
index 0000000000000..da00a418eecca
--- /dev/null
+++ b/tests/run-make/thumb-none-cortex-m/rmake.rs
@@ -0,0 +1,58 @@
+//! How to run this
+//! $ ./x.py clean
+//! $ ./x.py test --target thumbv6m-none-eabi,thumbv7m-none-eabi tests/run-make
+//!
+//! Supported targets:
+//! - thumbv6m-none-eabi (Bare Cortex-M0, M0+, M1)
+//! - thumbv7em-none-eabi (Bare Cortex-M4, M7)
+//! - thumbv7em-none-eabihf (Bare Cortex-M4F, M7F, FPU, hardfloat)
+//! - thumbv7m-none-eabi (Bare Cortex-M3)
+
+//@ only-thumb
+
+use std::path::PathBuf;
+
+use run_make_support::rfs::create_dir;
+use run_make_support::{cmd, env_var};
+
+const CRATE: &str = "cortex-m";
+const CRATE_URL: &str = "https://github.com/rust-embedded/cortex-m";
+const CRATE_SHA1: &str = "a448e9156e2cb1e556e5441fd65426952ef4b927"; // v0.5.0
+
+fn main() {
+    // See below link for git usage:
+    // https://stackoverflow.com/questions/3489173#14091182
+    cmd("git").args(["clone", CRATE_URL, CRATE]).run();
+    std::env::set_current_dir(CRATE).unwrap();
+    cmd("git").args(["reset", "--hard", CRATE_SHA1]).run();
+
+    let target_dir = PathBuf::from("target");
+    let target = env_var("TARGET");
+
+    let manifest_path = PathBuf::from("Cargo.toml");
+
+    let path = env_var("PATH");
+    let rustc = env_var("RUSTC");
+    let bootstrap_cargo = env_var("BOOTSTRAP_CARGO");
+    let mut cmd = cmd(bootstrap_cargo);
+    cmd.args(&[
+        "build",
+        "--manifest-path",
+        manifest_path.to_str().unwrap(),
+        "-Zbuild-std=core",
+        "--target",
+        &target,
+    ])
+    .env("PATH", path)
+    .env("RUSTC", rustc)
+    // Don't make lints fatal, but they need to at least warn
+    // or they break Cargo's target info parsing.
+    .env("RUSTFLAGS", "-Copt-level=0 -Cdebug-assertions=yes --cap-lints=warn")
+    .env("CARGO_TARGET_DIR", &target_dir)
+    .env("RUSTC_BOOTSTRAP", "1")
+    // Visual Studio 2022 requires that the LIB env var be set so it can
+    // find the Windows SDK.
+    .env("LIB", std::env::var("LIB").unwrap_or_default());
+
+    cmd.run();
+}

From 608b322f49183868879b29d0edf927951dd20f63 Mon Sep 17 00:00:00 2001
From: Oneirical <manchot@videotron.ca>
Date: Mon, 5 Aug 2024 14:19:32 -0400
Subject: [PATCH 4/7] rewrite staticlib-dylib-linkage to rmake

---
 .../tidy/src/allowed_run_make_makefiles.txt   |  1 -
 .../run-make/staticlib-dylib-linkage/Makefile | 21 -----------
 .../run-make/staticlib-dylib-linkage/rmake.rs | 37 +++++++++++++++++++
 3 files changed, 37 insertions(+), 22 deletions(-)
 delete mode 100644 tests/run-make/staticlib-dylib-linkage/Makefile
 create mode 100644 tests/run-make/staticlib-dylib-linkage/rmake.rs

diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt
index 38880e5e95fcf..6766c3f49de89 100644
--- a/src/tools/tidy/src/allowed_run_make_makefiles.txt
+++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt
@@ -31,7 +31,6 @@ run-make/reproducible-build/Makefile
 run-make/rlib-format-packed-bundled-libs/Makefile
 run-make/simd-ffi/Makefile
 run-make/split-debuginfo/Makefile
-run-make/staticlib-dylib-linkage/Makefile
 run-make/symbol-mangling-hashed/Makefile
 run-make/sysroot-crates-are-unstable/Makefile
 run-make/thumb-none-cortex-m/Makefile
diff --git a/tests/run-make/staticlib-dylib-linkage/Makefile b/tests/run-make/staticlib-dylib-linkage/Makefile
deleted file mode 100644
index a1e86a7ce4b65..0000000000000
--- a/tests/run-make/staticlib-dylib-linkage/Makefile
+++ /dev/null
@@ -1,21 +0,0 @@
-include ../tools.mk
-
-# ignore-cross-compile
-# ignore-msvc FIXME(bjorn3) can't figure out how to link with the MSVC toolchain
-# ignore-wasm wasm doesn't support dynamic libraries
-
-all:
-	$(RUSTC) -C prefer-dynamic bar.rs
-	$(RUSTC) foo.rs --crate-type staticlib --print native-static-libs \
-		-Z staticlib-allow-rdylib-deps 2>&1 | grep 'note: native-static-libs: ' \
-		| sed 's/note: native-static-libs: \(.*\)/\1/' > $(TMPDIR)/libs.txt
-	cat $(TMPDIR)/libs.txt
-
-ifdef IS_MSVC
-	$(CC) $(CFLAGS) /c foo.c /Fo:$(TMPDIR)/foo.o
-	$(RUSTC_LINKER) $(TMPDIR)/foo.o $(TMPDIR)/foo.lib $$(cat $(TMPDIR)/libs.txt) $(call OUT_EXE,foo)
-else
-	$(CC) $(CFLAGS) foo.c -L $(TMPDIR) -lfoo $$(cat $(TMPDIR)/libs.txt) -o $(call RUN_BINFILE,foo)
-endif
-
-	$(call RUN,foo)
diff --git a/tests/run-make/staticlib-dylib-linkage/rmake.rs b/tests/run-make/staticlib-dylib-linkage/rmake.rs
new file mode 100644
index 0000000000000..415491bb8ee0a
--- /dev/null
+++ b/tests/run-make/staticlib-dylib-linkage/rmake.rs
@@ -0,0 +1,37 @@
+// A basic smoke test to check that rustc supports linking to a rust dylib with
+// --crate-type staticlib. bar is a dylib, on which foo is dependent - the native
+// static lib search paths are collected and used to compile foo.c, the final executable
+// which depends on both foo and bar.
+// See https://github.com/rust-lang/rust/pull/106560
+
+//@ ignore-cross-compile
+// Reason: the compiled binary is executed.
+//@ ignore-wasm
+// Reason: WASM does not support dynamic libraries
+//@ ignore-msvc
+//FIXME(Oneirical): Getting this to work on MSVC requires passing libcmt.lib to CC,
+// which is not trivial to do.
+// Tracking issue: https://github.com/rust-lang/rust/issues/128602
+// Discussion: https://github.com/rust-lang/rust/pull/128407#discussion_r1702439172
+
+use run_make_support::{cc, regex, run, rustc};
+
+fn main() {
+    rustc().arg("-Cprefer-dynamic").input("bar.rs").run();
+    let libs = rustc()
+        .input("foo.rs")
+        .crate_type("staticlib")
+        .print("native-static-libs")
+        .arg("-Zstaticlib-allow-rdylib-deps")
+        .run()
+        .assert_stderr_contains("note: native-static-libs: ")
+        .stderr_utf8();
+    let re = regex::Regex::new(r#"note: native-static-libs:\s*(.+)"#).unwrap();
+    let libs = re.find(&libs).unwrap().as_str().trim();
+    // remove the note
+    let (_, library_search_paths) = libs.split_once("note: native-static-libs: ").unwrap();
+    // divide the command-line arguments in a vec
+    let library_search_paths = library_search_paths.split(' ').collect::<Vec<&str>>();
+    cc().input("foo.c").arg("-lfoo").args(library_search_paths).out_exe("foo").run();
+    run("foo");
+}

From 201ca3f65ce1749e2f790a56b16ea2c88309ca71 Mon Sep 17 00:00:00 2001
From: Folkert <folkert@folkertdev.nl>
Date: Tue, 6 Aug 2024 22:08:28 +0200
Subject: [PATCH 5/7] changes after review

---
 tests/run-make/thumb-none-cortex-m/rmake.rs | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/tests/run-make/thumb-none-cortex-m/rmake.rs b/tests/run-make/thumb-none-cortex-m/rmake.rs
index da00a418eecca..0ddb91d378fb7 100644
--- a/tests/run-make/thumb-none-cortex-m/rmake.rs
+++ b/tests/run-make/thumb-none-cortex-m/rmake.rs
@@ -1,3 +1,7 @@
+//! Test building of the `cortex-m` crate, a foundational crate in the embedded ecosystem
+//! for a collection of thumb targets. This is a smoke test that verifies that both cargo
+//! and rustc work in this case.
+//!
 //! How to run this
 //! $ ./x.py clean
 //! $ ./x.py test --target thumbv6m-none-eabi,thumbv7m-none-eabi tests/run-make
@@ -13,13 +17,14 @@
 use std::path::PathBuf;
 
 use run_make_support::rfs::create_dir;
-use run_make_support::{cmd, env_var};
+use run_make_support::{cmd, env_var, target};
 
 const CRATE: &str = "cortex-m";
 const CRATE_URL: &str = "https://github.com/rust-embedded/cortex-m";
 const CRATE_SHA1: &str = "a448e9156e2cb1e556e5441fd65426952ef4b927"; // v0.5.0
 
 fn main() {
+    // FIXME: requires an internet connection https://github.com/rust-lang/rust/issues/128733
     // See below link for git usage:
     // https://stackoverflow.com/questions/3489173#14091182
     cmd("git").args(["clone", CRATE_URL, CRATE]).run();
@@ -27,13 +32,13 @@ fn main() {
     cmd("git").args(["reset", "--hard", CRATE_SHA1]).run();
 
     let target_dir = PathBuf::from("target");
-    let target = env_var("TARGET");
-
     let manifest_path = PathBuf::from("Cargo.toml");
 
     let path = env_var("PATH");
     let rustc = env_var("RUSTC");
     let bootstrap_cargo = env_var("BOOTSTRAP_CARGO");
+    // FIXME: extract bootstrap cargo invocations to a proper command
+    // https://github.com/rust-lang/rust/issues/128734
     let mut cmd = cmd(bootstrap_cargo);
     cmd.args(&[
         "build",
@@ -41,18 +46,14 @@ fn main() {
         manifest_path.to_str().unwrap(),
         "-Zbuild-std=core",
         "--target",
-        &target,
+        &target(),
     ])
     .env("PATH", path)
     .env("RUSTC", rustc)
+    .env("CARGO_TARGET_DIR", &target_dir)
     // Don't make lints fatal, but they need to at least warn
     // or they break Cargo's target info parsing.
-    .env("RUSTFLAGS", "-Copt-level=0 -Cdebug-assertions=yes --cap-lints=warn")
-    .env("CARGO_TARGET_DIR", &target_dir)
-    .env("RUSTC_BOOTSTRAP", "1")
-    // Visual Studio 2022 requires that the LIB env var be set so it can
-    // find the Windows SDK.
-    .env("LIB", std::env::var("LIB").unwrap_or_default());
+    .env("RUSTFLAGS", "-Copt-level=0 -Cdebug-assertions=yes --cap-lints=warn");
 
     cmd.run();
 }

From 7e5a2ea583f47b562078a24009d089d1e4bcebff Mon Sep 17 00:00:00 2001
From: Oneirical <manchot@videotron.ca>
Date: Mon, 29 Jul 2024 16:16:55 -0400
Subject: [PATCH 6/7] rewrite pdb-buildinfo-cl-cmd to rmake

---
 .../tidy/src/allowed_run_make_makefiles.txt   |  1 -
 tests/run-make/pdb-buildinfo-cl-cmd/Makefile  | 16 ---------
 tests/run-make/pdb-buildinfo-cl-cmd/rmake.rs  | 35 +++++++++++++++++++
 .../pdb-buildinfo-cl-cmd/stringlist.txt       |  1 -
 4 files changed, 35 insertions(+), 18 deletions(-)
 delete mode 100644 tests/run-make/pdb-buildinfo-cl-cmd/Makefile
 create mode 100644 tests/run-make/pdb-buildinfo-cl-cmd/rmake.rs
 delete mode 100644 tests/run-make/pdb-buildinfo-cl-cmd/stringlist.txt

diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt
index 5c1d9a2d47e20..759889621b75c 100644
--- a/src/tools/tidy/src/allowed_run_make_makefiles.txt
+++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt
@@ -16,7 +16,6 @@ run-make/macos-deployment-target/Makefile
 run-make/min-global-align/Makefile
 run-make/native-link-modifier-bundle/Makefile
 run-make/no-alloc-shim/Makefile
-run-make/pdb-buildinfo-cl-cmd/Makefile
 run-make/pgo-indirect-call-promotion/Makefile
 run-make/remap-path-prefix-dwarf/Makefile
 run-make/reproducible-build/Makefile
diff --git a/tests/run-make/pdb-buildinfo-cl-cmd/Makefile b/tests/run-make/pdb-buildinfo-cl-cmd/Makefile
deleted file mode 100644
index a7be301a5b0d2..0000000000000
--- a/tests/run-make/pdb-buildinfo-cl-cmd/Makefile
+++ /dev/null
@@ -1,16 +0,0 @@
-include ../tools.mk
-
-# only-windows-msvc
-
-# tests if the pdb contains the following information in the LF_BUILDINFO:
-# 1. the commandline args to compile it (cmd)
-# 2. full path to the compiler (cl)
-
-# we just do a stringsearch on the pdb, as these need to show up at least once, as the LF_BUILDINFO is created for each cgu
-# actual parsing would be better, but this is a simple and good enough solution for now
-
-all:
-	$(RUSTC_ORIGINAL) main.rs -g --crate-name my_crate_name --crate-type bin -C metadata=dc9ef878b0a48666 --out-dir $(TMPDIR)
-	cat '$(TMPDIR)/my_crate_name.pdb' | grep -F '$(RUSTC_ORIGINAL)'
-# using a file containing the string so I don't have problems with escaping quotes and spaces 
-	cat '$(TMPDIR)/my_crate_name.pdb' | grep -f 'stringlist.txt'
diff --git a/tests/run-make/pdb-buildinfo-cl-cmd/rmake.rs b/tests/run-make/pdb-buildinfo-cl-cmd/rmake.rs
new file mode 100644
index 0000000000000..347b3d67a2542
--- /dev/null
+++ b/tests/run-make/pdb-buildinfo-cl-cmd/rmake.rs
@@ -0,0 +1,35 @@
+// Check if the pdb file contains the following information in the LF_BUILDINFO:
+// 1. full path to the compiler (cl)
+// 2. the commandline args to compile it (cmd)
+// This is because these used to be missing in #96475.
+// See https://github.com/rust-lang/rust/pull/113492
+
+//@ only-windows-msvc
+// Reason: pdb files are unique to this architecture
+
+use run_make_support::{assert_contains, env_var, rfs, rustc};
+
+fn main() {
+    rustc()
+        .input("main.rs")
+        .arg("-g")
+        .crate_name("my_crate_name")
+        .crate_type("bin")
+        .metadata("dc9ef878b0a48666")
+        .run();
+    assert_contains(rfs::read_to_string("my_crate_name.pdb"), env_var("RUSTC_ORIGINAL"));
+    let strings = [
+        r#""main.rs""#,
+        r#""-g""#,
+        r#""--crate-name""#,
+        r#""my_crate_name""#,
+        r#""--crate-type""#,
+        r#""bin""#,
+        r#""-C""#,
+        r#""metadata=dc9ef878b0a48666""#,
+        r#""--out-dir""#,
+    ];
+    for string in strings {
+        assert_contains(rfs::read_to_string("my_crate_name.pdb"), string);
+    }
+}
diff --git a/tests/run-make/pdb-buildinfo-cl-cmd/stringlist.txt b/tests/run-make/pdb-buildinfo-cl-cmd/stringlist.txt
deleted file mode 100644
index 634e9f19e8973..0000000000000
--- a/tests/run-make/pdb-buildinfo-cl-cmd/stringlist.txt
+++ /dev/null
@@ -1 +0,0 @@
-"main.rs" "-g" "--crate-name" "my_crate_name" "--crate-type" "bin" "-C" "metadata=dc9ef878b0a48666" "--out-dir"
\ No newline at end of file

From 7d1a97fae9fc6d9f1790d054a40529fda4aacf3c Mon Sep 17 00:00:00 2001
From: Oneirical <manchot@videotron.ca>
Date: Mon, 29 Jul 2024 16:28:38 -0400
Subject: [PATCH 7/7] rewrite pgo-indirect-call-promotion to rmake

---
 src/tools/compiletest/src/command-list.rs     |  1 +
 .../tidy/src/allowed_run_make_makefiles.txt   |  1 -
 tests/run-make/pdb-buildinfo-cl-cmd/rmake.rs  | 20 ++++++-----
 .../pgo-indirect-call-promotion/Makefile      | 23 -------------
 .../pgo-indirect-call-promotion/rmake.rs      | 33 +++++++++++++++++++
 5 files changed, 46 insertions(+), 32 deletions(-)
 delete mode 100644 tests/run-make/pgo-indirect-call-promotion/Makefile
 create mode 100644 tests/run-make/pgo-indirect-call-promotion/rmake.rs

diff --git a/src/tools/compiletest/src/command-list.rs b/src/tools/compiletest/src/command-list.rs
index 0706f3bee05c1..04c4b99d49773 100644
--- a/src/tools/compiletest/src/command-list.rs
+++ b/src/tools/compiletest/src/command-list.rs
@@ -204,6 +204,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "only-watchos",
     "only-windows",
     "only-windows-gnu",
+    "only-windows-msvc",
     "only-x86",
     "only-x86_64",
     "only-x86_64-fortanix-unknown-sgx",
diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt
index 759889621b75c..4bf53c28484e8 100644
--- a/src/tools/tidy/src/allowed_run_make_makefiles.txt
+++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt
@@ -16,7 +16,6 @@ run-make/macos-deployment-target/Makefile
 run-make/min-global-align/Makefile
 run-make/native-link-modifier-bundle/Makefile
 run-make/no-alloc-shim/Makefile
-run-make/pgo-indirect-call-promotion/Makefile
 run-make/remap-path-prefix-dwarf/Makefile
 run-make/reproducible-build/Makefile
 run-make/rlib-format-packed-bundled-libs/Makefile
diff --git a/tests/run-make/pdb-buildinfo-cl-cmd/rmake.rs b/tests/run-make/pdb-buildinfo-cl-cmd/rmake.rs
index 347b3d67a2542..2ab9057b24c1b 100644
--- a/tests/run-make/pdb-buildinfo-cl-cmd/rmake.rs
+++ b/tests/run-make/pdb-buildinfo-cl-cmd/rmake.rs
@@ -7,7 +7,7 @@
 //@ only-windows-msvc
 // Reason: pdb files are unique to this architecture
 
-use run_make_support::{assert_contains, env_var, rfs, rustc};
+use run_make_support::{assert_contains, bstr, env_var, rfs, rustc};
 
 fn main() {
     rustc()
@@ -17,19 +17,23 @@ fn main() {
         .crate_type("bin")
         .metadata("dc9ef878b0a48666")
         .run();
-    assert_contains(rfs::read_to_string("my_crate_name.pdb"), env_var("RUSTC_ORIGINAL"));
-    let strings = [
+    let tests = [
+        &env_var("RUSTC"),
         r#""main.rs""#,
         r#""-g""#,
         r#""--crate-name""#,
         r#""my_crate_name""#,
         r#""--crate-type""#,
         r#""bin""#,
-        r#""-C""#,
-        r#""metadata=dc9ef878b0a48666""#,
-        r#""--out-dir""#,
+        r#""-Cmetadata=dc9ef878b0a48666""#,
     ];
-    for string in strings {
-        assert_contains(rfs::read_to_string("my_crate_name.pdb"), string);
+    for test in tests {
+        assert_pdb_contains(test);
     }
 }
+
+fn assert_pdb_contains(needle: &str) {
+    let needle = needle.as_bytes();
+    use bstr::ByteSlice;
+    assert!(&rfs::read("my_crate_name.pdb").find(needle).is_some());
+}
diff --git a/tests/run-make/pgo-indirect-call-promotion/Makefile b/tests/run-make/pgo-indirect-call-promotion/Makefile
deleted file mode 100644
index 8d1e69c4aba37..0000000000000
--- a/tests/run-make/pgo-indirect-call-promotion/Makefile
+++ /dev/null
@@ -1,23 +0,0 @@
-# needs-profiler-support
-# ignore-cross-compile
-
-include ../tools.mk
-
-all:
-	# We don't compile `opaque` with either optimizations or instrumentation.
-	# We don't compile `opaque` with either optimizations or instrumentation.
-	$(RUSTC) $(COMMON_FLAGS) opaque.rs
-	# Compile the test program with instrumentation
-	mkdir -p "$(TMPDIR)"/prof_data_dir
-	$(RUSTC) $(COMMON_FLAGS) interesting.rs \
-		-Cprofile-generate="$(TMPDIR)"/prof_data_dir -O -Ccodegen-units=1
-	$(RUSTC) $(COMMON_FLAGS) main.rs -Cprofile-generate="$(TMPDIR)"/prof_data_dir -O
-	# The argument below generates to the expected branch weights
-	$(call RUN,main) || exit 1
-	"$(LLVM_BIN_DIR)"/llvm-profdata merge \
-		-o "$(TMPDIR)"/prof_data_dir/merged.profdata \
-		"$(TMPDIR)"/prof_data_dir
-	$(RUSTC) $(COMMON_FLAGS) interesting.rs \
-		-Cprofile-use="$(TMPDIR)"/prof_data_dir/merged.profdata -O \
-		-Ccodegen-units=1 --emit=llvm-ir
-	cat "$(TMPDIR)"/interesting.ll | "$(LLVM_FILECHECK)" filecheck-patterns.txt
diff --git a/tests/run-make/pgo-indirect-call-promotion/rmake.rs b/tests/run-make/pgo-indirect-call-promotion/rmake.rs
new file mode 100644
index 0000000000000..d0ccfd8a4d7e4
--- /dev/null
+++ b/tests/run-make/pgo-indirect-call-promotion/rmake.rs
@@ -0,0 +1,33 @@
+// This test checks that indirect call promotion is performed. The test
+// programs calls the same function a thousand times through a function pointer.
+// Only PGO data provides the information that it actually always is the same
+// function. We verify that the indirect call promotion pass inserts a check
+// whether it can make a direct call instead of the indirect call.
+// See https://github.com/rust-lang/rust/pull/66631
+
+//@ needs-profiler-support
+// Reason: llvm_profdata is used
+//@ ignore-cross-compile
+// Reason: the compiled binary is executed
+
+use run_make_support::{llvm_filecheck, llvm_profdata, rfs, run, rustc};
+
+fn main() {
+    // We don't compile `opaque` with either optimizations or instrumentation.
+    rustc().input("opaque.rs").run();
+    // Compile the test program with instrumentation
+    rfs::create_dir("prof_data_dir");
+    rustc().input("interesting.rs").profile_generate("prof_data_dir").opt().codegen_units(1).run();
+    rustc().input("main.rs").profile_generate("prof_data_dir").opt().run();
+    // The argument below generates to the expected branch weights
+    run("main");
+    llvm_profdata().merge().output("prof_data_dir/merged.profdata").input("prof_data_dir").run();
+    rustc()
+        .input("interesting.rs")
+        .profile_use("prof_data_dir/merged.profdata")
+        .opt()
+        .codegen_units(1)
+        .emit("llvm-ir")
+        .run();
+    llvm_filecheck().patterns("filecheck-patterns.txt").stdin(rfs::read("interesting.ll")).run();
+}