From e8ce9fac85ed952f8d4ce01102a3b2dcfab218a5 Mon Sep 17 00:00:00 2001 From: Damian Heaton 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 +#include +#include +#include +#include + +// 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 +#include +#include +#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 +#include +#include +#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 +#include +#include +#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 +#include +#include +#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 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 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 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::>(); + 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 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 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 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(); +}