From 27c6d20b6cd25fb147a5202e3839d6fe330a62ce Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Mon, 4 May 2026 08:13:09 +0000 Subject: [PATCH 1/2] Add library support for `aarch64-unknown-linux-pauthtest` --- .../std/src/sys/pal/unix/stack_overflow.rs | 20 +++++++++--- library/std/src/sys/personality/gcc.rs | 31 ++++++++++++++++++- library/std/tests/pipe_subprocess.rs | 5 +++ library/std/tests/process_spawning.rs | 12 +++++-- .../std_detect/src/detect/os/linux/auxvec.rs | 4 +-- library/unwind/src/lib.rs | 5 +++ 6 files changed, 68 insertions(+), 9 deletions(-) diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 3b951899dfec9..0b67f4f8b5e37 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -409,9 +409,15 @@ mod imp { unsafe { // this way someone on any unix-y OS can check that all these compile - if cfg!(all(target_os = "linux", not(target_env = "musl"))) { + if cfg!(all( + target_os = "linux", + not(any(target_env = "musl", target_env = "pauthtest")) + )) { install_main_guard_linux(page_size) - } else if cfg!(all(target_os = "linux", target_env = "musl")) { + } else if cfg!(all( + target_os = "linux", + any(target_env = "musl", target_env = "pauthtest") + )) { install_main_guard_linux_musl(page_size) } else if cfg!(target_os = "freebsd") { #[cfg(not(target_os = "freebsd"))] @@ -588,7 +594,10 @@ mod imp { let mut guardsize = 0; assert_eq!(libc::pthread_attr_getguardsize(attr.as_ptr(), &mut guardsize), 0); if guardsize == 0 { - if cfg!(all(target_os = "linux", target_env = "musl")) { + if cfg!(all( + target_os = "linux", + any(target_env = "musl", target_env = "pauthtest") + )) { // musl versions before 1.1.19 always reported guard // size obtained from pthread_attr_get_np as zero. // Use page size as a fallback. @@ -604,7 +613,10 @@ mod imp { let stackaddr = stackptr.addr(); ret = if cfg!(any(target_os = "freebsd", target_os = "netbsd", target_os = "hurd")) { Some(stackaddr - guardsize..stackaddr) - } else if cfg!(all(target_os = "linux", target_env = "musl")) { + } else if cfg!(all( + target_os = "linux", + any(target_env = "musl", target_env = "pauthtest") + )) { Some(stackaddr - guardsize..stackaddr) } else if cfg!(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc"))) { diff --git a/library/std/src/sys/personality/gcc.rs b/library/std/src/sys/personality/gcc.rs index 019d5629d6d6e..ea1a6be30354d 100644 --- a/library/std/src/sys/personality/gcc.rs +++ b/library/std/src/sys/personality/gcc.rs @@ -89,6 +89,34 @@ const UNWIND_DATA_REG: (i32, i32) = (10, 11); // x10, x11 #[cfg(any(target_arch = "loongarch32", target_arch = "loongarch64"))] const UNWIND_DATA_REG: (i32, i32) = (4, 5); // a0, a1 +unsafe fn sign_lpad(context: *mut uw::_Unwind_Context, lpad: *const u8) -> *const u8 { + cfg_select! { + all(target_env = "pauthtest", target_arch = "aarch64") => { + // DWARF register number for SP on AArch64. + const SP_REG: i32 = 31; + + unsafe { + let sp = uw::_Unwind_GetGR(context, SP_REG) as u64; + let mut addr = lpad as usize; + + // `pacib` corresponds to `ptrauth_key_process_dependent_code` in . + core::arch::asm!( + "pacib {addr}, {sp}", + addr = inout(reg) addr, + sp = in(reg) sp, + options(nostack, preserves_flags) + ); + + lpad.with_addr(addr) + } + } + _ => { + let _ = context; + lpad + } + } +} + // The following code is based on GCC's C and C++ personality routines. For reference, see: // https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_personality.cc // https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c @@ -239,7 +267,8 @@ cfg_select! { exception_object.cast(), ); uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, core::ptr::null()); - uw::_Unwind_SetIP(context, lpad); + let maybe_signed_lpad = sign_lpad(context, lpad); + uw::_Unwind_SetIP(context, maybe_signed_lpad); uw::_URC_INSTALL_CONTEXT } EHAction::Terminate => uw::_URC_FATAL_PHASE2_ERROR, diff --git a/library/std/tests/pipe_subprocess.rs b/library/std/tests/pipe_subprocess.rs index c51a4459e718b..7b034f8129d12 100644 --- a/library/std/tests/pipe_subprocess.rs +++ b/library/std/tests/pipe_subprocess.rs @@ -12,10 +12,15 @@ fn main() { fn parent() { let me = env::current_exe().unwrap(); + // If `runner` is set up for current target, we'll be executing `./runner ./test`, not + // just `./test`. For such a case, use the same arguments for child to avoid executing + // `runner` without actual executable. + let args = env::args(); let (rx, tx) = pipe().unwrap(); assert!( process::Command::new(me) + .args(args) .env("I_AM_THE_CHILD", "1") .stdout(tx) .status() diff --git a/library/std/tests/process_spawning.rs b/library/std/tests/process_spawning.rs index 93f73ccad3ea4..b1973b54d7670 100644 --- a/library/std/tests/process_spawning.rs +++ b/library/std/tests/process_spawning.rs @@ -26,8 +26,16 @@ fn issue_15149() { env::join_paths(paths).unwrap() }; - let child_output = - process::Command::new("mytest").env("PATH", &path).arg("child").output().unwrap(); + // If `runner` is set up for current target, we'll be executing `./runner ./test`, not + // just `./test`. For such a case, use the same arguments for child to avoid executing + // `runner` without actual executable. + let args = env::args(); + let child_output = process::Command::new("mytest") + .args(args) + .env("PATH", &path) + .arg("child") + .output() + .unwrap(); assert!( child_output.status.success(), diff --git a/library/std_detect/src/detect/os/linux/auxvec.rs b/library/std_detect/src/detect/os/linux/auxvec.rs index c0bbc7d4efa88..dec33ffeedd34 100644 --- a/library/std_detect/src/detect/os/linux/auxvec.rs +++ b/library/std_detect/src/detect/os/linux/auxvec.rs @@ -51,7 +51,7 @@ pub(crate) struct AuxVec { /// Note that run-time feature detection is not invoked for features that can /// be detected at compile-time. /// -/// Note: We always directly use `getauxval` on `*-linux-{gnu,musl,ohos}*` and +/// Note: We always directly use `getauxval` on `*-linux-{gnu,musl,ohos,pauthtest}*` and /// `*-android*` targets rather than `dlsym` it because we can safely assume /// `getauxval` is linked to the binary. /// - `*-linux-gnu*` targets ([since Rust 1.64](https://blog.rust-lang.org/2022/08/01/Increasing-glibc-kernel-requirements.html)) @@ -125,7 +125,7 @@ fn getauxval(key: usize) -> Result { any( all( target_os = "linux", - any(target_env = "gnu", target_env = "musl", target_env = "ohos"), + any(target_env = "gnu", target_env = "musl", target_env = "ohos", target_env = "pauthtest"), ), target_os = "android", ) => { diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 22568d5f6f1f9..c9062a87d9d24 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -95,6 +95,11 @@ cfg_select! { } } +// For pauthtest the only supported unwinding mechanism is provided by libunwind. +#[cfg(target_env = "pauthtest")] +#[link(name = "unwind")] +unsafe extern "C" {} + // This is the same as musl except that we default to using the system libunwind // instead of libgcc. #[cfg(target_env = "ohos")] From 72ea1911e05ab2a10174ca03ae46a975c7bf15db Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Mon, 11 May 2026 12:46:32 +0000 Subject: [PATCH 2/2] Use target_env = "musl" & target_abi = "pauthtest" instead of env --- .../std/src/sys/pal/unix/stack_overflow.rs | 20 ++++--------------- library/std/src/sys/personality/gcc.rs | 2 +- .../std_detect/src/detect/os/linux/auxvec.rs | 4 ++-- library/unwind/src/lib.rs | 12 +++++------ 4 files changed, 13 insertions(+), 25 deletions(-) diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 0b67f4f8b5e37..3b951899dfec9 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -409,15 +409,9 @@ mod imp { unsafe { // this way someone on any unix-y OS can check that all these compile - if cfg!(all( - target_os = "linux", - not(any(target_env = "musl", target_env = "pauthtest")) - )) { + if cfg!(all(target_os = "linux", not(target_env = "musl"))) { install_main_guard_linux(page_size) - } else if cfg!(all( - target_os = "linux", - any(target_env = "musl", target_env = "pauthtest") - )) { + } else if cfg!(all(target_os = "linux", target_env = "musl")) { install_main_guard_linux_musl(page_size) } else if cfg!(target_os = "freebsd") { #[cfg(not(target_os = "freebsd"))] @@ -594,10 +588,7 @@ mod imp { let mut guardsize = 0; assert_eq!(libc::pthread_attr_getguardsize(attr.as_ptr(), &mut guardsize), 0); if guardsize == 0 { - if cfg!(all( - target_os = "linux", - any(target_env = "musl", target_env = "pauthtest") - )) { + if cfg!(all(target_os = "linux", target_env = "musl")) { // musl versions before 1.1.19 always reported guard // size obtained from pthread_attr_get_np as zero. // Use page size as a fallback. @@ -613,10 +604,7 @@ mod imp { let stackaddr = stackptr.addr(); ret = if cfg!(any(target_os = "freebsd", target_os = "netbsd", target_os = "hurd")) { Some(stackaddr - guardsize..stackaddr) - } else if cfg!(all( - target_os = "linux", - any(target_env = "musl", target_env = "pauthtest") - )) { + } else if cfg!(all(target_os = "linux", target_env = "musl")) { Some(stackaddr - guardsize..stackaddr) } else if cfg!(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc"))) { diff --git a/library/std/src/sys/personality/gcc.rs b/library/std/src/sys/personality/gcc.rs index ea1a6be30354d..4a55613ca3f6b 100644 --- a/library/std/src/sys/personality/gcc.rs +++ b/library/std/src/sys/personality/gcc.rs @@ -91,7 +91,7 @@ const UNWIND_DATA_REG: (i32, i32) = (4, 5); // a0, a1 unsafe fn sign_lpad(context: *mut uw::_Unwind_Context, lpad: *const u8) -> *const u8 { cfg_select! { - all(target_env = "pauthtest", target_arch = "aarch64") => { + all(target_abi = "pauthtest", target_arch = "aarch64") => { // DWARF register number for SP on AArch64. const SP_REG: i32 = 31; diff --git a/library/std_detect/src/detect/os/linux/auxvec.rs b/library/std_detect/src/detect/os/linux/auxvec.rs index dec33ffeedd34..c0bbc7d4efa88 100644 --- a/library/std_detect/src/detect/os/linux/auxvec.rs +++ b/library/std_detect/src/detect/os/linux/auxvec.rs @@ -51,7 +51,7 @@ pub(crate) struct AuxVec { /// Note that run-time feature detection is not invoked for features that can /// be detected at compile-time. /// -/// Note: We always directly use `getauxval` on `*-linux-{gnu,musl,ohos,pauthtest}*` and +/// Note: We always directly use `getauxval` on `*-linux-{gnu,musl,ohos}*` and /// `*-android*` targets rather than `dlsym` it because we can safely assume /// `getauxval` is linked to the binary. /// - `*-linux-gnu*` targets ([since Rust 1.64](https://blog.rust-lang.org/2022/08/01/Increasing-glibc-kernel-requirements.html)) @@ -125,7 +125,7 @@ fn getauxval(key: usize) -> Result { any( all( target_os = "linux", - any(target_env = "gnu", target_env = "musl", target_env = "ohos", target_env = "pauthtest"), + any(target_env = "gnu", target_env = "musl", target_env = "ohos"), ), target_os = "android", ) => { diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index c9062a87d9d24..071e9bead456f 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -57,7 +57,12 @@ cfg_select! { } } -#[cfg(target_env = "musl")] +// For pauthtest the only supported unwinding mechanism is provided by libunwind. +#[cfg(target_abi = "pauthtest")] +#[link(name = "unwind")] +unsafe extern "C" {} + +#[cfg(all(target_env = "musl", not(target_abi = "pauthtest")))] cfg_select! { all(feature = "llvm-libunwind", feature = "system-llvm-libunwind") => { compile_error!("`llvm-libunwind` and `system-llvm-libunwind` cannot be enabled at the same time"); @@ -95,11 +100,6 @@ cfg_select! { } } -// For pauthtest the only supported unwinding mechanism is provided by libunwind. -#[cfg(target_env = "pauthtest")] -#[link(name = "unwind")] -unsafe extern "C" {} - // This is the same as musl except that we default to using the system libunwind // instead of libgcc. #[cfg(target_env = "ohos")]