From 93630386128aeeb8d098e0b926362aa2a24b6474 Mon Sep 17 00:00:00 2001 From: Rohan Singla Date: Thu, 18 Jun 2026 15:23:02 +0530 Subject: [PATCH 01/38] bootstrap: fix RUSTFLAGS with spaces in paths via CARGO_ENCODED_RUSTFLAGS --- src/bootstrap/src/core/builder/cargo.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 24d7a24152a32..8e135a3c20a30 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -14,18 +14,18 @@ use crate::{ RemapScheme, TargetSelection, command, prepare_behaviour_dump_dir, t, }; -/// Represents flag values in `String` form with whitespace delimiter to pass it to the compiler -/// later. +/// Represents flag values passed to the compiler, stored as individual arguments to support +/// values that contain spaces (such as paths from `llvm-config --libdir`). /// /// `-Z crate-attr` flags will be applied recursively on the target code using the /// `rustc_parse::parser::Parser`. See `rustc_builtin_macros::cmdline_attrs::inject` for more /// information. #[derive(Debug, Clone)] -struct Rustflags(String, TargetSelection); +struct Rustflags(Vec, TargetSelection); impl Rustflags { fn new(target: TargetSelection) -> Rustflags { - Rustflags(String::new(), target) + Rustflags(Vec::new(), target) } /// By default, cargo will pick up on various variables in the environment. However, bootstrap @@ -51,11 +51,9 @@ impl Rustflags { } fn arg(&mut self, arg: &str) -> &mut Self { - assert_eq!(arg.split(' ').count(), 1); - if !self.0.is_empty() { - self.0.push(' '); + if !arg.is_empty() { + self.0.push(arg.to_owned()); } - self.0.push_str(arg); self } @@ -459,12 +457,14 @@ impl From for BootstrapCommand { let rustflags = &cargo.rustflags.0; if !rustflags.is_empty() { - cargo.command.env("RUSTFLAGS", rustflags); + // CARGO_ENCODED_RUSTFLAGS uses \x1f (unit separator) as delimiter, which allows + // individual flags to contain spaces (e.g. paths from `llvm-config --libdir`). + cargo.command.env("CARGO_ENCODED_RUSTFLAGS", rustflags.join("\x1f")); } let rustdocflags = &cargo.rustdocflags.0; if !rustdocflags.is_empty() { - cargo.command.env("RUSTDOCFLAGS", rustdocflags); + cargo.command.env("RUSTDOCFLAGS", rustdocflags.join(" ")); } let encoded_hostflags = cargo.hostflags.encode(); @@ -1181,8 +1181,9 @@ impl Builder<'_> { if (mode == Mode::ToolRustcPrivate || mode == Mode::Codegen) && let Some(llvm_config) = self.llvm_config(target) { - let llvm_libdir = + let llvm_libdir_raw = command(llvm_config).cached().arg("--libdir").run_capture_stdout(self).stdout(); + let llvm_libdir = llvm_libdir_raw.trim(); if target.is_msvc() { rustflags.arg(&format!("-Clink-arg=-LIBPATH:{llvm_libdir}")); } else { From 63d3395c4c69e71a942ae0cf7c1a9986f1f1120d Mon Sep 17 00:00:00 2001 From: Rohan Singla Date: Thu, 18 Jun 2026 16:42:33 +0530 Subject: [PATCH 02/38] bootstrap: also use CARGO_ENCODED_RUSTDOCFLAGS to support spaces in paths --- src/bootstrap/src/core/builder/cargo.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 8e135a3c20a30..b025bfdecbf20 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -464,7 +464,7 @@ impl From for BootstrapCommand { let rustdocflags = &cargo.rustdocflags.0; if !rustdocflags.is_empty() { - cargo.command.env("RUSTDOCFLAGS", rustdocflags.join(" ")); + cargo.command.env("CARGO_ENCODED_RUSTDOCFLAGS", rustdocflags.join("\x1f")); } let encoded_hostflags = cargo.hostflags.encode(); From 386748b45ac3d0756fb2dcf997896f11c4eae617 Mon Sep 17 00:00:00 2001 From: Rohan Singla Date: Thu, 18 Jun 2026 17:44:33 +0530 Subject: [PATCH 03/38] =?UTF-8?q?=E2=8F=BA=20bootstrap:=20reset=20CARGO=5F?= =?UTF-8?q?ENCODED=5FRUSTFLAGS=20in=20Miri=20test=20runner?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/tools/miri/tests/ui.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index 633452f6052d7..3e46ee17c8a65 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -182,8 +182,9 @@ fn miri_config( .map(Into::into) .collect(), envs: vec![ - // Reset `RUSTFLAGS` to work around . + // Reset `RUSTFLAGS`/`CARGO_ENCODED_RUSTFLAGS` to work around . ("RUSTFLAGS".into(), None), + ("CARGO_ENCODED_RUSTFLAGS".into(), None), // Reset `MIRIFLAGS` because it caused trouble in the past and should not be needed. ("MIRIFLAGS".into(), None), // Allow `cargo miri build`. From 8085cd3b44bf0c9683197c6082763a97c3dbb0f9 Mon Sep 17 00:00:00 2001 From: Rohan Singla Date: Thu, 18 Jun 2026 23:11:08 +0530 Subject: [PATCH 04/38] bootstrap: use \x1f-delimited String for Rustflags instead of Vec --- src/bootstrap/src/core/builder/cargo.rs | 26 ++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index b025bfdecbf20..cabcd5e86efa7 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -14,18 +14,21 @@ use crate::{ RemapScheme, TargetSelection, command, prepare_behaviour_dump_dir, t, }; -/// Represents flag values passed to the compiler, stored as individual arguments to support -/// values that contain spaces (such as paths from `llvm-config --libdir`). +/// Represents flag values in `String` form with a `\x1f` delimiter to pass to the compiler later. +/// +/// Flags are emitted via `CARGO_ENCODED_RUSTFLAGS` / `CARGO_ENCODED_RUSTDOCFLAGS`, +/// which use `\x1f` (ASCII Unit Separator) as the delimiter and therefore allow spaces +/// within individual flag values (e.g. paths from `llvm-config --libdir`). /// /// `-Z crate-attr` flags will be applied recursively on the target code using the /// `rustc_parse::parser::Parser`. See `rustc_builtin_macros::cmdline_attrs::inject` for more /// information. #[derive(Debug, Clone)] -struct Rustflags(Vec, TargetSelection); +struct Rustflags(String, TargetSelection); impl Rustflags { fn new(target: TargetSelection) -> Rustflags { - Rustflags(Vec::new(), target) + Rustflags(String::new(), target) } /// By default, cargo will pick up on various variables in the environment. However, bootstrap @@ -51,8 +54,15 @@ impl Rustflags { } fn arg(&mut self, arg: &str) -> &mut Self { + assert!( + !arg.contains('\x1f'), + "rustflag must not contain the ASCII unit separator (\\x1f): {arg:?}" + ); if !arg.is_empty() { - self.0.push(arg.to_owned()); + if !self.0.is_empty() { + self.0.push('\x1f'); + } + self.0.push_str(arg); } self } @@ -457,14 +467,12 @@ impl From for BootstrapCommand { let rustflags = &cargo.rustflags.0; if !rustflags.is_empty() { - // CARGO_ENCODED_RUSTFLAGS uses \x1f (unit separator) as delimiter, which allows - // individual flags to contain spaces (e.g. paths from `llvm-config --libdir`). - cargo.command.env("CARGO_ENCODED_RUSTFLAGS", rustflags.join("\x1f")); + cargo.command.env("CARGO_ENCODED_RUSTFLAGS", rustflags); } let rustdocflags = &cargo.rustdocflags.0; if !rustdocflags.is_empty() { - cargo.command.env("CARGO_ENCODED_RUSTDOCFLAGS", rustdocflags.join("\x1f")); + cargo.command.env("CARGO_ENCODED_RUSTDOCFLAGS", rustdocflags); } let encoded_hostflags = cargo.hostflags.encode(); From b4a7c3eaef31af895127408796c72744c18ec810 Mon Sep 17 00:00:00 2001 From: Rohan Singla Date: Mon, 22 Jun 2026 02:55:59 +0530 Subject: [PATCH 05/38] unset RUSTFLAGS/RUSTDOCFLAGS when setting encoded forms --- src/bootstrap/src/core/builder/cargo.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index cabcd5e86efa7..b76dc77683f94 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -465,6 +465,13 @@ impl From for BootstrapCommand { cargo.command.args(cargo.args); + // Always unset the plain RUSTFLAGS/RUSTDOCFLAGS so that downstream + // tools (e.g. build.rs scripts) see only the encoded form. Any flags + // from the caller's environment have already been folded into the + // Rustflags struct via `propagate_cargo_env`. + cargo.command.env_remove("RUSTFLAGS"); + cargo.command.env_remove("RUSTDOCFLAGS"); + let rustflags = &cargo.rustflags.0; if !rustflags.is_empty() { cargo.command.env("CARGO_ENCODED_RUSTFLAGS", rustflags); From dcc21d6329e74939f109737e3faf29b2dc731727 Mon Sep 17 00:00:00 2001 From: Mahdi Ali-Raihan Date: Sat, 20 Jun 2026 02:28:37 -0400 Subject: [PATCH 06/38] Added implementation on set_permissions_nofollow for all platforms supported (windows, unix, uefi, etc.); modified the Unix implementation to use fchmodat instead of open + fchmod; clarified documentations for set_permissions_nofollow; added a test case for set_permissions_nofollow --- library/std/src/fs.rs | 53 ++++++++++++++--- library/std/src/fs/tests.rs | 83 +++++++++++++++++++++++++++ library/std/src/sys/fs/hermit.rs | 4 ++ library/std/src/sys/fs/mod.rs | 24 +------- library/std/src/sys/fs/motor.rs | 5 ++ library/std/src/sys/fs/solid.rs | 5 ++ library/std/src/sys/fs/uefi.rs | 5 ++ library/std/src/sys/fs/unix.rs | 37 ++++++++++++ library/std/src/sys/fs/unsupported.rs | 4 ++ library/std/src/sys/fs/vexos.rs | 4 ++ library/std/src/sys/fs/windows.rs | 9 +++ 11 files changed, 203 insertions(+), 30 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 750ddb91c482b..05695dacbf912 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -3337,23 +3337,60 @@ pub fn set_permissions>(path: P, perm: Permissions) -> io::Result fs_imp::set_permissions(path.as_ref(), perm.0) } -/// Set the permissions of a file, unless it is a symlink. +/// Changes the permissions found on a file or a directory. On certain platforms, if the file +/// is a symlink, it will change the permissions bits on the symlink itself rather than +/// the target (e.g. Windows, BSD, MacOS). On other platforms, this results in an error when +/// attempting to change permissions on a symlink (e.g. Linux). /// -/// Note that the non-final path elements are allowed to be symlinks. +/// Note that non-final path elements are allowed to be symlinks. /// /// # Platform-specific behavior /// -/// Currently unimplemented on Windows. +/// This function currently corresponds to `open` with `O_NOFOLLOW` flag enabled +/// + `fchmod` on WASI and the `fchmodat` function on all other Unix platforms with +/// the flag `AT_SYMLINK_NOFOLLOW` enabled. On Windows, the file is opened +/// with the flag `FILE_FLAG_OPEN_REPARSE_POINT` enabled and then the permissions +/// is set through `SetFileInformationByHandle`. On all other platforms, the behavior +/// remains the same with [`fs::set_permissions`]. /// -/// On Unix platforms, this results in a [`FilesystemLoop`] error if the last element is a symlink. +/// [`fs::set_permissions`]: crate::fs::set_permissions /// -/// This behavior may change in the future. +/// Note that, this [may change in the future][changes]. /// -/// [`FilesystemLoop`]: crate::io::ErrorKind::FilesystemLoop -#[doc(alias = "chmod", alias = "SetFileAttributes")] +/// [changes]: io#platform-specific-behavior +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// * `path` does not exist. +/// * The user lacks the permission to change attributes of the file. +/// +/// Note: On Linux, this will result in a [`Unsupported`] error +/// if the final element is a symlink. +/// +/// [`Unsupported`]: crate::io::ErrorKind::Unsupported +/// +/// # Examples +/// +/// ```no_run +/// #![feature(set_permissions_nofollow)] +/// use std::fs; +/// +/// fn main() -> std::io::Result<()> { +/// let mut perms = fs::symlink_metadata("foo.txt")?.permissions(); +/// perms.set_readonly(true); +/// // This should result in an error on certain platforms +/// // or succeed in modifying the permissions of a symlink +/// fs::set_permissions_nofollow("foo.txt", perms)?; +/// Ok(()) +/// } +/// ``` +#[doc(alias = "fchmodat", alias = "SetFileInformationByHandle")] #[unstable(feature = "set_permissions_nofollow", issue = "141607")] pub fn set_permissions_nofollow>(path: P, perm: Permissions) -> io::Result<()> { - fs_imp::set_permissions_nofollow(path.as_ref(), perm) + fs_imp::set_permissions_nofollow(path.as_ref(), perm.0) } impl DirBuilder { diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index eb619aa4884c5..bf58660754876 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -613,6 +613,66 @@ fn set_get_unix_permissions() { assert_eq!(mask & metadata1.permissions().mode(), 0o0777); } +#[test] +fn set_get_permissions_nofollows() { + let tmpdir = tmpdir(); + let filename = tmpdir.join("set_get_unix_permissions_file"); + check!(File::create(&filename)); + let file_metadata = check!(fs::metadata(&filename)); + assert!(!file_metadata.permissions().readonly()); + let mut permission_bits = file_metadata.permissions(); + permission_bits.set_readonly(true); + let result = fs::set_permissions_nofollow(&filename, permission_bits); + + cfg_select! { + any(windows, unix, target_os = "uefi", target_os = "solid_asp3", target_os = "motor") => { + assert_eq!(result.unwrap(), ()); + let metadata0 = check!(fs::metadata(&filename)); + assert!(metadata0.permissions().readonly()); + }, + _ => { + let error_kind = result.unwrap_err().kind(); + assert_eq!(error_kind, crate::io::ErrorKind::Unsupported); + } + } +} + +// Only Windows and Unix support `fs::set_permissions_nofollow` +#[test] +#[cfg(any(windows, unix))] +fn set_get_permissions_nofollows_symlink() { + #[cfg(not(windows))] + use crate::os::unix::fs::symlink; + #[cfg(windows)] + use crate::os::windows::fs::symlink_dir; + + let tmpdir = tmpdir(); + let filename = tmpdir.join("set_get_unix_permissions_file"); + let symlink_name = tmpdir.join("set_get_unix_permissions"); + check!(File::create(&filename)); + #[cfg(not(windows))] + check!(symlink(&filename, &symlink_name)); + #[cfg(windows)] + check!(symlink_dir(&filename, &symlink_name)); + + let sym_metadata = check!(fs::symlink_metadata(&symlink_name)); + let mut permission_bits = sym_metadata.permissions(); + permission_bits.set_readonly(true); + let result = fs::set_permissions_nofollow(&symlink_name, permission_bits); + + cfg_select! { + any(target_os = "macos", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "dragonfly", target_os = "espidf", target_os = "horizon") => { + assert_eq!(result.unwrap(), ()); + let metadata0 = check!(fs::symlink_metadata(&symlink_name)); + assert!(metadata0.permissions().readonly()); + }, + _ => { + let error_kind = result.unwrap_err().kind(); + assert_eq!(error_kind, crate::io::ErrorKind::Unsupported); + } + } +} + #[test] #[cfg(windows)] fn file_test_io_seek_read_write() { @@ -1330,6 +1390,29 @@ fn fchmod_works() { check!(file.set_permissions(p)); } +#[test] +fn fchmodat_works() { + let tmpdir = tmpdir(); + let file = tmpdir.join("in.txt"); + + check!(File::create(&file)); + let attr = check!(fs::metadata(&file)); + assert!(!attr.permissions().readonly()); + let mut p = attr.permissions(); + p.set_readonly(true); + check!(fs::set_permissions_nofollow(&file, p.clone())); + let attr = check!(fs::metadata(&file)); + assert!(attr.permissions().readonly()); + + match fs::set_permissions_nofollow(&tmpdir.join("foo"), p.clone()) { + Ok(..) => panic!("wanted an error"), + Err(..) => {} + } + + p.set_readonly(false); + check!(fs::set_permissions_nofollow(&file, p)); +} + #[test] fn sync_doesnt_kill_anything() { let tmpdir = tmpdir(); diff --git a/library/std/src/sys/fs/hermit.rs b/library/std/src/sys/fs/hermit.rs index 5f69560998293..d29ea81c67e6d 100644 --- a/library/std/src/sys/fs/hermit.rs +++ b/library/std/src/sys/fs/hermit.rs @@ -566,6 +566,10 @@ pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { Err(Error::from_raw_os_error(22)) } +pub fn set_perm_nofollow(_p: &Path, _perm: FilePermissions) -> io::Result<()> { + unsupported() +} + pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { Err(Error::from_raw_os_error(22)) } diff --git a/library/std/src/sys/fs/mod.rs b/library/std/src/sys/fs/mod.rs index 0c297c5766b82..b5a5fa93fd491 100644 --- a/library/std/src/sys/fs/mod.rs +++ b/library/std/src/sys/fs/mod.rs @@ -120,28 +120,8 @@ pub fn set_permissions(path: &Path, perm: FilePermissions) -> io::Result<()> { with_native_path(path, &|path| imp::set_perm(path, perm.clone())) } -#[cfg(all(unix, not(target_os = "vxworks")))] -pub fn set_permissions_nofollow(path: &Path, perm: crate::fs::Permissions) -> io::Result<()> { - use crate::fs::OpenOptions; - - let mut options = OpenOptions::new(); - - // ESP-IDF and Horizon do not support O_NOFOLLOW, so we skip setting it. - // Their filesystems do not have symbolic links, so no special handling is required. - #[cfg(not(any(target_os = "espidf", target_os = "horizon")))] - { - use crate::os::unix::fs::OpenOptionsExt; - options.custom_flags(libc::O_NOFOLLOW); - } - - options.open(path)?.set_permissions(perm) -} - -#[cfg(any(not(unix), target_os = "vxworks"))] -pub fn set_permissions_nofollow(_path: &Path, _perm: crate::fs::Permissions) -> io::Result<()> { - crate::unimplemented!( - "`set_permissions_nofollow` is currently only implemented on Unix platforms" - ) +pub fn set_permissions_nofollow(path: &Path, perm: FilePermissions) -> io::Result<()> { + with_native_path(path, &|path| imp::set_perm_nofollow(path, perm.clone())) } pub fn canonicalize(path: &Path) -> io::Result { diff --git a/library/std/src/sys/fs/motor.rs b/library/std/src/sys/fs/motor.rs index 51b7f347b0b17..a76f64a47c24a 100644 --- a/library/std/src/sys/fs/motor.rs +++ b/library/std/src/sys/fs/motor.rs @@ -319,6 +319,11 @@ pub fn remove_dir_all(path: &Path) -> io::Result<()> { } pub fn set_perm(path: &Path, perm: FilePermissions) -> io::Result<()> { + // Motor does not support symlinks + set_perm_nofollow(path, perm) +} + +pub fn set_perm_nofollow(path: &Path, perm: FilePermissions) -> io::Result<()> { let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?; moto_rt::fs::set_perm(path, perm.rt_perm).map_err(map_motor_error) } diff --git a/library/std/src/sys/fs/solid.rs b/library/std/src/sys/fs/solid.rs index b61147db57ed5..bd963b1d2f038 100644 --- a/library/std/src/sys/fs/solid.rs +++ b/library/std/src/sys/fs/solid.rs @@ -531,6 +531,11 @@ pub fn rename(old: &Path, new: &Path) -> io::Result<()> { } pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { + // Solid does not support symlinks + set_perm_nofollow(p, perm) +} + +pub fn set_perm_nofollow(p: &Path, perm: FilePermissions) -> io::Result<()> { error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Chmod(cstr(p)?.as_ptr(), perm.0.into()) }) diff --git a/library/std/src/sys/fs/uefi.rs b/library/std/src/sys/fs/uefi.rs index c4467ed23e125..1b4c1074b4339 100644 --- a/library/std/src/sys/fs/uefi.rs +++ b/library/std/src/sys/fs/uefi.rs @@ -480,6 +480,11 @@ pub fn rename(old: &Path, new: &Path) -> io::Result<()> { } pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { + // UEFI does not support symlinks + set_perm_nofollow(p, perm); +} + +pub fn set_perm_nofollow(p: &Path, perm: FilePermissions) -> io::Result<()> { let f = uefi_fs::File::from_path(p, file::MODE_READ | file::MODE_WRITE, 0)?; set_perm_inner(&f, perm) } diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 3152a22534f6c..9f65c486b8c26 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1989,6 +1989,43 @@ pub fn set_perm(p: &CStr, perm: FilePermissions) -> io::Result<()> { cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) }).map(|_| ()) } +pub fn set_perm_nofollow(p: &CStr, perm: FilePermissions) -> io::Result<()> { + // ESP-IDF and Horizon do not support O_NOFOLLOW, so we skip setting it. + // Their filesystems do not have symbolic links, so no special handling is required. + cfg_select! { + // wasm32-wasip1 targets do not support fchmodat, so we fall down to + // open + fchmod + target_os = "wasi" => { + use crate::fs::OpenOptions; + use crate::fs::Permissions; + use crate::os::wasi::ffi::OsStrExt; + let mut options = OpenOptions::new(); + + #[cfg(not(any(target_os = "espidf", target_os = "horizon")))] + { + use crate::os::wasi::fs::OpenOptionsExt; + options.custom_flags(libc::O_NOFOLLOW); + } + + let bytes = p.to_bytes(); + let os_str = OsStr::from_bytes(bytes); + options.open(Path::new(os_str))?.set_permissions(Permissions::from_inner(perm)) + } + all(target_os = "linux", not(any(target_os = "espidf", target_os = "horizon"))) => { + cvt_r(|| unsafe { + libc::fchmodat(libc::AT_FDCWD, p.as_ptr(), perm.mode, libc::AT_SYMLINK_NOFOLLOW) + }) + .map(|_| ()) + }, + _ => { + cvt_r(|| unsafe { + libc::fchmodat(libc::AT_FDCWD, p.as_ptr(), perm.mode, 0) + }) + .map(|_| ()) + } + } +} + pub fn rmdir(p: &CStr) -> io::Result<()> { cvt(unsafe { libc::rmdir(p.as_ptr()) }).map(|_| ()) } diff --git a/library/std/src/sys/fs/unsupported.rs b/library/std/src/sys/fs/unsupported.rs index 703ebef380383..512af27401dfe 100644 --- a/library/std/src/sys/fs/unsupported.rs +++ b/library/std/src/sys/fs/unsupported.rs @@ -313,6 +313,10 @@ pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { match perm.0 {} } +pub fn set_perm_nofollow(_p: &Path, perm: FilePermissions) -> io::Result<()> { + match perm.0 {} +} + pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { unsupported() } diff --git a/library/std/src/sys/fs/vexos.rs b/library/std/src/sys/fs/vexos.rs index 9de9570cb5b24..0ed0a4cbfc437 100644 --- a/library/std/src/sys/fs/vexos.rs +++ b/library/std/src/sys/fs/vexos.rs @@ -492,6 +492,10 @@ pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { unsupported() } +pub fn set_perm_nofollow(_p: &Path, _perm: FilePermissions) -> io::Result<()> { + unsupported() +} + pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { unsupported() } diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index e3e7b081b47d5..a7e8e8b3ac630 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -1564,6 +1564,15 @@ pub fn set_perm(p: &WCStr, perm: FilePermissions) -> io::Result<()> { } } +pub fn set_perm_nofollow(p: &WCStr, perm: FilePermissions) -> io::Result<()> { + let mut opts = OpenOptions::new(); + opts.access_mode(c::FILE_WRITE_ATTRIBUTES); + // `FILE_FLAG_OPEN_REPARSE_POINT` for no_follow behavior + opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT); + let file = File::open_native(p, &opts)?; + file.set_permissions(perm) +} + pub fn set_times(p: &WCStr, times: FileTimes) -> io::Result<()> { let mut opts = OpenOptions::new(); opts.access_mode(c::FILE_WRITE_ATTRIBUTES); From dbc4eb8f42ad2d2a9e7170d52e00512a6e12ce69 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 3 Apr 2026 14:55:36 +0000 Subject: [PATCH 07/38] Introduce `aarch64-unknown-linux-pauthtest` Co-authored-by: Daniil Kovalev --- compiler/rustc_codegen_llvm/src/builder.rs | 53 ++- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 8 + compiler/rustc_codegen_llvm/src/llvm/mod.rs | 16 + compiler/rustc_codegen_llvm/src/llvm_util.rs | 2 + .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 29 ++ compiler/rustc_session/src/errors.rs | 6 + compiler/rustc_session/src/session.rs | 7 +- compiler/rustc_target/src/spec/mod.rs | 2 + .../aarch64_unknown_linux_pauthtest.rs | 36 ++ src/bootstrap/src/core/sanity.rs | 1 + src/doc/rustc/src/SUMMARY.md | 1 + src/doc/rustc/src/platform-support.md | 1 + .../aarch64-unknown-linux-pauthtest.md | 438 ++++++++++++++++++ src/librustdoc/clean/cfg.rs | 1 + .../src/directives/directive_names.rs | 2 + ...targets-aarch64_unknown_linux_pauthtest.rs | 44 ++ tests/assembly-llvm/targets/targets-elf.rs | 3 + tests/ui/check-cfg/well-known-values.stderr | 2 +- tests/ui/statics/crt-static-pauthtest.rs | 9 + tests/ui/statics/crt-static-pauthtest.stderr | 4 + 20 files changed, 661 insertions(+), 4 deletions(-) create mode 100644 compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_pauthtest.rs create mode 100644 src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md create mode 100644 tests/assembly-llvm/targets/targets-aarch64_unknown_linux_pauthtest.rs create mode 100644 tests/ui/statics/crt-static-pauthtest.rs create mode 100644 tests/ui/statics/crt-static-pauthtest.stderr diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index afb6985d21a95..52ffac2146258 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -7,7 +7,7 @@ pub(crate) mod autodiff; pub(crate) mod gpu_offload; use libc::{c_char, c_uint}; -use rustc_abi::{self as abi, Align, Size, WrappingRange}; +use rustc_abi::{self as abi, Align, CanonAbi, Size, WrappingRange}; use rustc_codegen_ssa::MemFlags; use rustc_codegen_ssa::common::{IntPredicate, RealPredicate, SynchronizationScope, TypeKind}; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; @@ -26,7 +26,7 @@ use rustc_sanitizers::{cfi, kcfi}; use rustc_session::config::OptLevel; use rustc_span::Span; use rustc_target::callconv::{FnAbi, PassMode}; -use rustc_target::spec::{Arch, HasTargetSpec, SanitizerSet, Target}; +use rustc_target::spec::{Arch, Env, HasTargetSpec, SanitizerSet, Target}; use smallvec::SmallVec; use tracing::{debug, instrument}; @@ -475,6 +475,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { bundles.push(kcfi_bundle); } + let pauth = self.ptrauth_operand_bundle(llfn, fn_abi); + if let Some(p) = pauth.as_ref().map(|b| b.as_ref()) { + bundles.push(p); + } + let invoke = unsafe { llvm::LLVMBuildInvokeWithOperandBundles( self.llbuilder, @@ -1472,6 +1477,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { bundles.push(kcfi_bundle); } + let pauth = self.ptrauth_operand_bundle(llfn, fn_abi); + if let Some(p) = pauth.as_ref().map(|b| b.as_ref()) { + bundles.push(p); + } + let call = unsafe { llvm::LLVMBuildCallWithOperandBundles( self.llbuilder, @@ -1902,6 +1912,11 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { bundles.push(kcfi_bundle); } + let pauth = self.ptrauth_operand_bundle(llfn, fn_abi); + if let Some(p) = pauth.as_ref().map(|b| b.as_ref()) { + bundles.push(p); + } + let callbr = unsafe { llvm::LLVMBuildCallBr( self.llbuilder, @@ -2021,6 +2036,40 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { kcfi_bundle } + // Emits pauth operand bundle. + fn ptrauth_operand_bundle( + &mut self, + llfn: &'ll Value, + fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, + ) -> Option> { + if self.sess().target.env != Env::Pauthtest { + return None; + } + // Pointer authentication support is currently limited to extern "C" calls; filter out other + // ABIs. + if fn_abi?.conv != CanonAbi::C { + return None; + } + // Filter out LLVM intrinsics. + if llvm::get_value_name(llfn).starts_with(b"llvm.") { + return None; + } + + // FIXME(jchlanda) Operand bundles should only be attached to indirect function calls. + // However, function pointer signing is currently performed in `get_fn_addr`, which causes + // the logic to be applied too broadly, including to function values (not just pointers). + // As a result, direct calls using signed function values must also receive operand + // bundles. + // Once this is resolved, we should analyze each call and skip direct calls. See the + // discussion in the rust-lang issue: + let key: u32 = 0; + let discriminator: u64 = 0; + Some(llvm::OperandBundleBox::new( + "ptrauth", + &[self.const_u32(key), self.const_u64(discriminator)], + )) + } + /// Emits a call to `llvm.instrprof.increment`. Used by coverage instrumentation. #[instrument(level = "debug", skip(self))] pub(crate) fn instrprof_increment( diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index dd527eafe8362..bd770d286851b 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2647,4 +2647,12 @@ unsafe extern "C" { Aliasee: &Value, Name: *const c_char, ) -> &'ll Value; + + pub(crate) fn LLVMRustConstPtrAuth( + ptr: *const Value, + key: u32, + disc: u64, + addr_diversity: *const Value, + deactivation_symbol: *const Value, + ) -> *const Value; } diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 2ec19b1795b5a..e9bc6ae0e80ef 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -475,3 +475,19 @@ pub(crate) fn add_alias<'ll>( ) -> &'ll Value { unsafe { LLVMAddAlias2(module, ty, address_space.0, aliasee, name.as_ptr()) } } + +/// Safe wrapper for `LLVMRustConstPtrAuth`. +pub(crate) fn const_ptr_auth<'ll>( + ptr: &'ll Value, + key: u32, + disc: u64, + addr_diversity: Option<&'ll Value>, +) -> &'ll Value { + unsafe { + let addr_div_ptr = addr_diversity.map_or(std::ptr::null(), |v| v as *const Value); + let deactivation_symbol = std::ptr::null(); + let result = + LLVMRustConstPtrAuth(ptr as *const Value, key, disc, addr_div_ptr, deactivation_symbol); + &*result + } +} diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 73b7f699b606d..951094d9aa369 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -387,6 +387,8 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) { cfg.has_reliable_f128 = match (target_arch, target_os) { // Unsupported https://github.com/llvm/llvm-project/issues/121122 (Arch::AmdGpu, _) => false, + // Pauthtest musl does not support 128-bit floating point math. + (Arch::AArch64, _) if *target_env == Env::Pauthtest => false, // Unsupported (Arch::Arm64EC, _) => false, // Selection bug . This issue is closed diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 8b063af187a58..3b8e6f6415365 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1848,6 +1848,35 @@ extern "C" bool LLVMRustIsTargetIntrinsic(unsigned ID) { return Intrinsic::isTargetIntrinsic(ID); } +extern "C" LLVMValueRef LLVMRustConstPtrAuth(LLVMValueRef Ptr, uint32_t Key, + uint64_t Disc, + LLVMValueRef AddrDiversity, + LLVMValueRef DeactivationSymbol) { + auto *C = cast(unwrap(Ptr)); + assert(C->getType()->isPointerTy() && "Expected pointer type"); + assert(!isa(C) && "Unexpected undef in const_ptr_auth"); + assert(!isa(C) && "Unexpected null in const_ptr_auth"); + + LLVMContext &Ctx = C->getContext(); + auto *KeyC = ConstantInt::get(Type::getInt32Ty(Ctx), Key); + auto *DiscC = ConstantInt::get(Type::getInt64Ty(Ctx), Disc); + auto *PTy = cast(C->getType()); + Constant *AddrDiv = + AddrDiversity ? dyn_cast(unwrap(AddrDiversity)) + : ConstantPointerNull::get(cast(C->getType())); + assert(AddrDiv && "Failed to get Address Diversity"); +#if LLVM_VERSION_GE(22, 0) + Constant *DeactivationSym = + DeactivationSymbol ? dyn_cast(unwrap(DeactivationSymbol)) + : ConstantPointerNull::get(PTy); + assert(DeactivationSym && "Failed to get Deactivation Symbol"); + + return wrap(ConstantPtrAuth::get(C, KeyC, DiscC, AddrDiv, DeactivationSym)); +#else + return wrap(ConstantPtrAuth::get(C, KeyC, DiscC, AddrDiv)); +#endif +} + // Statically assert that the fixed metadata kind IDs declared in // `metadata_kind.rs` match the ones actually used by LLVM. #define FIXED_MD_KIND(VARIANT, VALUE) \ diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 41b35f5700eca..31627887b662b 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -311,6 +311,12 @@ pub(crate) struct CannotMixAndMatchSanitizers { )] pub(crate) struct CannotEnableCrtStaticLinux; +#[derive(Diagnostic)] +#[diag( + "pointer authentication requires dynamic linking. Statically linked libc is incompatible, disable it using `-C target-feature=-crt-static`" +)] +pub(crate) struct CannotEnableCrtStaticPointerAuth; + #[derive(Diagnostic)] #[diag("`-Zsanitizer=cfi` requires `-Clto` or `-Clinker-plugin-lto`")] pub(crate) struct SanitizerCfiRequiresLto; diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 580c9f2a34b17..66d4620641190 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -29,7 +29,7 @@ use rustc_span::source_map::{FilePathMapping, SourceMap}; use rustc_span::{RealFileName, Span, Symbol}; use rustc_target::asm::InlineAsmArch; use rustc_target::spec::{ - Arch, CodeModel, DebuginfoKind, Os, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, + Arch, CodeModel, DebuginfoKind, Env, Os, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, SmallDataThresholdSupport, SplitDebuginfo, StackProtector, SymbolVisibility, Target, TargetTuple, TlsModel, apple, }; @@ -1229,6 +1229,11 @@ fn validate_commandline_args_with_session_available(sess: &Session) { sess.dcx().emit_err(errors::CannotEnableCrtStaticLinux); } + // Using static linking is prohibited on pauthtest target + if sess.crt_static(None) && sess.target.env == Env::Pauthtest { + sess.dcx().emit_err(errors::CannotEnableCrtStaticPointerAuth); + } + // LLVM CFI requires LTO. if sess.is_sanitizer_cfi_enabled() && !(sess.lto() == config::Lto::Fat || sess.opts.cg.linker_plugin_lto.enabled()) diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index ef5c98642e9f8..dd077f1e89586 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1488,6 +1488,7 @@ supported_targets! { ("armv7-unknown-linux-musleabihf", armv7_unknown_linux_musleabihf), ("aarch64-unknown-linux-gnu", aarch64_unknown_linux_gnu), ("aarch64-unknown-linux-musl", aarch64_unknown_linux_musl), + ("aarch64-unknown-linux-pauthtest", aarch64_unknown_linux_pauthtest), ("aarch64_be-unknown-linux-musl", aarch64_be_unknown_linux_musl), ("x86_64-unknown-linux-musl", x86_64_unknown_linux_musl), ("i686-unknown-linux-musl", i686_unknown_linux_musl), @@ -2042,6 +2043,7 @@ crate::target_spec_enum! { P1 = "p1", P2 = "p2", P3 = "p3", + Pauthtest = "pauthtest", Uclibc = "uclibc", V5 = "v5", Unspecified = "", diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_pauthtest.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_pauthtest.rs new file mode 100644 index 0000000000000..b578b6c5cacf2 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_pauthtest.rs @@ -0,0 +1,36 @@ +use crate::spec::{ + Arch, Env, FramePointer, LinkSelfContainedDefault, StackProbeType, Target, TargetMetadata, + TargetOptions, base, +}; + +pub(crate) fn target() -> Target { + Target { + llvm_target: "aarch64-unknown-linux-pauthtest".into(), + metadata: TargetMetadata { + description: Some("ARM64 Linux with pauth enabled musl".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(false), + }, + pointer_width: 64, + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), + arch: Arch::AArch64, + + options: TargetOptions { + env: Env::Pauthtest, + // `pauthtest` requires v8.3a, which includes lse, no need for outline-atomics + features: "+v8.3a,+pauth".into(), + max_atomic_width: Some(128), + stack_probes: StackProbeType::Inline, + crt_static_default: false, + crt_static_allows_dylibs: false, + // the AAPCS64 expects use of non-leaf frame pointers per + // https://github.com/ARM-software/abi-aa/blob/4492d1570eb70c8fd146623e0db65b2d241f12e7/aapcs64/aapcs64.rst#the-frame-pointer + // and we tend to encounter interesting bugs in AArch64 unwinding code if we do not + frame_pointer: FramePointer::NonLeaf, + link_self_contained: LinkSelfContainedDefault::False, + mcount: "\u{1}_mcount".into(), + ..base::linux::opts() + }, + } +} diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 3c2348c9bf71c..8e19b45046ccc 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -38,6 +38,7 @@ pub struct Finder { const STAGE0_MISSING_TARGETS: &[&str] = &[ // just a dummy comment so the list doesn't get onelined "powerpc64-unknown-linux-gnuelfv2", + "aarch64-unknown-linux-pauthtest", // Stage 0 compiler is not guaranteed to see pauthtest yet. ]; /// Minimum version threshold for libstdc++ required when using prebuilt LLVM diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index af8060adcc262..9118bf124fed3 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -49,6 +49,7 @@ - [aarch64-nintendo-switch-freestanding](platform-support/aarch64-nintendo-switch-freestanding.md) - [aarch64-unknown-linux-gnu](platform-support/aarch64-unknown-linux-gnu.md) - [aarch64-unknown-linux-musl](platform-support/aarch64-unknown-linux-musl.md) + - [aarch64-unknown-linux-pauthtest](platform-support/aarch64-unknown-linux-pauthtest.md) - [aarch64-unknown-none*](platform-support/aarch64-unknown-none.md) - [aarch64v8r-unknown-none*](platform-support/aarch64v8r-unknown-none.md) - [aarch64_be-unknown-none-softfloat](platform-support/aarch64_be-unknown-none-softfloat.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 0bc90e2a14008..be64a6cfe3eb9 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -271,6 +271,7 @@ target | std | host | notes [`aarch64-unknown-hermit`](platform-support/hermit.md) | ✓ | | ARM64 Hermit [`aarch64-unknown-illumos`](platform-support/illumos.md) | ✓ | ✓ | ARM64 illumos `aarch64-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (ILP32 ABI) +[`aarch64-unknown-linux-pauthtest`](platform-support/aarch64-unknown-linux-pauthtest.md) | ✓ | ✓ | ARM64 PAC ELF ABI [`aarch64-unknown-managarm-mlibc`](platform-support/managarm.md) | ? | | ARM64 Managarm [`aarch64-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | ARM64 NetBSD [`aarch64-unknown-nto-qnx700`](platform-support/nto-qnx.md) | ? | | ARM64 QNX Neutrino 7.0 RTOS | diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md new file mode 100644 index 0000000000000..3630c66c3c60f --- /dev/null +++ b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md @@ -0,0 +1,438 @@ +# aarch64-unknown-linux-pauthtest + +**Tier: 3** + +This target enables Pointer Authentication Code (PAC) support in Rust on AArch64 +ELF-based Linux systems. It uses the `aarch64-unknown-linux-pauthtest` LLVM +target and a pointer-authentication-enabled sysroot with a custom musl as a +reference libc implementation. Dynamic linking is required, with a dynamic +linker acting as the ELF interpreter that can resolve pauth relocations and +enforce pointer authentication constraints. + +Supported features include: +* authentication of signed function pointers for extern "C" calls (corresponds + to LLVM's `-fptrauth-calls`) +* signing of return addresses before spilling to the stack and authentication + after restoring for non-leaf functions (corresponds to `-fptrauth-returns`) +* trapping on authentication failure when the FPAC feature is not present + (corresponds to `-fptrauth-auth-traps`) +* signing of init/fini array entries using the LLVM-defined pointer + authentication scheme (corresponds to `-fptrauth-init-fini` and + `-fptrauth-init-fini-address-discrimination`) +* non-ABI-affecting indirect control-flow hardening features as implemented in + LLVM (corresponds to `-faarch64-jump-table-hardening` and + `-fptrauth-indirect-gotos`) +* signed ELF GOT entries (gated behind `-Z ptrauth-elf-got`, off by default) + +A tracking issue for adding support for the AArch64 pointer authentication ABI +in Rust can be found at +[#148640](https://github.com/rust-lang/rust/issues/148640). + +Existing compiler support, such as enabling branch authentication instructions +(i.e.: `-Z branch-protection`) provide limited functionality, mainly signing +return addresses (`pac-ret`). The new target goes further by enabling ABI-level +pointer authentication support. + +This target does not define a new ABI; it builds on the existing C/C++ language +ABI with pointer authentication support added. However, different authentication +features, encoded in the signing schema, are not ABI-compatible with one +another. + +Useful links: +* Clang pointer authentication documentation: + https://clang.llvm.org/docs/PointerAuthentication.html +* LLVM pointer authentication documentation: + https://llvm.org/docs/PointerAuth.html +* PAuth ABI Extension to ELF for the AArch64 architecture: + https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst + +## Target maintainers + +[@jchlanda](https://github.com/jchlanda) + +## Requirements + +This target supports cross-compilation from any Linux host, but execution +requires AArch64 with pointer authentication support (ARMv8.3-A or higher). + +## Standard library support + +Full std support is available: `core`, `alloc`, and `std` all build +successfully. All library tests (`core`, `alloc`, `std`) pass for this target as +well. + +## Building the toolchain + +Building this target requires a pointer-authentication-enabled sysroot based on +a custom musl toolchain. The sysroot must be available on the system before +compilation. To build it, follow the instructions in the [build scripts +repo](https://github.com/access-softek/pauth-toolchain-build-scripts). + +The target uses Clang, please make sure it is `v22.1.0` or higher. When using a +system-provided Clang, a compiler wrapper is required to supply the necessary +flags. Please consult the listing: + +```sh +#!/usr/bin/env sh + +clang \ + -target aarch64-unknown-linux-pauthtest \ + -march=armv8.3-a+pauth \ + --sysroot /aarch64-linux-pauthtest/usr \ + -resource-dir /lib/clang/ \ + --rtlib=compiler-rt \ + --ld-path=/usr/bin/ld.lld \ + --unwindlib=libunwind \ + -Wl,--dynamic-linker=/aarch64-linux-pauthtest/usr/lib/libc.so \ + -Wl,--rpath=/aarch64-linux-pauthtest/usr/lib \ + "$@" +``` + +The Rust compiler validates the name of the configured C compiler, so when using +a wrapper its name must contain `clang`. A recommended name is +`aarch64-unknown-linux-pauthtest-clang`. Update the script to set `--sysroot`, +`-resource-dir`, `--dynamic-linker` and `--rpath` correctly by replacing +`` with the directory produced by the build scripts and the +`` with LLVM's version. Make the wrapper executable. + +To verify that the toolchain layout is correct, check that: +* the sysroot contains a pointer-authentication-enabled version of libunwind + (`/aarch64-linux-pauthtest/usr/lib/libunwind.so`), +* the Clang resource directory contains the appropriate `compiler-rt` objects + (`/lib/clang//lib/aarch64-unknown-linux-pauthtest/{clang_rt.crtbegin.o,clang_rt.crtend.o}`) + +When using the AccessSoftek scripts to build the sysroot, the result includes a +Clang-based toolchain. In this case, no wrapper script is required, +`/bin/aarch64-linux-pauthtest-clang` can be used directly. + +## Building the target + +Introduction of `aarch64-unknown-linux-pauthtest` target needs to be propagated +to various crates/repos, so that they can correctly recognise and handle it. +Specifically: +* `cc-rs`: https://github.com/jchlanda/cc-rs/tree/jakub/cc-v1.2.28-pauthtest +* `libc`: https://github.com/jchlanda/libc/tree/jakub/0.2.183-pauthtest +* `backtrace`: https://github.com/jchlanda/backtrace-rs/tree/jakub/backtrace-v0.3.76-pauthtest + +The patched versions of `cc-rs` and `libc` will have to be registered through +`[patch.crates-io]` section of `Cargo.toml` files both in: +`/src/bootstrap/` and `/library/`. Check out `cc-rs` and +`libc` to `/patches` and update config files. See attached diff for +details: + +
+ +```diff +diff --git a/library/Cargo.toml b/library/Cargo.toml +index e30e6240942..fb5a12f0065 100644 +--- a/library/Cargo.toml ++++ b/library/Cargo.toml +@@ -59,3 +59,4 @@ rustflags = ["-Cpanic=abort"] + rustc-std-workspace-core = { path = 'rustc-std-workspace-core' } + rustc-std-workspace-alloc = { path = 'rustc-std-workspace-alloc' } + rustc-std-workspace-std = { path = 'rustc-std-workspace-std' } ++libc = { path = '/patches/libc' } +diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml +index e1725db60cf..46763cdf9a4 100644 +--- a/src/bootstrap/Cargo.toml ++++ b/src/bootstrap/Cargo.toml +@@ -94,3 +94,6 @@ debug = 0 + [profile.dev.package] + # Only use debuginfo=1 to further reduce compile times. + bootstrap.debug = 1 ++ ++[patch.crates-io] ++cc = { path = '/patches/cc-rs' } +``` + +
+ +In contrast to `cc-rs` and `libc`, which are external crates resolved from +[crates.io](https://crates.io/) and can be overridden using `[patch.crates-io]`, +`backtrace` is included in the Rust repository as a git submodule under +`/library/backtrace`. At the time of writing, the necessary change +has not yet been committed there, which means an in-tree patch is currently +required. The patch: + +
+ +```diff +diff --git a/src/backtrace/libunwind.rs b/src/backtrace/libunwind.rs +index 0564f2e..a8a0d1a 100644 +--- a/src/backtrace/libunwind.rs ++++ b/src/backtrace/libunwind.rs +@@ -79,6 +79,18 @@ impl Frame { + // clause, and if this is fixed that test in theory can be run on macOS! + if cfg!(target_vendor = "apple") { + self.ip() ++ } else if cfg!(target_env = "pauthtest") { ++ // NOTE: ip here is an unsigned (raw) pointer, so we must not use ++ // uw::_Unwind_FindEnclosingFunction. ++ // ++ // Otherwise, in the pointer-authentication-enabled reference ++ // toolchain, libunwind would attempt to authenticate and re-sign ++ // values. Performing signing here is not safe: it could create a ++ // signing oracle, and more importantly it is incorrect under the ++ // expected signing schema. ++ // The schema requires the stack pointer (SP) as the discriminator. ++ // However, the SP available at this point would not match the SP ++ // at authentication/re-sign time, since ++ // _Unwind_FindEnclosingFunction constructs a new unwind context. ++ // The SP used here would therefore correspond to a different frame. ++ // As a result, we must return the raw value. ++ self.ip() + } else { + unsafe { uw::_Unwind_FindEnclosingFunction(self.ip()) } + } +``` + +
+ +The target can be built by enabling it for a `rustc` build. + +```toml +[build] +target = ["aarch64-unknown-linux-pauthtest"] +``` + +Specify the binaries used by the target. + +```toml +[target.aarch64-unknown-linux-pauthtest] +cc = "/aarch64-unknown-linux-pauthtest-clang" +ar = "/llvm-ar" +ranlib = "/llvm-ranlib" +linker = "/aarch64-unknown-linux-pauthtest-clang" +``` + +Note that `cc` and `linker` must refer to the same binary (either Clang itself +or its wrapper). The bootstrap process will fail if they differ. On non-AArch64 +systems, ensure that QEMU is installed and that `binfmt_misc` is correctly +configured so that foreign architecture binaries can be executed transparently. + +## Building Rust programs + +Rust does not currently ship precompiled artifacts for this target. Programs +must be built using a locally compiled Rust toolchain, with +`aarch64-unknown-linux-pauthtest` target enabled. + +For a comprehensive example of how to interact between C and Rust programs +within the testing framework please consult +`/tests/run-make/pauth-quicksort-c-driver/rmake.rs`, the test builds +a C executable linked against Rust library. +`/tests/run-make/pauth-quicksort-rust-driver/rmake.rs` shows how to +link a Rust program against a library compiled from a C source file. + +### Minimal standalone Rust and C interoperability example + +A minimal standalone example demonstrating Rust and C interoperability on the +`aarch64-unknown-linux-pauthtest` target is listed below. + +
+ +* Project structure + +```text +rust_c_indirect/ + ┣━ Cargo.toml + ┣━ build.rs + ┣━ src/ + ┃ ┗━ main.rs + ┣━ c_src/ + ┃ ┗━ plugin.c + ┗━ target/ +``` + +* `Cargo.toml` + +```toml +[package] +name = "rust_c_indirect" +edition = "2024" +build = "build.rs" +``` + +* `build.rs` + +```rust, ignore (platform-specific) +use std::env; +use std::path::Path; +use std::process::Command; + +fn main() { + println!("cargo:rerun-if-changed=c_src/plugin.c"); + + let clang = "/aarch64-unknown-linux-pauthtest-clang"; + + let out_dir = env::var("OUT_DIR").unwrap(); + let lib_path = Path::new(&out_dir).join("libplugin.so"); + let c_src = "c_src/plugin.c"; + + let status = Command::new(clang) + .args(["-shared", "-fPIC", c_src]) + .arg("-o") + .arg(&lib_path) + .status() + .unwrap_or_else(|_| panic!("failed to build shared library")); + assert!(status.success(), "failed to build shared library"); + + println!("cargo:rustc-link-arg=-Wl,--dynamic-linker=/aarch64-linux-pauthtest/usr/lib/libc.so"); + println!("cargo:rustc-link-arg=-Wl,-rpath,/aarch64-linux-pauthtest/usr/lib"); + println!("cargo:rustc-link-search=native={}", out_dir); + println!("cargo:rustc-link-lib=dylib=plugin"); +} + +``` + +* `src/main.rs` + +```rust, ignore (platform-specific) +use std::ptr; +use std::os::raw::c_int; + +unsafe extern "C" { + fn add(a: c_int, b: c_int) -> c_int; +} + +static OP: unsafe extern "C" fn(c_int, c_int) -> c_int = add; + +fn main() { + let a = 10; + let b = 32; + + let op = unsafe { ptr::read_volatile(&raw const OP) }; + let result = unsafe { op(a, b) }; + + println!("Result: {}", result); +} +``` + +* `c_src/plugin.c` + +```c +int add(int a, int b) { return a + b; } +``` + +
+ +* compile: `cargo build --target aarch64-unknown-linux-pauthtest --release` +* run: `./target/aarch64-unknown-linux-pauthtest/release/rust_c_indirect` + +Please make sure that `LD_LIBRARY_PATH` points to the directory containing +`libplugin.so`. For example: +`LD_LIBRARY_PATH=./target/aarch64-unknown-linux-pauthtest/release/build/rust_c_indirect-/out/`. + +To inspect pointer authentication behavior in IR, build with: +`RUSTFLAGS="--emit=llvm-ir"`. This generates an LLVM IR file, e.g.: +`target/aarch64-unknown-linux-pauthtest/release/deps/rust_c_indirect-*.ll`. +Relevant excerpt: + +```llvm +@_RNvCscVIHJvJIt8C_15rust_c_indirect2OP = internal constant ptr ptrauth (ptr @add, i32 0), align 8 + +%0 = load volatile ptr, ptr @_RNvCscVIHJvJIt8C_15rust_c_indirect2OP, align 8, !nonnull !5, !noundef !5 +%1 = tail call noundef i32 %0(i32 noundef 10, i32 noundef 32) #6 [ "ptrauth"(i32 0, i64 0) ] +``` + +Which shows that: +* function pointer (`@add`) is signed using `ptrauth`, when global variable is + initialized, +* the call is performed indirectly via a signed pointer, +* the `ptrauth` operand bundle enforces authentication at call time. + +Note, when building crates it is necessary to explicitly point Cargo to the +linker it has to use. This can be achieved by using a `config.toml` file (either +local to the project, or global), or by setting a +`CARGO_TARGET_AARCH64_UNKNOWN_LINUX_PAUTHTEST_LINKER` variable. For example: +* `.cargo/config.toml` + +```toml +[target.aarch64-unknown-linux-pauthtest] +linker = "/aarch64-unknown-linux-pauthtest-clang" +``` + +* `export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_PAUTHTEST_LINKER=/aarch64-unknown-linux-pauthtest-clang` + +Without it Cargo falls back to the system C toolchain (cc) and the compilation +fails. + +## Cross-compilation toolchains and C code + +This target supports interoperability with C code. A +pointer-authentication-enabled sysroot, built as described in the toolchain +build section of this document, is required. C code must be compiled with a +compiler configuration that supports pointer authentication. Mixed Rust/C +programs are supported and tested (e.g. quicksort examples). Pointer +authentication semantics must be consistent across Rust and C components. Only +dynamic linking is supported. + +The target can be cross-compiled from any Linux-based host, but execution +requires an AArch64 system that implements Pointer Authentication (PAC). In +practice, this means a CPU conforming to at least the Armv8.3-A architecture, +where the +[FEAT_PAuth](https://developer.arm.com/documentation/109697/2025_06/Feature-descriptions/The-Armv8-3-architecture-extension?lang=en#md448-the-armv83-architecture-extension__feat_FEAT_PAuth) +extension is defined. + +Cross-compilation has been successfully performed on both +`aarch64-unknown-linux-gnu` and `x86_64-unknown-linux-gnu` hosts. + +## Testing + +This target can be tested as normal with `x.py`. +The following categories are supported (all present in tree): +* Assembly tests + * targets-aarch64_unknown_linux_pauthtest.rs +* LLVM IR/codegen tests + * pauth-extern-c.rs + * pauth-extern-c-direct-indirect-call.rs + * pauth-extern-weak-global.rs + * pauth-init-fini.rs + * pauth-attr-special-funcs.rs +* End-to-end execution tests + * Rust-driven quicksort (pauth-quicksort-rust-driver) + * C-driven quicksort (pauth-quicksort-c-driver) +* UI error reporting (the target does not support `+crt-static`) + * crt-static-pauthtest.rs + +All tests from `assembly-llvm`, `codegen-llvm`, `codegen-units`, `coverage`, +`crashes`, `incremental`, `library`, `mir-opt`, `run-make`, `ui` and +`ui-fulldeps` subsets are expected to pass. + +Command to run all passing tests (with tests added by this target explicitly +named for convenience): + +```sh +x.py test --target aarch64-unknown-linux-pauthtest --force-rerun assembly-llvm \ + codegen-llvm codegen-units coverage crashes incremental library mir-opt \ + run-make ui ui-fulldeps \ + tests/assembly-llvm/targets/targets-aarch64_unknown_linux_pauthtest.rs \ + tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs \ + tests/codegen-llvm/pauth/pauth-extern-c.rs \ + tests/codegen-llvm/pauth/pauth-extern-c-direct-indirect-call.rs \ + tests/codegen-llvm/pauth/pauth-extern-weak-global.rs \ + tests/codegen-llvm/pauth/pauth-init-fini.rs \ + tests/run-make/pauth-quicksort-rust-driver \ + tests/run-make/pauth-quicksort-c-driver \ + tests/ui/statics/crt-static-pauthtest.rs +``` + +## Limitations + +Operand bundles should only be attached to indirect function calls. However, +function pointer signing is currently performed in `get_fn_addr`, which causes +the logic to be applied too broadly, including to function values (not just +pointers). As a result, direct calls using signed function values must also +receive operand bundles. Once this is resolved, we should analyze each call and +skip direct calls. +For more information please see the discussion in the [rust-lang issue +tracker](https://github.com/rust-lang/rust/issues/152532). + +The current version only supports C interoperability with pointer authentication +features explicitly mentioned at the beginning of this document. Further work is +needed to support configurable signing schemas (i.e. selection of signing keys, +discriminators, address diversity, and features opt-in/opt-out) as defined by +the LLVM pointer authentication model. + +C++ interoperability is not currently supported. Features such as signing C++ +member function pointers, virtual function pointers, and virtual table pointers +are not expected to work. diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 170f1439ecc9c..4c5cbf7f4942b 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -643,6 +643,7 @@ fn human_readable_target_env(env: Symbol) -> Option<&'static str> { P1 => "WASIp1", P2 => "WASIp2", P3 => "WASIp3", + Pauthtest => "pauthtest", Relibc => "relibc", Sgx => "SGX", Sim => "Simulator", diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs index b7130c0870800..786e780b7418b 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -49,6 +49,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "ignore-aarch64", "ignore-aarch64-pc-windows-msvc", "ignore-aarch64-unknown-linux-gnu", + "ignore-aarch64-unknown-linux-pauthtest", "ignore-aix", "ignore-android", "ignore-apple", @@ -216,6 +217,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-aarch64", "only-aarch64-apple-darwin", "only-aarch64-unknown-linux-gnu", + "only-aarch64-unknown-linux-pauthtest", "only-aarch64-unknown-uefi", "only-apple", "only-arm", diff --git a/tests/assembly-llvm/targets/targets-aarch64_unknown_linux_pauthtest.rs b/tests/assembly-llvm/targets/targets-aarch64_unknown_linux_pauthtest.rs new file mode 100644 index 0000000000000..6dbc4aebf35d9 --- /dev/null +++ b/tests/assembly-llvm/targets/targets-aarch64_unknown_linux_pauthtest.rs @@ -0,0 +1,44 @@ +//@ add-minicore +//@ assembly-output: emit-asm +//@ only-aarch64-unknown-linux-pauthtest +//@ revisions: aarch64_unknown_linux_pauthtest +//@ [aarch64_unknown_linux_pauthtest] compile-flags: --target=aarch64-unknown-linux-pauthtest +//@ [aarch64_unknown_linux_pauthtest] needs-llvm-components: aarch64 + +#![feature(no_core, lang_items)] +#![no_std] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; + +#[no_mangle] +#[inline(never)] +pub extern "C" fn c_func(a: i32) -> i32 { + a +} + +#[no_mangle] +#[inline(never)] +fn call_through(f: extern "C" fn(i32) -> i32, x: i32) -> i32 { + f(x) +} + +#[no_mangle] +#[inline(never)] +pub fn call_c_func(x: i32) -> i32 { + call_through(c_func, x) +} + +// CHECK-LABEL: call_through: +// CHECK: mov [[PTR:x[0-9]+]], x0 +// CHECK: mov w0, w1 +// CHECK: braaz [[PTR]] + +// CHECK-LABEL: call_c_func: +// CHECK: adrp [[GOT_REG:x[0-9]+]], :got:c_func +// CHECK: ldr [[GOT_REG]], [[[GOT_REG]], :got_lo12:c_func] +// CHECK: paciza [[FN_REG:x[0-9]+]] +// CHECK: mov w1, w0 +// CHECK: mov x0, [[FN_REG]] +// CHECK: b call_through diff --git a/tests/assembly-llvm/targets/targets-elf.rs b/tests/assembly-llvm/targets/targets-elf.rs index 7cc7cb037d4ce..3d7fd936baa88 100644 --- a/tests/assembly-llvm/targets/targets-elf.rs +++ b/tests/assembly-llvm/targets/targets-elf.rs @@ -55,6 +55,9 @@ //@ revisions: aarch64_unknown_linux_ohos //@ [aarch64_unknown_linux_ohos] compile-flags: --target aarch64-unknown-linux-ohos //@ [aarch64_unknown_linux_ohos] needs-llvm-components: aarch64 +//@ revisions: aarch64_unknown_linux_pauthtest +//@ [aarch64_unknown_linux_pauthtest] compile-flags: --target aarch64-unknown-linux-pauthtest +//@ [aarch64_unknown_linux_pauthtest] needs-llvm-components: aarch64 //@ revisions: aarch64_unknown_managarm_mlibc //@ [aarch64_unknown_managarm_mlibc] compile-flags: --target aarch64-unknown-managarm-mlibc //@ [aarch64_unknown_managarm_mlibc] needs-llvm-components: aarch64 diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index 47ed9891a1a6c..c9c6b00ab1ae4 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -156,7 +156,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_env = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_env` are: ``, `gnu`, `macabi`, `mlibc`, `msvc`, `musl`, `newlib`, `nto70`, `nto71`, `nto71_iosock`, `nto80`, `ohos`, `p1`, `p2`, `p3`, `relibc`, `sgx`, `sim`, `uclibc`, and `v5` + = note: expected values for `target_env` are: ``, `gnu`, `macabi`, `mlibc`, `msvc`, `musl`, `newlib`, `nto70`, `nto71`, `nto71_iosock`, `nto80`, `ohos`, `p1`, `p2`, `p3`, `pauthtest`, `relibc`, `sgx`, `sim`, `uclibc`, and `v5` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` diff --git a/tests/ui/statics/crt-static-pauthtest.rs b/tests/ui/statics/crt-static-pauthtest.rs new file mode 100644 index 0000000000000..2e2da67cbff13 --- /dev/null +++ b/tests/ui/statics/crt-static-pauthtest.rs @@ -0,0 +1,9 @@ +//@ compile-flags: -C target-feature=+crt-static --target aarch64-unknown-linux-pauthtest +//@ needs-llvm-components: aarch64 +//@ only-aarch64-unknown-linux-pauthtest + + +#![feature(no_core)] +#![no_main] + +//~? ERROR pointer authentication requires dynamic linking. Statically linked libc is incompatible, disable it using `-C target-feature=-crt-static` diff --git a/tests/ui/statics/crt-static-pauthtest.stderr b/tests/ui/statics/crt-static-pauthtest.stderr new file mode 100644 index 0000000000000..00ab7fb8079de --- /dev/null +++ b/tests/ui/statics/crt-static-pauthtest.stderr @@ -0,0 +1,4 @@ +error: pointer authentication requires dynamic linking. Statically linked libc is incompatible, disable it using `-C target-feature=-crt-static` + +error: aborting due to 1 previous error + From 844c6f4a02dae0ac3fcd6337fa6d26b3c96edaef Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 3 Apr 2026 15:05:47 +0000 Subject: [PATCH 08/38] New signature of `get_fn_addr`, teach Rust how to sign fn pointers Allow PAC metadata to be passed to `get_fn_addr` and related API changes. --- compiler/rustc_codegen_gcc/src/common.rs | 13 ++- compiler/rustc_codegen_gcc/src/context.rs | 6 +- compiler/rustc_codegen_llvm/src/base.rs | 3 +- compiler/rustc_codegen_llvm/src/common.rs | 91 +++++++++++++++++-- compiler/rustc_codegen_llvm/src/consts.rs | 21 ++++- compiler/rustc_codegen_llvm/src/context.rs | 37 ++++++-- compiler/rustc_codegen_ssa/src/base.rs | 4 +- compiler/rustc_codegen_ssa/src/common.rs | 6 +- compiler/rustc_codegen_ssa/src/mir/block.rs | 6 +- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 16 +++- .../rustc_codegen_ssa/src/traits/consts.rs | 12 ++- compiler/rustc_codegen_ssa/src/traits/misc.rs | 31 ++++++- compiler/rustc_codegen_ssa/src/traits/mod.rs | 2 +- 13 files changed, 207 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index dd0064d34bc4a..64f3748e4952e 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -2,7 +2,8 @@ use gccjit::{LValue, RValue, ToRValue, Type}; use rustc_abi::Primitive::Pointer; use rustc_abi::{self as abi, HasDataLayout}; use rustc_codegen_ssa::traits::{ - BaseTypeCodegenMethods, ConstCodegenMethods, MiscCodegenMethods, StaticCodegenMethods, + BaseTypeCodegenMethods, ConstCodegenMethods, MiscCodegenMethods, PacMetadata, + StaticCodegenMethods, }; use rustc_middle::mir::Mutability; use rustc_middle::mir::interpret::{GlobalAlloc, PointerArithmetic, Scalar}; @@ -229,7 +230,13 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> { None } - fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, ty: Type<'gcc>) -> RValue<'gcc> { + fn scalar_to_backend_with_pac( + &self, + cv: Scalar, + layout: abi::Scalar, + ty: Type<'gcc>, + _pac: Option, + ) -> RValue<'gcc> { let bitsize = if layout.is_bool() { 1 } else { layout.size(self).bits() }; match cv { Scalar::Int(int) => { @@ -278,7 +285,7 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> { } value } - GlobalAlloc::Function { instance, .. } => self.get_fn_addr(instance), + GlobalAlloc::Function { instance, .. } => self.get_fn_addr(instance, None), GlobalAlloc::VTable(ty, dyn_ty) => { let alloc = self .tcx diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index ea71546ea1c0e..3a6c415b38963 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -5,7 +5,9 @@ use gccjit::{Block, CType, Context, Function, FunctionType, LValue, Location, RV use rustc_abi::{Align, HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx}; use rustc_codegen_ssa::base::wants_msvc_seh; use rustc_codegen_ssa::errors as ssa_errors; -use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeCodegenMethods, MiscCodegenMethods}; +use rustc_codegen_ssa::traits::{ + BackendTypes, BaseTypeCodegenMethods, MiscCodegenMethods, PacMetadata, +}; use rustc_data_structures::base_n::{ALPHANUMERIC_ONLY, ToBaseN}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_middle::mir::interpret::Allocation; @@ -398,7 +400,7 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { get_fn(self, instance) } - fn get_fn_addr(&self, instance: Instance<'tcx>) -> RValue<'gcc> { + fn get_fn_addr(&self, instance: Instance<'tcx>, _pac: Option) -> RValue<'gcc> { let func_name = self.tcx.symbol_name(instance).name; let func = if let Some(variable) = self.get_declared_value(func_name) { diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index a7dec13422f46..b03f9f705b991 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -25,12 +25,13 @@ use rustc_middle::mono::Visibility; use rustc_middle::ty::TyCtxt; use rustc_session::config::{DebugInfo, Offload}; use rustc_span::Symbol; -use rustc_target::spec::SanitizerSet; +use rustc_target::spec::{Env, SanitizerSet}; use super::ModuleLlvm; use crate::attributes; use crate::builder::Builder; use crate::builder::gpu_offload::OffloadGlobals; +use crate::common::pauth_fn_attrs; use crate::context::CodegenCx; use crate::llvm::{self, Value}; diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 5c5e9ed7e082c..4c9af0e56312a 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -4,23 +4,81 @@ use std::borrow::Borrow; use libc::{c_char, c_uint}; use rustc_abi::Primitive::Pointer; -use rustc_abi::{self as abi, HasDataLayout as _}; +use rustc_abi::{self as abi, ExternAbi, HasDataLayout as _}; use rustc_ast::Mutability; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::traits::*; use rustc_data_structures::stable_hash::{StableHash, StableHasher}; use rustc_hashes::Hash128; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_middle::bug; use rustc_middle::mir::interpret::{GlobalAlloc, PointerArithmetic, Scalar}; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{Instance, TyCtxt}; use rustc_session::cstore::DllImport; +use rustc_target::spec::Env; use tracing::debug; -use crate::consts::const_alloc_to_llvm; +use crate::consts::{IsInitOrFini, IsStatic, const_alloc_to_llvm}; pub(crate) use crate::context::CodegenCx; use crate::context::{GenericCx, SCx}; -use crate::llvm::{self, BasicBlock, ConstantInt, FALSE, TRUE, ToLlvmBool, Type, Value}; +use crate::llvm::{ + self, BasicBlock, ConstantInt, FALSE, TRUE, ToLlvmBool, Type, Value, const_ptr_auth, +}; + +#[inline] +pub(crate) fn pauth_fn_attrs() -> &'static [&'static str] { + // FIXME(jchlanda) This is not an exhaustive list of all `ptrauth`-related attributes, but only + // those currently supported. The list is expected to grow as additional functionality is + // implemented, particularly for C++ interoperability. + &[ + "aarch64-jump-table-hardening", + "ptrauth-indirect-gotos", + "ptrauth-calls", + "ptrauth-returns", + "ptrauth-auth-traps", + ] +} + +pub(crate) fn maybe_sign_fn_ptr<'ll, 'tcx>( + cx: &CodegenCx<'ll, '_>, + instance: Instance<'tcx>, + llfn: &'ll llvm::Value, + pac: PacMetadata, +) -> &'ll llvm::Value { + if cx.sess().target.env != Env::Pauthtest { + return llfn; + } + + // Only free functions or methods + let def_id = instance.def_id(); + if !matches!(cx.tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) { + return llfn; + } + // Only C ABI + let abi = cx.tcx.fn_sig(def_id).skip_binder().abi(); + if !matches!(abi, ExternAbi::C { .. } | ExternAbi::System { .. }) { + return llfn; + } + // Ignore LLVM intrinsics + if llvm::get_value_name(llfn).starts_with(b"llvm.") { + return llfn; + } + if Some(def_id) == cx.tcx.lang_items().eh_personality() { + return llfn; + } + + let addr_diversity = match pac.addr_diversity { + AddressDiversity::None => None, + AddressDiversity::Real => Some(llfn), + AddressDiversity::Synthetic(val) => { + let llval = cx.const_u64(val); + let llty = cx.val_ty(llfn); + Some(unsafe { llvm::LLVMConstIntToPtr(llval, llty) }) + } + }; + const_ptr_auth(llfn, pac.key, pac.disc, addr_diversity) +} /* * A note on nomenclature of linking: "extern", "foreign", and "upcall". @@ -268,7 +326,13 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { }) } - fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, llty: &'ll Type) -> &'ll Value { + fn scalar_to_backend_with_pac( + &self, + cv: Scalar, + layout: abi::Scalar, + llty: &'ll Type, + pac: Option, + ) -> &'ll Value { let bitsize = if layout.is_bool() { 1 } else { layout.size(self).bits() }; match cv { Scalar::Int(int) => { @@ -297,8 +361,12 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { self.const_bitcast(llval, llty) }; } else { - let init = - const_alloc_to_llvm(self, alloc.inner(), /*static*/ false); + let init = const_alloc_to_llvm( + self, + alloc.inner(), + IsStatic::No, + IsInitOrFini::No, + ); let alloc = alloc.inner(); let value = match alloc.mutability { Mutability::Mut => self.static_addr_of_mut(init, alloc.align, None), @@ -319,7 +387,7 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { value } } - GlobalAlloc::Function { instance, .. } => self.get_fn_addr(instance), + GlobalAlloc::Function { instance, .. } => self.get_fn_addr(instance, pac), GlobalAlloc::VTable(ty, dyn_ty) => { let alloc = self .tcx @@ -330,7 +398,12 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { }), ))) .unwrap_memory(); - let init = const_alloc_to_llvm(self, alloc.inner(), /*static*/ false); + let init = const_alloc_to_llvm( + self, + alloc.inner(), + IsStatic::No, + IsInitOrFini::No, + ); self.static_addr_of_impl(init, alloc.inner().align, None) } GlobalAlloc::Static(def_id) => { diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index ab3e4b8d9effa..ab82b0208d620 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -1,6 +1,6 @@ use std::ops::Range; -use rustc_abi::{Align, HasDataLayout, Primitive, Scalar, Size, WrappingRange}; +use rustc_abi::{Align, ExternAbi, HasDataLayout, Primitive, Scalar, Size, WrappingRange}; use rustc_codegen_ssa::common; use rustc_codegen_ssa::traits::*; use rustc_hir::LangItem; @@ -17,19 +17,30 @@ use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; use rustc_middle::ty::{self, Instance}; use rustc_middle::{bug, span_bug}; use rustc_span::Symbol; -use rustc_target::spec::Arch; +use rustc_target::spec::{Arch, Env}; use tracing::{debug, instrument, trace}; use crate::common::CodegenCx; use crate::errors::SymbolAlreadyDefined; -use crate::llvm::{self, Type, Value}; +use crate::llvm::{self, Type, Value, const_ptr_auth}; use crate::type_of::LayoutLlvmExt; use crate::{base, debuginfo}; +/// Indicates whether a value originates from a `static`. +pub(crate) enum IsStatic { + Yes, + No, +} +/// Indicates whether a symbol is part of `.init_array` or `.fini_array`. +pub(crate) enum IsInitOrFini { + Yes, + No, +} pub(crate) fn const_alloc_to_llvm<'ll>( cx: &CodegenCx<'ll, '_>, alloc: &Allocation, - is_static: bool, + is_static: IsStatic, + is_init_fini: IsInitOrFini, ) -> &'ll Value { // We expect that callers of const_alloc_to_llvm will instead directly codegen a pointer or // integer for any &ZST where the ZST is a constant (i.e. not a static). We should never be @@ -776,7 +787,7 @@ impl<'ll> StaticCodegenMethods for CodegenCx<'ll, '_> { fn static_addr_of(&self, alloc: ConstAllocation<'_>, kind: Option<&str>) -> &'ll Value { // FIXME: should we cache `const_alloc_to_llvm` to avoid repeating this for the // same `ConstAllocation`? - let cv = const_alloc_to_llvm(self, alloc.inner(), /*static*/ false); + let cv = const_alloc_to_llvm(self, alloc.inner(), IsStatic::No, IsInitOrFini::No); let gv = self.static_addr_of_impl(cv, alloc.inner().align, kind); // static_addr_of_impl returns the bare global variable, which might not be in the default diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 84661f5160b14..75eeaeaa27864 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -871,8 +871,24 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { get_fn(self, instance) } - fn get_fn_addr(&self, instance: Instance<'tcx>) -> &'ll Value { - get_fn(self, instance) + fn get_fn_addr(&self, instance: Instance<'tcx>, pac: Option) -> &'ll Value { + // When pointer authentication metadata is provided, `get_fn_addr` will + // attempt to sign the pointer using LLVM's `ConstPtrAuth` constant + // expression. + // + // FIXME(jchlanda) Currently, all function addresses requested from + // within LLVM codegen are signed. This behavior is too broad, resulting + // in the logic being applied to function values, not just pointers + // (addresses). + // + // See the discussion in the rust-lang issue: + // , and comment in + // builder's `ptrauth_operand_bundle`. + let llfn = get_fn(self, instance); + match pac { + Some(pac) => common::maybe_sign_fn_ptr(self, instance, llfn, pac), + None => llfn, + } } fn eh_personality(&self) -> &'ll Value { @@ -914,13 +930,16 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { let tcx = self.tcx; let llfn = match tcx.lang_items().eh_personality() { - Some(def_id) if name.is_none() => self.get_fn_addr(ty::Instance::expect_resolve( - tcx, - self.typing_env(), - def_id, - ty::List::empty(), - DUMMY_SP, - )), + Some(def_id) if name.is_none() => self.get_fn_addr( + ty::Instance::expect_resolve( + tcx, + self.typing_env(), + def_id, + ty::List::empty(), + DUMMY_SP, + ), + Some(PacMetadata::default()), + ), _ => { let name = name.unwrap_or("rust_eh_personality"); if let Some(llfn) = self.get_declared_value(name) { diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 2a5e421282f4e..4e979df471318 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -493,7 +493,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( return None; } - let main_llfn = cx.get_fn_addr(instance); + let main_llfn = cx.get_fn_addr(instance, Some(PacMetadata::default())); let entry_fn = create_entry_fn::(cx, main_llfn, main_def_id, entry_type); return Some(entry_fn); @@ -554,7 +554,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( cx.tcx().mk_args(&[main_ret_ty.into()]), DUMMY_SP, ); - let start_fn = cx.get_fn_addr(start_instance); + let start_fn = cx.get_fn_addr(start_instance, Some(PacMetadata::default())); let i8_ty = cx.type_i8(); let arg_sigpipe = bx.const_u8(sigpipe); diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index eca013c83cb4a..3a2ff3ab5d2ae 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -117,7 +117,11 @@ pub(crate) fn build_langcall<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let tcx = bx.tcx(); let def_id = tcx.require_lang_item(li, span); let instance = ty::Instance::mono(tcx, def_id); - (bx.fn_abi_of_instance(instance, ty::List::empty()), bx.get_fn_addr(instance), instance) + ( + bx.fn_abi_of_instance(instance, ty::List::empty()), + bx.get_fn_addr(instance, Some(PacMetadata::default())), + instance, + ) } pub(crate) fn shift_mask_val<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 115c50edf4e9f..84d0e6a7bf146 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -686,7 +686,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } _ => ( false, - bx.get_fn_addr(drop_fn), + bx.get_fn_addr(drop_fn, Some(PacMetadata::default())), bx.fn_abi_of_instance(drop_fn, ty::List::empty()), drop_fn, ), @@ -1097,7 +1097,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ) .unwrap(); - (None, Some(bx.get_fn_addr(instance))) + (None, Some(bx.get_fn_addr(instance, Some(PacMetadata::default())))) } _ => (Some(instance), None), } @@ -1409,7 +1409,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } let fn_ptr = match (instance, llfn) { - (Some(instance), None) => bx.get_fn_addr(instance), + (Some(instance), None) => bx.get_fn_addr(instance, Some(PacMetadata::default())), (_, Some(llfn)) => llfn, _ => span_bug!(fn_span, "no instance or llfn for call"), }; diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 5dc4617717c11..49f03fe1376e2 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -434,7 +434,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args, ) .unwrap(); - OperandValue::Immediate(bx.get_fn_addr(instance)) + OperandValue::Immediate( + bx.get_fn_addr( + instance, + Some(PacMetadata::default()), + ), + ) } _ => bug!("{} cannot be reified to a fn ptr", operand.layout.ty), } @@ -448,7 +453,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args, ty::ClosureKind::FnOnce, ); - OperandValue::Immediate(bx.cx().get_fn_addr(instance)) + OperandValue::Immediate( + bx.cx().get_fn_addr( + instance, + Some(PacMetadata::default()), + ), + ) } _ => bug!("{} cannot be cast to a fn ptr", operand.layout.ty), } @@ -668,7 +678,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { def: ty::InstanceKind::Shim(ty::ShimKind::ThreadLocal(def_id)), args: ty::GenericArgs::empty(), }; - let fn_ptr = bx.get_fn_addr(instance); + let fn_ptr = bx.get_fn_addr(instance, Some(PacMetadata::default())); let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty()); let fn_ty = bx.fn_decl_backend_type(fn_abi); let fn_attrs = if bx.tcx().def_kind(instance.def_id()).has_codegen_attrs() { diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs index 22784a8868ab5..20738ba79e9fb 100644 --- a/compiler/rustc_codegen_ssa/src/traits/consts.rs +++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs @@ -2,6 +2,7 @@ use rustc_abi as abi; use rustc_middle::mir::interpret::Scalar; use super::BackendTypes; +use crate::traits::PacMetadata; pub trait ConstCodegenMethods: BackendTypes { // Constant constructors @@ -38,7 +39,16 @@ pub trait ConstCodegenMethods: BackendTypes { fn const_to_opt_uint(&self, v: Self::Value) -> Option; fn const_to_opt_u128(&self, v: Self::Value, sign_ext: bool) -> Option; - fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, llty: Self::Type) -> Self::Value; + fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, llty: Self::Type) -> Self::Value { + self.scalar_to_backend_with_pac(cv, layout, llty, None) + } + fn scalar_to_backend_with_pac( + &self, + cv: Scalar, + layout: abi::Scalar, + llty: Self::Type, + pac: Option, + ) -> Self::Value; fn const_ptr_byte_offset(&self, val: Self::Value, offset: abi::Size) -> Self::Value; } diff --git a/compiler/rustc_codegen_ssa/src/traits/misc.rs b/compiler/rustc_codegen_ssa/src/traits/misc.rs index 92ddc1f347994..ecef986b55855 100644 --- a/compiler/rustc_codegen_ssa/src/traits/misc.rs +++ b/compiler/rustc_codegen_ssa/src/traits/misc.rs @@ -7,6 +7,35 @@ use rustc_span::Symbol; use super::BackendTypes; +/// Strategy for incorporating address-based diversity into PAC computation. +#[derive(Default)] +pub enum AddressDiversity { + /// No address diversity is applied. + #[default] + None, + /// Use the actual memory address for diversification. + Real, + /// Use a fixed synthetic value instead of the real address, + /// i.e. `1` is used for `.init_array` / `.fini_array`. + Synthetic(u64), +} + +/// Metadata used for pointer authentication. +pub struct PacMetadata { + /// The PAC key to use. + pub key: u32, + /// Discriminator value used to diversify the PAC. + pub disc: u64, + /// Controls how address diversity is applied when computing the PAC. + pub addr_diversity: AddressDiversity, +} + +impl Default for PacMetadata { + fn default() -> Self { + PacMetadata { key: 0, disc: 0, addr_diversity: AddressDiversity::default() } + } +} + pub trait MiscCodegenMethods<'tcx>: BackendTypes { fn vtables( &self, @@ -19,7 +48,7 @@ pub trait MiscCodegenMethods<'tcx>: BackendTypes { ) { } fn get_fn(&self, instance: Instance<'tcx>) -> Self::Function; - fn get_fn_addr(&self, instance: Instance<'tcx>) -> Self::Value; + fn get_fn_addr(&self, instance: Instance<'tcx>, pac: Option) -> Self::Value; fn eh_personality(&self) -> Self::Function; fn sess(&self) -> &Session; fn set_frame_pointer_type(&self, llfn: Self::Function); diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs index f46d07ea5008e..ed2dad5a510cd 100644 --- a/compiler/rustc_codegen_ssa/src/traits/mod.rs +++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs @@ -42,7 +42,7 @@ pub use self::coverageinfo::CoverageInfoBuilderMethods; pub use self::debuginfo::{DebugInfoBuilderMethods, DebugInfoCodegenMethods}; pub use self::declare::PreDefineCodegenMethods; pub use self::intrinsic::IntrinsicCallBuilderMethods; -pub use self::misc::MiscCodegenMethods; +pub use self::misc::{AddressDiversity, MiscCodegenMethods, PacMetadata}; pub use self::statics::{StaticBuilderMethods, StaticCodegenMethods}; pub use self::type_::{ ArgAbiBuilderMethods, BaseTypeCodegenMethods, DerivedTypeCodegenMethods, From 8eccfb14d81a7e991128b7e3e8c9baee474b6e54 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 3 Apr 2026 15:08:35 +0000 Subject: [PATCH 09/38] Add pauth attributes and first IR tests The set of supported attributes is: function * "aarch64-jump-table-hardening" * "ptrauth-auth-traps" * "ptrauth-calls" * "ptrauth-indirect-gotos" * "ptrauth-returns" module * "ptrauth-elf-got" * "ptrauth-sign-personality" --- compiler/rustc_codegen_llvm/src/attributes.rs | 9 +- compiler/rustc_codegen_llvm/src/base.rs | 33 ++++++- compiler/rustc_codegen_llvm/src/context.rs | 18 ++++ compiler/rustc_codegen_llvm/src/intrinsic.rs | 12 ++- .../pauth/pauth-attr-special-funcs.rs | 31 ++++++ .../pauth-extern-c-direct-indirect-call.rs | 98 +++++++++++++++++++ tests/codegen-llvm/pauth/pauth-extern-c.rs | 75 ++++++++++++++ 7 files changed, 272 insertions(+), 4 deletions(-) create mode 100644 tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs create mode 100644 tests/codegen-llvm/pauth/pauth-extern-c-direct-indirect-call.rs create mode 100644 tests/codegen-llvm/pauth/pauth-extern-c.rs diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index fe36a9865485d..e283a2a6fdcf9 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -12,9 +12,10 @@ use rustc_session::config::{ }; use rustc_span::sym; use rustc_symbol_mangling::mangle_internal_symbol; -use rustc_target::spec::{Arch, FramePointer, SanitizerSet, StackProbeType, StackProtector}; +use rustc_target::spec::{Arch, Env, FramePointer, SanitizerSet, StackProbeType, StackProtector}; use smallvec::SmallVec; +use crate::common::pauth_fn_attrs; use crate::context::SimpleCx; use crate::errors::{PackedStackBackchainNeedsSoftfloat, SanitizerMemtagRequiresMte}; use crate::llvm::AttributePlace::Function; @@ -643,6 +644,12 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( } } + if sess.target.env == Env::Pauthtest { + for &ptrauth_attr in pauth_fn_attrs() { + to_add.push(llvm::CreateAttrString(cx.llcx, ptrauth_attr)); + } + } + to_add.extend(target_features_attr(cx, tcx, function_features)); attributes::apply_to_llfn(llfn, Function, &to_add); diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index b03f9f705b991..74cb57a859399 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -124,7 +124,14 @@ pub(crate) fn compile_codegen_unit( if let Some(entry) = maybe_create_entry_wrapper::>(&cx, cx.codegen_unit) { - let attrs = attributes::sanitize_attrs(&cx, tcx, SanitizerFnAttrs::default()); + let mut attrs = attributes::sanitize_attrs(&cx, tcx, SanitizerFnAttrs::default()); + // When pointer authentication is enabled, ensure that the ptrauth-* attributes are + // also attached to the entry wrapper. + if cx.sess().target.env == Env::Pauthtest { + for &ptrauth_attr in pauth_fn_attrs() { + attrs.push(llvm::CreateAttrString(cx.llcx, ptrauth_attr)); + } + } attributes::apply_to_llfn(entry, llvm::AttributePlace::Function, &attrs); } @@ -141,6 +148,30 @@ pub(crate) fn compile_codegen_unit( cx.add_objc_module_flags(); } + if cx.sess().target.env == Env::Pauthtest { + // FIXME(jchlanda): In LLVM/Clang, there are also `aarch64-elf-pauthabi-platform` + // and `aarch64-elf-pauthabi-version` module flags. These are emitted into the + // PAuth core info section of the resulting ELF, which the linker uses to enforce + // binary compatibility. + // + // We intentionally do not emit these flags now, since only a subset of features + // included in pauthtest ABI is currently supported. By default, the absence of + // this info is treated as compatible with any binary. + // + // Please note, that this would cause compatibility issues, specifically runtime + // crashes due to authentication failures (while compiling and linking + // successfully) when linking against binaries that support larger set of features + // (for example, signing of C++ member function pointers, virtual function + // pointers, virtual table pointers). + // + // Link to PAuth core info documentation: + // + if cx.sess().opts.unstable_opts.ptrauth_elf_got { + cx.add_ptrauth_elf_got_flag(); + } + cx.add_ptrauth_sign_personality_flag(); + } + // Finalize code coverage by injecting the coverage map. Note, the coverage map will // also be added to the `llvm.compiler.used` variable, created next. if cx.sess().instrument_coverage() { diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 75eeaeaa27864..6f411636bc69b 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -727,6 +727,24 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { } } + pub(crate) fn add_ptrauth_elf_got_flag(&self) { + llvm::add_module_flag_u32( + self.llmod, + llvm::ModuleFlagMergeBehavior::Error, + "ptrauth-elf-got", + 1, + ); + } + + pub(crate) fn add_ptrauth_sign_personality_flag(&self) { + llvm::add_module_flag_u32( + self.llmod, + llvm::ModuleFlagMergeBehavior::Error, + "ptrauth-sign-personality", + 1, + ); + } + // We do our best here to match what Clang does when compiling Objective-C natively. // See Clang's `CGObjCCommonMac::EmitImageInfo`: // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5085 diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 1caa95f369360..170a35f15d1e0 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -28,7 +28,7 @@ use rustc_session::lint::builtin::DEPRECATED_LLVM_INTRINSIC; use rustc_span::{ErrorGuaranteed, Span, Symbol, sym}; use rustc_symbol_mangling::{mangle_internal_symbol, symbol_name_for_instance_in_crate}; use rustc_target::callconv::PassMode; -use rustc_target::spec::Arch; +use rustc_target::spec::{Arch, Env}; use tracing::debug; use crate::abi::FnAbiLlvmExt; @@ -37,6 +37,7 @@ use crate::builder::autodiff::{adjust_activity_to_abi, generate_enzyme_call}; use crate::builder::gpu_offload::{ OffloadKernelDims, gen_call_handling, gen_define_handling, register_offload, }; +use crate::common::pauth_fn_attrs; use crate::context::CodegenCx; use crate::declare::declare_raw_fn; use crate::errors::{ @@ -44,7 +45,7 @@ use crate::errors::{ OffloadWithoutEnable, OffloadWithoutFatLTO, UnknownIntrinsic, }; use crate::intrinsic::ty::typetree::fnc_typetrees; -use crate::llvm::{self, Type, Value}; +use crate::llvm::{self, Attribute, AttributePlace, Type, Value}; use crate::type_of::LayoutLlvmExt; use crate::va_arg::emit_va_arg; @@ -1697,6 +1698,13 @@ fn get_rust_try_fn<'a, 'll, 'tcx>( hir::Safety::Unsafe, )); let rust_try = gen_fn(cx, "__rust_try", rust_fn_sig, codegen); + if cx.sess().target.env == Env::Pauthtest { + let attrs: Vec<&Attribute> = + pauth_fn_attrs().iter().map(|name| llvm::CreateAttrString(cx.llcx, name)).collect(); + let (_ty, rust_try_fn) = rust_try; + crate::attributes::apply_to_llfn(rust_try_fn, AttributePlace::Function, &attrs); + } + cx.rust_try_fn.set(Some(rust_try)); rust_try } diff --git a/tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs b/tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs new file mode 100644 index 0000000000000..0d3d164c51444 --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs @@ -0,0 +1,31 @@ +//@ only-aarch64-unknown-linux-pauthtest +//@ compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 +// Make sure that compiler generated functions (main wrapper and __rust_try) also have ptrauth +// attributes set correctly. Rustc only generates __rust_try at O0, so use that opt level for the +// test. + +//@ needs-llvm-components: aarch64 + +use std::panic; + +// CHECK: define {{.*}} @__rust_try{{.*}} [[ATTR_TRY:#[0-9]+]] +// CHECK: define {{.*}} @main{{.*}} [[ATTR_MAIN:#[0-9]+]] + +// CHECK: attributes [[ATTR_TRY]] = { {{.*}}"aarch64-jump-table-hardening" +// CHECK-DAG: "ptrauth-auth-traps" +// CHECK-DAG: "ptrauth-calls" +// CHECK-DAG: "ptrauth-indirect-gotos" +// CHECK-DAG: "ptrauth-returns" + +// CHECK: attributes [[ATTR_MAIN]] = { {{.*}}"aarch64-jump-table-hardening" +// CHECK-DAG: "ptrauth-auth-traps" +// CHECK-DAG: "ptrauth-calls" +// CHECK-DAG: "ptrauth-indirect-gotos" +// CHECK-DAG: "ptrauth-returns" +fn main() { + let _ = panic::catch_unwind(|| { + panic!("BOOM"); + }); +} + +// CHECK: !{{[0-9]+}} = !{i32 1, !"ptrauth-sign-personality", i32 1} diff --git a/tests/codegen-llvm/pauth/pauth-extern-c-direct-indirect-call.rs b/tests/codegen-llvm/pauth/pauth-extern-c-direct-indirect-call.rs new file mode 100644 index 0000000000000..aec39c2a3034a --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-extern-c-direct-indirect-call.rs @@ -0,0 +1,98 @@ +//@ add-minicore +// ignore-tidy-linelength +//@ only-aarch64-unknown-linux-pauthtest +//@ revisions: O0_PAUTH O3_PAUTH + +//@ [O0_PAUTH] needs-llvm-components: aarch64 +//@ [O0_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 +//@ [O3_PAUTH] needs-llvm-components: aarch64 +//@ [O3_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=3 + +// Make sure that direct extern "C" calls are not handled by pointer authentication operand bundle +// logic. +#![feature(no_core, lang_items)] +#![no_std] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::hint::black_box; +use minicore::*; + +extern "C" { + fn rand() -> bool; + fn add(a: i32, b: i32) -> i32; + fn sub(a: i32, b: i32) -> i32; + + // Corresponds to: void *woof; + static mut woof: *mut c_void; + fn direct_function_taking_void_arg(data: *mut c_void); + fn direct_no_arg(); + fn direct_function_taking_fp_arg(func: unsafe extern "C" fn()); +} + +type CFnPtr = unsafe extern "C" fn(i32, i32) -> i32; + +// CHECK-LABE: test_indirect_call +#[inline(never)] +fn test_indirect_call() { + let fp_add: CFnPtr = black_box(add); + let fp_sub: CFnPtr = black_box(sub); + + unsafe { + let a = black_box(fp_add); + let b = black_box(fp_sub); + + // O0_PAUTH: call i32 %{{.*}}(i32 7, i32 4) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + // O3_PAUTH: call noundef i32 %{{.*}}(i32 noundef 7, i32 noundef 4) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + let _id1 = a(7, 4); + + // O0_PAUTH: call i32 %{{.*}}(i32 10, i32 6) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + // O3_PAUTH: call noundef i32 %{{.*}}(i32 noundef 10, i32 noundef 6) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + let _id2 = b(10, 6); + } + + // Also test calling via conditional pointer + unsafe { + // O0_PAUTH: call {{.*}}i1 ptrauth (ptr @rand, i32 0)({{.*}}) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + // O3_PAUTH: call {{.*}}i1 @rand() # + let use_add = rand(); + // O0_PAUTH: store ptr ptrauth (ptr @sub, i32 0), ptr %[[FP_O0:[a-zA-Z0-9_.]+]] + // O0_PAUTH: store ptr ptrauth (ptr @add, i32 0), ptr %[[FP_O0]]{{.*}} + // O0_PAUTH: %[[LOAD_FP_O0:[a-zA-Z0-9_.]+]] = load ptr, ptr %[[FP_O0]]{{.*}} + // O3_PAUTH: %[[FP_O3:.*]] = select i1 %{{.*}}, ptr ptrauth (ptr @add, i32 0), ptr ptrauth (ptr @sub, i32 0) + let fp: CFnPtr = if use_add { add } else { sub }; + // O0_PAUTH: call i32 %[[LOAD_FP_O0]](i32 1, i32 2) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + // O3_PAUTH: call {{.*}}i32 %[[FP_O3]](i32 noundef 1, i32 noundef 2) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + let _id3 = fp(1, 2); + } + + unsafe { + direct_function_taking_fp_arg(direct_no_arg); + } +} + +// CHECK-LABEL: test_direct_call +#[inline(never)] +fn test_direct_call() { + unsafe { + // O0_PAUTH: call {{.*}}i32 ptrauth (ptr @add, i32 0)({{.*}}) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + // O3_PAUTH: call {{.*}}i32 @add(i32 {{.*}}2, i32 {{.*}}3) # + let _d1 = add(2, 3); + // O0_PAUTH: call {{.*}}i32 ptrauth (ptr @sub, i32 0)({{.*}}) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + // O3_PAUTH: call {{.*}}i32 @sub(i32 {{.*}}5, i32 {{.*}}1) # + let _d2 = sub(5, 1); + + // O0_PAUTH: call {{.*}}void ptrauth (ptr @direct_function_taking_void_arg, i32 0)({{.*}}) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + // O3_PAUTH: {{(tail )?}}call void @direct_function_taking_void_arg(ptr noundef %{{.*}}) # + direct_function_taking_void_arg(woof); + } +} + +pub fn entry() { + test_indirect_call(); + test_direct_call(); +} + +// O0_PAUTH: !{{[0-9]+}} = !{i32 1, !"ptrauth-sign-personality", i32 1} +// O3_PAUTH: !{{[0-9]+}} = !{i32 1, !"ptrauth-sign-personality", i32 1} diff --git a/tests/codegen-llvm/pauth/pauth-extern-c.rs b/tests/codegen-llvm/pauth/pauth-extern-c.rs new file mode 100644 index 0000000000000..2c5612cff4be4 --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-extern-c.rs @@ -0,0 +1,75 @@ +// ignore-tidy-linelength +//@ only-aarch64-unknown-linux-pauthtest +//@ add-minicore + +//@ revisions: O0_PAUTH O3_PAUTH O0_PAUTH-ELF-GOT O3_PAUTH-ELF-GOT O0_NO_PAUTH O3_NO_PAUTH + +//@ [O0_PAUTH] needs-llvm-components: aarch64 +//@ [O0_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 +//@ [O3_PAUTH] needs-llvm-components: aarch64 +//@ [O3_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=3 +//@ [O0_PAUTH-ELF-GOT] needs-llvm-components: aarch64 +//@ [O0_PAUTH-ELF-GOT] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 -Z ptrauth-elf-got +//@ [O3_PAUTH-ELF-GOT] needs-llvm-components: aarch64 +//@ [O3_PAUTH-ELF-GOT] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=3 -Z ptrauth-elf-got +//@ [O0_NO_PAUTH] needs-llvm-components: aarch64 +//@ [O0_NO_PAUTH] compile-flags: --target=aarch64-unknown-linux-gnu -C opt-level=0 +//@ [O3_NO_PAUTH] needs-llvm-components: aarch64 +//@ [O3_NO_PAUTH] compile-flags: --target=aarch64-unknown-linux-gnu -C opt-level=3 + +#![crate_type = "lib"] +#![no_std] +#![no_core] +#![feature(no_core)] + +extern crate minicore; + +type FnPtr = unsafe extern "C" fn(i32, i32) -> i32; +// O0_NO_PAUTH-NOT: "ptrauth"(i32 +// O3_NO_PAUTH-NOT: "ptrauth"(i32 + +// O0_PAUTH: define {{.*}}test_entry +// O3_PAUTH: define {{.*}}test_entry +#[no_mangle] +pub unsafe extern "C" fn test_entry(x: usize) { + // O0_PAUTH: call{{.*}}_RNvCshUtaFcP1mZ5_14pauth_extern_c7call_it(ptr ptrauth (ptr @external_c_callee, i32 0), i32 5, i32 7) + // O3_PAUTH: call{{.*}}_RNvCshUtaFcP1mZ5_14pauth_extern_c7call_it(ptr{{.*}}ptrauth (ptr @external_c_callee, i32 0), i32{{.*}}5, i32{{.*}}7) + let _ = call_it(external_c_callee, 5, 7); +} + +// O0_PAUTH: define {{.*}}pauth_extern_c7call_it{{.*}} #[[ATTR_O0_1:[0-9]+]] +// O3_PAUTH: define {{.*}}pauth_extern_c7call_it{{.*}} #[[ATTR_O3_1:[0-9]+]] +#[inline(never)] +pub fn call_it(fn_ptr: FnPtr, arg_1: i32, arg_2: i32) -> i32 { + // O0_PAUTH: call i32 %fn_ptr(i32 %arg_1, i32 %arg_2){{.*}}[ "ptrauth"(i32 0, i64 0) ] + // O3_PAUTH: call{{.*}}i32 %fn_ptr(i32{{.*}}%arg_1, i32{{.*}}%arg_2){{.*}}[ "ptrauth"(i32 0, i64 0) ] + unsafe { fn_ptr(arg_1, arg_2) } +} + +extern "C" { + fn external_c_callee(a: i32, b: i32) -> i32; +} + +// O0_PAUTH-CHECK: attributes #[[ATTR_O0_1]] = { {{.*}}"aarch64-jump-table-hardening" +// O0_PAUTH-CHECK-SAME: "ptrauth-auth-traps" +// O0_PAUTH-CHECK-SAME: "ptrauth-calls" +// O0_PAUTH-CHECK-SAME: "ptrauth-indirect-gotos" +// O0_PAUTH-CHECK-SAME: "ptrauth-returns" + +// O3_PAUTH-CHECK: attributes #[[ATTR_O3_1]] = { {{.*}}"aarch64-jump-table-hardening" +// O3_PAUTH-CHECK-SAME: "ptrauth-auth-traps" +// O3_PAUTH-CHECK-SAME: "ptrauth-calls" +// O3_PAUTH-CHECK-SAME: "ptrauth-indirect-gotos" +// O3_PAUTH-CHECK-SAME: "ptrauth-returns" + +// O0_PAUTH-ELF-GOT: !{{[0-9]+}} = !{i32 1, !"ptrauth-elf-got", i32 1} +// O0_PAUTH-NOT: !{{[0-9]+}} = !{i32 1, !"ptrauth-elf-got", i32 1} +// O0_PAUTH: !{{[0-9]+}} = !{i32 1, !"ptrauth-sign-personality", i32 1} +// O3_PAUTH-ELF-GOT: !{{[0-9]+}} = !{i32 1, !"ptrauth-elf-got", i32 1} +// O3_PAUTH-NOT: !{{[0-9]+}} = !{i32 1, !"ptrauth-elf-got", i32 1} +// O3_PAUTH: !{{[0-9]+}} = !{i32 1, !"ptrauth-sign-personality", i32 1} + +// O0_NO_PAUTH-NOT: !{{[0-9]+}} = !{i32 1, !"ptrauth-elf-got", i32 1} +// O0_NO_PAUTH-NOT: !{{[0-9]+}} = !{i32 1, !"ptrauth-sign-personality", i32 1} +// O3_NO_PAUTH-NOT: !{{[0-9]+}} = !{i32 1, !"ptrauth-elf-got", i32 1} +// O3_NO_PAUTH-NOT: !{{[0-9]+}} = !{i32 1, !"ptrauth-sign-personality", i32 1} From 157212bdcab0cb7a5e39a5773d3a55e43022631e Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 3 Apr 2026 15:09:53 +0000 Subject: [PATCH 10/38] Add support for init/fini signing and correctly sign extern weak globals Also add flag for ELF-GOT signing. --- compiler/rustc_codegen_llvm/src/consts.rs | 62 +++++++++++++++++-- compiler/rustc_session/src/options.rs | 1 + tests/auxiliary/minicore.rs | 20 ++++++ .../pauth/pauth-extern-weak-global.rs | 38 ++++++++++++ tests/codegen-llvm/pauth/pauth-init-fini.rs | 34 ++++++++++ 5 files changed, 149 insertions(+), 6 deletions(-) create mode 100644 tests/codegen-llvm/pauth/pauth-extern-weak-global.rs create mode 100644 tests/codegen-llvm/pauth/pauth-init-fini.rs diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index ab82b0208d620..7ec338a263376 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -49,7 +49,7 @@ pub(crate) fn const_alloc_to_llvm<'ll>( // // Statics have a guaranteed meaningful address so it's less clear that we want to do // something like this; it's also harder. - if !is_static { + if matches!(is_static, IsStatic::No) { assert!(alloc.len() != 0); } let mut llvals = Vec::with_capacity(alloc.provenance().ptrs().len() + 1); @@ -120,14 +120,29 @@ pub(crate) fn const_alloc_to_llvm<'ll>( as u64; let address_space = cx.tcx.global_alloc(prov.alloc_id()).address_space(cx); - - llvals.push(cx.scalar_to_backend( + // Under pointer authentication, function pointers stored in init/fini arrays need special + // handling. + let pac_metadata = Some( + if cx.sess().target.env == Env::Pauthtest && matches!(is_init_fini, IsInitOrFini::Yes) { + PacMetadata { + // Must correspond to ptrauth_key_init_fini_pointer from `ptrauth.h`. + key: 0, + // ptrauth_string_discriminator("init_fini") + disc: 0xd9d4, + addr_diversity: AddressDiversity::Synthetic(1), + } + } else { + PacMetadata::default() + }, + ); + llvals.push(cx.scalar_to_backend_with_pac( InterpScalar::from_pointer(Pointer::new(prov, Size::from_bytes(ptr_offset)), &cx.tcx), Scalar::Initialized { value: Primitive::Pointer(address_space), valid_range: WrappingRange::full(pointer_size), }, cx.type_ptr_ext(address_space), + pac_metadata, )); next_offset = offset + pointer_size_bytes; } @@ -152,7 +167,19 @@ fn codegen_static_initializer<'ll, 'tcx>( def_id: DefId, ) -> Result<(&'ll Value, ConstAllocation<'tcx>), ErrorHandled> { let alloc = cx.tcx.eval_static_initializer(def_id)?; - Ok((const_alloc_to_llvm(cx, alloc.inner(), /*static*/ true), alloc)) + let attrs = cx.tcx.codegen_fn_attrs(def_id); + let is_in_init_fini: IsInitOrFini = attrs + .link_section + .map(|link_section| { + let s = link_section.as_str(); + if s.starts_with(".init_array") || s.starts_with(".fini_array") { + IsInitOrFini::Yes + } else { + IsInitOrFini::No + } + }) + .unwrap_or(IsInitOrFini::No); + Ok((const_alloc_to_llvm(cx, alloc.inner(), IsStatic::Yes, is_in_init_fini), alloc)) } fn set_global_alignment<'ll>(cx: &CodegenCx<'ll, '_>, gv: &'ll Value, mut align: Align) { @@ -175,6 +202,7 @@ fn check_and_apply_linkage<'ll, 'tcx>( if let Some(linkage) = attrs.import_linkage { debug!("get_static: sym={} linkage={:?}", sym, linkage); + let mut should_sign = false; // Declare a symbol `foo`. If `foo` is an extern_weak symbol, we declare // an extern_weak function, otherwise a global with the desired linkage. let g1 = if matches!(attrs.import_linkage, Some(Linkage::ExternalWeak)) { @@ -187,8 +215,13 @@ fn check_and_apply_linkage<'ll, 'tcx>( && let ty::FnPtr(sig, header) = args.type_at(0).kind() { let fn_sig = sig.with(*header); - let fn_abi = cx.fn_abi_of_fn_ptr(fn_sig, ty::List::empty()); + // Decide if the initializer needs to be signed + if cx.sess().target.env == Env::Pauthtest + && matches!(fn_sig.abi(), ExternAbi::C { .. } | ExternAbi::System { .. }) + { + should_sign = true; + } cx.declare_fn(sym, &fn_abi, None) } else { cx.declare_global(sym, cx.type_i8()) @@ -218,7 +251,24 @@ fn check_and_apply_linkage<'ll, 'tcx>( }); llvm::set_linkage(g2, llvm::Linkage::InternalLinkage); llvm::set_unnamed_address(g2, llvm::UnnamedAddr::Global); - llvm::set_initializer(g2, g1); + + // Sign the function pointer that is used to initialize the global + let initializer = if should_sign { + let key: u32 = 0; + let discriminator: u64 = 0; + + const_ptr_auth( + cx.const_bitcast(g1, llty), + key, + discriminator, + None, /* address_diversity */ + ) + } else { + g1 + }; + + llvm::set_initializer(g2, initializer); + g2 } else if cx.tcx.sess.target.arch == Arch::X86 && common::is_mingw_gnu_toolchain(&cx.tcx.sess.target) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index efc8a70f4feb2..7e878cc220a0f 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2650,6 +2650,7 @@ options! { "use the given `.prof` file for sampled profile-guided optimization (also known as AutoFDO)"), profiler_runtime: String = (String::from("profiler_builtins"), parse_string, [TRACKED], "name of the profiler runtime crate to automatically inject (default: `profiler_builtins`)"), + ptrauth_elf_got: bool = (false, parse_bool, [TRACKED], "enable signing of ELF GOT entries"), query_dep_graph: bool = (false, parse_bool, [UNTRACKED], "enable queries of the dependency graph for regression testing (default: no)"), randomize_layout: bool = (false, parse_bool, [TRACKED], diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index c949d8861975b..dcf88e7e9e286 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -299,6 +299,16 @@ impl_marker_trait!( impl Sync for () {} impl Sync for [T; N] {} +// Function pointers are treated as `Sync` to match real `core` behavior. +// +// Minicore provides only the minimal set of impls required by tests. Rather +// than exhaustively covering all possible function pointer signatures, +// additional impls should be added as needed. +impl Sync for fn() -> R {} +impl Sync for extern "C" fn() -> R {} +impl Sync for unsafe extern "C" fn() -> R {} +impl Sync for extern "C" fn(A) -> R {} +impl Sync for unsafe extern "C" fn(A) -> R {} #[lang = "drop_glue"] fn drop_glue(_: &mut T) {} @@ -367,6 +377,16 @@ pub mod ptr { } } +pub mod hint { + #[inline] + pub fn black_box(dummy: T) -> T { + #[rustc_intrinsic] + fn black_box(dummy: T) -> T; + + unsafe { black_box(dummy) } + } +} + #[lang = "c_void"] #[repr(u8)] pub enum c_void { diff --git a/tests/codegen-llvm/pauth/pauth-extern-weak-global.rs b/tests/codegen-llvm/pauth/pauth-extern-weak-global.rs new file mode 100644 index 0000000000000..d39bfb0118a4b --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-extern-weak-global.rs @@ -0,0 +1,38 @@ +// ignore-tidy-linelength +//@ only-aarch64-unknown-linux-pauthtest +//@ revisions: O0_PAUTH O3_PAUTH O0_NO_PAUTH O3_NO_PAUTH +//@ add-minicore + +//@ [O0_PAUTH] needs-llvm-components: aarch64 +//@ [O0_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 +//@ [O3_PAUTH] needs-llvm-components: aarch64 +//@ [O3_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=3 +//@ [O0_NO_PAUTH] needs-llvm-components: aarch64 +//@ [O0_NO_PAUTH] compile-flags: --target=aarch64-unknown-linux-gnu -C opt-level=0 +//@ [O3_NO_PAUTH] needs-llvm-components: aarch64 +//@ [O3_NO_PAUTH] compile-flags: --target=aarch64-unknown-linux-gnu -C opt-level=3 + +#![crate_type = "lib"] +#![no_std] +#![no_core] +#![feature(no_core)] +#![feature(linkage)] + +extern crate minicore; +use minicore::*; + +// O0_PAUTH: @{{[0-9A-Za-z_]+}}FUNCTION_PTR_DECL = constant ptr ptrauth (ptr @extern_weak_fn, i32 0) +// O0_PAUTH: declare i64 @extern_weak_fn({{.*}}) +// O3_PAUTH: @{{[0-9A-Za-z_]+}}FUNCTION_PTR_DECL = constant ptr ptrauth (ptr @extern_weak_fn, i32 0) +// O3_PAUTH: declare {{.*}} i64 @extern_weak_fn({{.*}}) +// +// O0_NO_PAUTH-NOT: ptr ptrauth +// O3_NO_PAUTH-NOT: ptr ptrauth +extern "C" { + #[link_name = "extern_weak_fn"] + #[linkage = "extern_weak"] + fn extern_weak_fn() -> i64; +} + +#[used] +static FUNCTION_PTR_DECL: unsafe extern "C" fn() -> i64 = extern_weak_fn; diff --git a/tests/codegen-llvm/pauth/pauth-init-fini.rs b/tests/codegen-llvm/pauth/pauth-init-fini.rs new file mode 100644 index 0000000000000..0f089b22fd73b --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-init-fini.rs @@ -0,0 +1,34 @@ +//@ add-minicore +// ignore-tidy-linelength +//@ only-aarch64-unknown-linux-pauthtest +//@ revisions: O0_PAUTH O3_PAUTH + +//@ [O0_PAUTH] needs-llvm-components: aarch64 +//@ [O0_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 +//@ [O3_PAUTH] needs-llvm-components: aarch64 +//@ [O3_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=3 + +// Make sure that init/fini metadata uses correct discriminator: 0xd9d4/55764 + +#![feature(no_core, lang_items)] +#![no_std] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::*; + +// O0_PAUTH: @{{[0-9A-Za-z_]+}}GLOBAL_INIT = constant ptr ptrauth (ptr @{{[0-9A-Za-z_]+}}init_fn, i32 0, i64 55764, ptr inttoptr (i64 1 to ptr)), section ".init_array.90" +// O3_PAUTH: @{{[0-9A-Za-z_]+}}GLOBAL_INIT = constant ptr ptrauth (ptr @{{[0-9A-Za-z_]+}}init_fn, i32 0, i64 55764, ptr inttoptr (i64 1 to ptr)), section ".init_array.90" +#[used] +#[link_section = ".init_array.90"] +static GLOBAL_INIT: extern "C" fn() = init_fn; + +// O0_PAUTH: @{{[0-9A-Za-z_]+}}GLOBAL_FINI = constant ptr ptrauth (ptr @{{[0-9A-Za-z_]+}}fini_fn, i32 0, i64 55764, ptr inttoptr (i64 1 to ptr)), section ".fini_array.90" +// O3_PAUTH: @{{[0-9A-Za-z_]+}}GLOBAL_FINI = constant ptr ptrauth (ptr @{{[0-9A-Za-z_]+}}fini_fn, i32 0, i64 55764, ptr inttoptr (i64 1 to ptr)), section ".fini_array.90" +#[used] +#[link_section = ".fini_array.90"] +static GLOBAL_FINI: extern "C" fn(i32) = fini_fn; + +extern "C" fn init_fn() {} +extern "C" fn fini_fn(_: i32) {} From 7669c4498188ee5dde4921a28d77d0dcf88ede23 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 3 Apr 2026 15:11:58 +0000 Subject: [PATCH 11/38] Teach bootstrap how to build rust_test_helpers for pauthtest Also: * update tests to force dynamic library when targetting pauthtest * various test fixes * introduce end-to-end tests for pauthtest (in run-make) --- src/bootstrap/src/core/build_steps/test.rs | 75 +++++++++++++------ .../src/core/config/target_selection.rs | 4 + src/bootstrap/src/core/sanity.rs | 51 ++++++++++++- .../asm/aarch64-outline-atomics.rs | 3 + tests/codegen-llvm/box-uninit-bytes.rs | 2 +- tests/codegen-llvm/cffi/c-variadic.rs | 4 + .../inline-always-works-always.rs | 2 + tests/codegen-llvm/issues/issue-73258.rs | 8 +- tests/incremental/auxiliary/issue-54059.rs | 3 +- .../c-link-to-rust-va-list-fn/rmake.rs | 2 + .../run-make/pauth-quicksort-c-driver/main.c | 45 +++++++++++ .../pauth-quicksort-c-driver/quicksort.rs | 66 ++++++++++++++++ .../pauth-quicksort-c-driver/rmake.rs | 42 +++++++++++ .../pauth-quicksort-rust-driver/main.rs | 43 +++++++++++ .../pauth-quicksort-rust-driver/quicksort.c | 50 +++++++++++++ .../pauth-quicksort-rust-driver/rmake.rs | 34 +++++++++ tests/run-make/rustdoc-test-builder/rmake.rs | 1 + tests/ui/abi/abi-sysv64-arg-passing.rs | 3 +- tests/ui/abi/anon-extern-mod.rs | 3 +- tests/ui/abi/c-stack-as-value.rs | 3 +- tests/ui/abi/cabi-int-widening.rs | 3 +- .../anon-extern-mod-cross-crate-1.rs | 3 +- .../auxiliary/extern-crosscrate-source.rs | 3 +- tests/ui/abi/extern/extern-call-deep.rs | 3 +- tests/ui/abi/extern/extern-call-deep2.rs | 3 +- tests/ui/abi/extern/extern-call-indirect.rs | 3 +- tests/ui/abi/extern/extern-call-scrub.rs | 3 +- tests/ui/abi/extern/extern-pass-FiveU16s.rs | 3 +- tests/ui/abi/extern/extern-pass-TwoU16s.rs | 3 +- tests/ui/abi/extern/extern-pass-TwoU32s.rs | 3 +- tests/ui/abi/extern/extern-pass-TwoU64s.rs | 3 +- tests/ui/abi/extern/extern-pass-TwoU8s.rs | 3 +- tests/ui/abi/extern/extern-pass-char.rs | 3 +- tests/ui/abi/extern/extern-pass-double.rs | 3 +- tests/ui/abi/extern/extern-pass-empty.rs | 3 +- tests/ui/abi/extern/extern-pass-u32.rs | 3 +- tests/ui/abi/extern/extern-pass-u64.rs | 3 +- tests/ui/abi/extern/extern-return-FiveU16s.rs | 3 +- tests/ui/abi/extern/extern-return-TwoU16s.rs | 3 +- tests/ui/abi/extern/extern-return-TwoU32s.rs | 3 +- tests/ui/abi/extern/extern-return-TwoU64s.rs | 3 +- tests/ui/abi/extern/extern-return-TwoU8s.rs | 3 +- tests/ui/abi/foreign/auxiliary/foreign_lib.rs | 3 +- tests/ui/abi/foreign/foreign-fn-with-byval.rs | 3 +- tests/ui/abi/issue-28676.rs | 3 +- .../issues/issue-62350-sysv-neg-reg-counts.rs | 3 +- ...sue-97463-broken-abi-leaked-uninit-data.rs | 3 +- .../ui/abi/mir/mir_codegen_calls_variadic.rs | 3 +- tests/ui/abi/numbers-arithmetic/i128-ffi.rs | 3 +- tests/ui/abi/segfault-no-out-of-stack.rs | 3 +- tests/ui/abi/stack-probes.rs | 3 +- tests/ui/abi/statics/static-mut-foreign.rs | 3 +- tests/ui/abi/struct-enums/struct-return.rs | 3 +- tests/ui/abi/union/union-c-interop.rs | 3 +- tests/ui/abi/variadic-ffi.rs | 3 +- .../target-has-reliable-nightly-float.rs | 2 +- tests/ui/link-native-libs/lib-defaults.rs | 3 +- tests/ui/linking/auxiliary/aux-25185-1.rs | 3 +- tests/ui/macros/macros-in-extern.rs | 3 +- tests/ui/proc-macro/macros-in-extern.rs | 3 +- tests/ui/process/nofile-limit.rs | 3 + 61 files changed, 493 insertions(+), 73 deletions(-) create mode 100644 tests/run-make/pauth-quicksort-c-driver/main.c create mode 100644 tests/run-make/pauth-quicksort-c-driver/quicksort.rs create mode 100644 tests/run-make/pauth-quicksort-c-driver/rmake.rs create mode 100644 tests/run-make/pauth-quicksort-rust-driver/main.rs create mode 100644 tests/run-make/pauth-quicksort-rust-driver/quicksort.c create mode 100644 tests/run-make/pauth-quicksort-rust-driver/rmake.rs diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index f7f708902c095..a2d05f8347cbe 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -10,6 +10,7 @@ use std::collections::HashSet; use std::env::split_paths; use std::ffi::{OsStr, OsString}; use std::path::{Path, PathBuf}; +use std::process::Command; use std::{env, fs, iter}; use build_helper::exit; @@ -2474,7 +2475,13 @@ Please disable assertions with `rust.debug-assertions = false`. "-Lnative={}", builder.test_helpers_out(test_compiler.host).display() )); - targetflags.push(format!("-Lnative={}", builder.test_helpers_out(target).display())); + let target_helpers = builder.test_helpers_out(target); + targetflags.push(format!("-Lnative={}", target_helpers.display())); + if target.is_pauthtest() { + // For the pauthtest target, embed an rpath to the directory containing the helper + // dynamic library. + targetflags.push(format!("-Clink-arg=-Wl,-rpath,{}", target_helpers.display())); + } } for flag in hostflags { @@ -4120,32 +4127,54 @@ impl Step for TestHelpers { }; let dst = builder.test_helpers_out(target); let src = builder.src.join("tests/auxiliary/rust_test_helpers.c"); - if up_to_date(&src, &dst.join("librust_test_helpers.a")) { - return; - } - let _guard = builder.msg_unstaged(Kind::Build, "test helpers", target); t!(fs::create_dir_all(&dst)); - let mut cfg = cc::Build::new(); - // We may have found various cross-compilers a little differently due to our - // extra configuration, so inform cc of these compilers. Note, though, that - // on MSVC we still need cc's detection of env vars (ugh). - if !target.is_msvc() { - if let Some(ar) = builder.ar(target) { - cfg.archiver(ar); + if !up_to_date(&src, &dst.join("librust_test_helpers.a")) { + let mut cfg = cc::Build::new(); + + // We may have found various cross-compilers a little differently due to our + // extra configuration, so inform cc of these compilers. Note, though, that + // on MSVC we still need cc's detection of env vars (ugh). + if !target.is_msvc() { + if let Some(ar) = builder.ar(target) { + cfg.archiver(ar); + } + cfg.compiler(builder.cc(target)); + } + cfg.cargo_metadata(false) + .out_dir(&dst) + .target(&target.triple) + .host(&builder.config.host_target.triple) + .opt_level(0) + .warnings(false) + .debug(false) + .file(builder.src.join("tests/auxiliary/rust_test_helpers.c")) + .compile("rust_test_helpers"); + } + if target.is_pauthtest() { + let so = dst.join("librust_test_helpers.so"); + if up_to_date(&src, &so) { + return; } - cfg.compiler(builder.cc(target)); - } - cfg.cargo_metadata(false) - .out_dir(&dst) - .target(&target.triple) - .host(&builder.config.host_target.triple) - .opt_level(0) - .warnings(false) - .debug(false) - .file(builder.src.join("tests/auxiliary/rust_test_helpers.c")) - .compile("rust_test_helpers"); + + let status = Command::new(builder.cc(target)) + .arg("-target") + .arg(target.triple) + .arg("-march=armv8.3-a+pauth") + .arg("-fPIC") + .arg("-shared") + .arg("-O0") // Use O0 to match what static library is compiled at. + .arg("-o") + .arg(&so) + .arg(&src) + .status() + .unwrap_or_else(|_| panic!("Failed to run clang for {} toolchain", target.triple)); + + if !status.success() { + panic!("Linking of librust_test_helpers.so failed (target: {})", target.triple); + } + } } } diff --git a/src/bootstrap/src/core/config/target_selection.rs b/src/bootstrap/src/core/config/target_selection.rs index 8457607b897dd..5f87e81e2ccb7 100644 --- a/src/bootstrap/src/core/config/target_selection.rs +++ b/src/bootstrap/src/core/config/target_selection.rs @@ -78,6 +78,10 @@ impl TargetSelection { self.contains("msvc") } + pub fn is_pauthtest(&self) -> bool { + self.triple == "aarch64-unknown-linux-pauthtest" + } + pub fn is_windows(&self) -> bool { self.contains("windows") } diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 8e19b45046ccc..6ebfa4da4906a 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -21,7 +21,7 @@ use crate::builder::Kind; use crate::core::build_steps::tool; use crate::core::config::{CompilerBuiltins, Target}; use crate::utils::exec::command; -use crate::{Build, Subcommand}; +use crate::{Build, Subcommand, t}; pub struct Finder { cache: HashMap>, @@ -38,7 +38,7 @@ pub struct Finder { const STAGE0_MISSING_TARGETS: &[&str] = &[ // just a dummy comment so the list doesn't get onelined "powerpc64-unknown-linux-gnuelfv2", - "aarch64-unknown-linux-pauthtest", // Stage 0 compiler is not guaranteed to see pauthtest yet. + "aarch64-unknown-linux-pauthtest", // Stage 0 compiler is not guaranteed to see the target yet. ]; /// Minimum version threshold for libstdc++ required when using prebuilt LLVM @@ -413,6 +413,53 @@ $ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake { cmd_finder.must_have("wasm-component-ld"); } + + // aarch64-unknown-linux-pauthtest must use clang + if !skip_tools_checks && target.is_pauthtest() { + let cc_tool = build.cc_tool(*target); + let linker_path = build + .linker(*target) + .unwrap_or_else(|| panic!("{} requires an explicit clang linker", target.triple)); + + if !cc_tool.is_like_clang() { + panic!( + "Clang is required to build C code for {} target, got:\n\ + cc tool: `{}`,\n\ + linker: `{}`\n", + target.triple, + cc_tool.path().display(), + linker_path.display(), + ); + } + let cc_canon = t!(fs::canonicalize(cc_tool.path())); + let linker_canon = t!(fs::canonicalize(&linker_path)); + if cc_canon != linker_canon { + panic!( + "CC and Linker are expected to be the same for {} target, got:\n\ + CC: `{}`,\n\ + Linker: `{}`\n", + target.triple, + cc_canon.display(), + linker_canon.display(), + ); + } + + let output = + command(cc_tool.path()).arg("-dumpversion").run_capture_stdout(&build).stdout(); + let version_str = output.trim(); + let mut parts = version_str.split('.').map(|s| s.parse::().unwrap_or(0)); + let major = parts.next().unwrap_or(0); + let minor = parts.next().unwrap_or(0); + let patch = parts.next().unwrap_or(0); + if (major, minor, patch) < (22, 1, 0) { + panic!( + "clang version too old: {} ({} target trequires >= 22.1.0), path: {}", + target.triple, + version_str, + cc_tool.path().display() + ); + } + } } if let Some(ref s) = build.config.ccache { diff --git a/tests/assembly-llvm/asm/aarch64-outline-atomics.rs b/tests/assembly-llvm/asm/aarch64-outline-atomics.rs index 1177c1e68ed56..690db72b1322b 100644 --- a/tests/assembly-llvm/asm/aarch64-outline-atomics.rs +++ b/tests/assembly-llvm/asm/aarch64-outline-atomics.rs @@ -2,6 +2,9 @@ //@ compile-flags: -Copt-level=3 //@ only-aarch64 //@ only-linux +// aarch64-unknown-linux-pauthtest requires armv8.3-a, which includes Large System Extensions, +// providing hardware implementations of atomic operations. +//@ ignore-aarch64-unknown-linux-pauthtest #![crate_type = "rlib"] diff --git a/tests/codegen-llvm/box-uninit-bytes.rs b/tests/codegen-llvm/box-uninit-bytes.rs index 7ac929646cd4d..fc47e42d6f9db 100644 --- a/tests/codegen-llvm/box-uninit-bytes.rs +++ b/tests/codegen-llvm/box-uninit-bytes.rs @@ -43,4 +43,4 @@ pub fn box_lotsa_padding() -> Box { // from the CHECK-NOT above, and also verify the attributes got set reasonably. // CHECK: declare {{(dso_local )?}}noalias noundef ptr @{{.*}}__rust_alloc(i{{[0-9]+}} noundef, i{{[0-9]+}} allocalign noundef range(i{{[0-9]+}} 1, {{-2147483647|-9223372036854775807}})) unnamed_addr [[RUST_ALLOC_ATTRS:#[0-9]+]] -// CHECK-DAG: attributes [[RUST_ALLOC_ATTRS]] = { {{.*}} allockind("alloc,uninitialized,aligned") allocsize(0) {{(uwtable )?}}"alloc-family"="__rust_alloc" {{.*}} } +// CHECK-DAG: attributes [[RUST_ALLOC_ATTRS]] = { {{.*}} allockind("alloc,uninitialized,aligned"){{.*}} allocsize(0) {{(uwtable )?}}{{.*}}"alloc-family"="__rust_alloc" {{.*}} } diff --git a/tests/codegen-llvm/cffi/c-variadic.rs b/tests/codegen-llvm/cffi/c-variadic.rs index ceef7e72251d9..b609a2720f913 100644 --- a/tests/codegen-llvm/cffi/c-variadic.rs +++ b/tests/codegen-llvm/cffi/c-variadic.rs @@ -1,5 +1,9 @@ //@ needs-unwind //@ compile-flags: -C no-prepopulate-passes -Copt-level=0 +// Pauthtest generates pointer authentication metadata for call instructions +// and wraps function pointers in ConstPtrAuth. Disable this test for this target +// to avoid clutter from pointer authentication complexity. +//@ ignore-aarch64-unknown-linux-pauthtest #![crate_type = "lib"] #![feature(c_variadic)] diff --git a/tests/codegen-llvm/inline-always-works-always.rs b/tests/codegen-llvm/inline-always-works-always.rs index 07200fd9e373a..91a13ab1a7da3 100644 --- a/tests/codegen-llvm/inline-always-works-always.rs +++ b/tests/codegen-llvm/inline-always-works-always.rs @@ -2,6 +2,8 @@ //@[NO-OPT] compile-flags: -Copt-level=0 //@[SIZE-OPT] compile-flags: -Copt-level=s //@[SPEED-OPT] compile-flags: -Copt-level=3 +// Pointer authenticated calls are not guaranteed to be inlined. +//@ ignore-aarch64-unknown-linux-pauthtest #![crate_type = "rlib"] diff --git a/tests/codegen-llvm/issues/issue-73258.rs b/tests/codegen-llvm/issues/issue-73258.rs index c9eceb0dccf73..ff4c1aa8a0603 100644 --- a/tests/codegen-llvm/issues/issue-73258.rs +++ b/tests/codegen-llvm/issues/issue-73258.rs @@ -3,6 +3,8 @@ #![crate_type = "lib"] // Adapted from +// We explicitly match against `call{{.*}}(` because the aarch64-unknown-linux-pauthtest target +// emits `ptrauth-calls` attribute, which would otherwise make a plain `call` match ambiguous. #[derive(Clone, Copy)] #[repr(u8)] @@ -17,7 +19,7 @@ pub enum Foo { #[no_mangle] pub unsafe fn issue_73258(ptr: *const Foo) -> Foo { // CHECK-NOT: icmp - // CHECK-NOT: call + // CHECK-NOT: call{{.*}}( // CHECK-NOT: br {{.*}} // CHECK-NOT: select @@ -25,14 +27,14 @@ pub unsafe fn issue_73258(ptr: *const Foo) -> Foo { // CHECK-SAME: !range ! // CHECK-NOT: icmp - // CHECK-NOT: call + // CHECK-NOT: call{{.*}}( // CHECK-NOT: br {{.*}} // CHECK-NOT: select // CHECK: ret i8 %[[R]] // CHECK-NOT: icmp - // CHECK-NOT: call + // CHECK-NOT: call{{.*}}( // CHECK-NOT: br {{.*}} // CHECK-NOT: select let k: Option = Some(ptr.read()); diff --git a/tests/incremental/auxiliary/issue-54059.rs b/tests/incremental/auxiliary/issue-54059.rs index 6bbc94149e822..9c535ce0c7b90 100644 --- a/tests/incremental/auxiliary/issue-54059.rs +++ b/tests/incremental/auxiliary/issue-54059.rs @@ -40,7 +40,8 @@ proc_macro_expr_impl! { } } -#[link(name="rust_test_helpers")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers"))] extern "C" { pub fn rust_dbg_extern_identity_u64(v: u64) -> u64; } diff --git a/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs b/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs index 96b8ade1f1b16..fb5d5209a17db 100644 --- a/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs +++ b/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs @@ -6,6 +6,8 @@ //@ needs-target-std //@ ignore-android: FIXME(#142855) //@ ignore-sgx: (x86 machine code cannot be directly executed) +//@ ignore-aarch64-unknown-linux-pauthtest: (it requires non-trivial compilation of c sources, +// and only supports dynamic linking, ignore the test). use run_make_support::{cc, extra_c_flags, run, rustc, static_lib_name}; diff --git a/tests/run-make/pauth-quicksort-c-driver/main.c b/tests/run-make/pauth-quicksort-c-driver/main.c new file mode 100644 index 0000000000000..58f8d149cc446 --- /dev/null +++ b/tests/run-make/pauth-quicksort-c-driver/main.c @@ -0,0 +1,45 @@ +#include +#include + +#define NUM_ELEMS 5 + +void quickSort(void *Base, size_t N, size_t Size, + int (*Cmp)(const void *, const void *)); + +#ifdef __cplusplus +} +#endif + +int cmpI32Ascending(const void *LHS, const void *RHS) { + int32_t x = *(const int32_t *)LHS; + int32_t y = *(const int32_t *)RHS; + + if (x < y) + return -1; + else if (x > y) + return 1; + else + return 0; +} + +int main() { + int32_t Data[NUM_ELEMS] = {4, 2, 5, 3, 1}; + + printf("Before sorting: "); + for (int i = 0; i < NUM_ELEMS; i++) + printf("%d ", Data[i]); + printf("\n"); + + quickSort(Data, NUM_ELEMS, sizeof(int32_t), cmpI32Ascending); + + printf("After sorting: "); + for (int i = 0; i < NUM_ELEMS; i++) + printf("%d ", Data[i]); + printf("\n"); + + for (size_t i = 1; i < NUM_ELEMS; i++) + if (Data[i - 1] > Data[i]) + return 42; + + return 0; +} diff --git a/tests/run-make/pauth-quicksort-c-driver/quicksort.rs b/tests/run-make/pauth-quicksort-c-driver/quicksort.rs new file mode 100644 index 0000000000000..ac73dced2e1fc --- /dev/null +++ b/tests/run-make/pauth-quicksort-c-driver/quicksort.rs @@ -0,0 +1,66 @@ +use std::mem::size_of; +use std::os::raw::{c_int, c_void}; +use std::ptr; + +unsafe fn swap_i32(lhs: *mut i32, rhs: *mut i32) { + ptr::swap(lhs, rhs); +} + +unsafe fn partition( + arr: *mut i32, + low: isize, + high: isize, + cmp: extern "C" fn(*const c_void, *const c_void) -> c_int, +) -> isize { + let pivot = arr.offset(low); + let mut i = low; + let mut j = high; + + while i < j { + while i <= high - 1 && cmp(arr.offset(i) as *const c_void, pivot as *const c_void) <= 0 { + i += 1; + } + + while j >= low + 1 && cmp(arr.offset(j) as *const c_void, pivot as *const c_void) > 0 { + j -= 1; + } + + if i < j { + swap_i32(arr.offset(i), arr.offset(j)); + } + } + + swap_i32(arr.offset(low), arr.offset(j)); + j +} + +unsafe fn quicksort_rec( + arr: *mut i32, + low: isize, + high: isize, + cmp: extern "C" fn(*const c_void, *const c_void) -> c_int, +) { + if low < high { + let part = partition(arr, low, high, cmp); + quicksort_rec(arr, low, part - 1, cmp); + quicksort_rec(arr, part + 1, high, cmp); + } +} + +#[no_mangle] +pub extern "C" fn quickSort( + base: *mut c_void, + n: usize, + size: usize, + cmp: extern "C" fn(*const c_void, *const c_void) -> c_int, +) { + if size != size_of::() { + std::process::abort(); + } + + if n > 1 { + unsafe { + quicksort_rec(base as *mut i32, 0, (n as isize) - 1, cmp); + } + } +} diff --git a/tests/run-make/pauth-quicksort-c-driver/rmake.rs b/tests/run-make/pauth-quicksort-c-driver/rmake.rs new file mode 100644 index 0000000000000..26e4b4f1feaf9 --- /dev/null +++ b/tests/run-make/pauth-quicksort-c-driver/rmake.rs @@ -0,0 +1,42 @@ +// Test compilation flow using custom pauth-enabled toolchain and signing extern "C" function +// pointers used from within rust. The test assumes that pointer-authentication-enabled `clang` is +// available on the path. In this test rust is the driver - providing the data and the comparison +// function; while c - provides the implementation of quicksort algorithm and is the user of the +// data and comparator. + +//@ only-aarch64-unknown-linux-pauthtest + +use run_make_support::{cc, env_var, rfs, run, run_fail, rustc}; + +fn main() { + // Use CC and CC_DEFAULT_FLAGS env variables to set up linker for rustc. This results in the + // same command as cc(). The CC env variable corresponds to cc field in the config toml file. + // This field is required to point to a clang family compiler on aarch64-unknown-linux-pauthtest + // target. + let rust_lib_name = "rust_quicksort"; + rustc() + .target("aarch64-unknown-linux-pauthtest") + .crate_type("cdylib") + .input("quicksort.rs") + .linker(&env_var("CC")) + .link_args(&env_var("CC_DEFAULT_FLAGS")) + .crate_name(rust_lib_name) + .run(); + + let exe_name = "main"; + cc().out_exe(exe_name) + .input("main.c") + .args(&[ + "-march=armv8.3-a+pauth", + "-target", + "aarch64-unknown-linux-pauthtest", + &format!("-l{}", rust_lib_name), + ]) + .library_search_path(".") + .run(); + + run(exe_name); + + rfs::remove_file(format!("{}{rust_lib_name}.{}", "lib", "so")); + run_fail(exe_name); +} diff --git a/tests/run-make/pauth-quicksort-rust-driver/main.rs b/tests/run-make/pauth-quicksort-rust-driver/main.rs new file mode 100644 index 0000000000000..37700e0ddc169 --- /dev/null +++ b/tests/run-make/pauth-quicksort-rust-driver/main.rs @@ -0,0 +1,43 @@ +use std::os::raw::{c_int, c_void}; + +#[link(name = "quicksort")] +extern "C" { + fn quickSort( + base: *mut c_void, + n: usize, + size: usize, + cmp: extern "C" fn(*const c_void, *const c_void) -> c_int, + ); +} + +extern "C" fn cmp_i32_ascending(a: *const c_void, b: *const c_void) -> c_int { + unsafe { + let x = *(a as *const i32); + let y = *(b as *const i32); + + if x < y { + -1 + } else if x > y { + 1 + } else { + 0 + } + } +} + +fn main() { + let mut data: [i32; 5] = [4, 2, 5, 3, 1]; + println!("Before sorting: {:?}", data); + + unsafe { + quickSort( + data.as_mut_ptr() as *mut c_void, + data.len(), + std::mem::size_of::(), + cmp_i32_ascending, + ); + } + + println!("After sorting: {:?}", data); + assert!(data.windows(2).all(|w| w[0] <= w[1])); +} diff --git a/tests/run-make/pauth-quicksort-rust-driver/quicksort.c b/tests/run-make/pauth-quicksort-rust-driver/quicksort.c new file mode 100644 index 0000000000000..029435d562bb6 --- /dev/null +++ b/tests/run-make/pauth-quicksort-rust-driver/quicksort.c @@ -0,0 +1,50 @@ +#include +#include +#include + +void swap(void *A, void *B, size_t Size) { + unsigned char Tmp[Size]; + memcpy(Tmp, A, Size); + memcpy(A, B, Size); + memcpy(B, Tmp, Size); +} + +int partition(void *Base, int Low, int High, size_t Size, + int (*Cmp)(const void *, const void *)) { + char *Arr = (char *)Base; + + void *Pivot = Arr + Low * Size; + int i = Low; + int j = High; + + while (i < j) { + while (i <= High - 1 && Cmp(Arr + i * Size, Pivot) <= 0) + i++; + + while (j >= Low + 1 && Cmp(Arr + j * Size, Pivot) > 0) + j--; + + if (i < j) + swap(Arr + i * Size, Arr + j * Size, Size); + } + + swap(Arr + Low * Size, Arr + j * Size, Size); + return j; +} + +void quickSortRec(void *Base, int Low, int High, size_t Size, + int (*Cmp)(const void *, const void *)) { + if (Low < High) { + int Part = partition(Base, Low, High, Size, Cmp); + quickSortRec(Base, Low, Part - 1, Size, Cmp); + quickSortRec(Base, Part + 1, High, Size, Cmp); + } +} + +void quickSort(void *Base, size_t N, size_t Size, + int (*Cmp)(const void *, const void *)) { + if (Size != sizeof(int32_t)) + abort(); + if (N > 1) + quickSortRec(Base, 0, (int)N - 1, Size, Cmp); +} diff --git a/tests/run-make/pauth-quicksort-rust-driver/rmake.rs b/tests/run-make/pauth-quicksort-rust-driver/rmake.rs new file mode 100644 index 0000000000000..7eeb8b2b3944c --- /dev/null +++ b/tests/run-make/pauth-quicksort-rust-driver/rmake.rs @@ -0,0 +1,34 @@ +// Test compilation flow using custom pauth-enabled toolchain and signing extern "C" function +// pointers used from within rust. The test assumes that pointer-authentication-enabled `clang` is +// available on the path. +// In this test rust is the driver - providing the data and the comparison function; while c - +// provides the implementation of quicksort algorithm and is the user of the data and comparator. + +//@ only-aarch64-unknown-linux-pauthtest + +use run_make_support::{cc, env_var, rfs, run, run_fail, rustc}; + +fn main() { + let input = "quicksort"; + let input_name = format!("{input}.c"); + let lib_name = format!("{}{input}.{}", "lib", "so"); + cc().out_exe(&lib_name) + .input(&input_name) + .args(&["-target", "aarch64-unknown-linux-pauthtest", "-march=armv8.3-a+pauth", "-shared"]) + .run(); + + // Use CC and CC_DEFAULT_FLAGS env variables to set up linker for rustc. This results in the + // same command as cc(). The CC env variable corresponds to cc field in the config toml file. + // This field is required to point to a clang family compiler on aarch64-unknown-linux-pauthtest + // target. + rustc() + .target("aarch64-unknown-linux-pauthtest") + .input("main.rs") + .linker(&env_var("CC")) + .link_args(&env_var("CC_DEFAULT_FLAGS")) + .run(); + run("main"); + + rfs::remove_file(&lib_name); + run_fail("main"); +} diff --git a/tests/run-make/rustdoc-test-builder/rmake.rs b/tests/run-make/rustdoc-test-builder/rmake.rs index 17d40c68fd922..5520426e16fa8 100644 --- a/tests/run-make/rustdoc-test-builder/rmake.rs +++ b/tests/run-make/rustdoc-test-builder/rmake.rs @@ -27,6 +27,7 @@ fn main() { // so only exercise the success path when the target can run on the host. if target().contains("wasm") || target().contains("sgx") + || target().contains("pauthtest") || std::env::var_os("REMOTE_TEST_CLIENT").is_some() { return; diff --git a/tests/ui/abi/abi-sysv64-arg-passing.rs b/tests/ui/abi/abi-sysv64-arg-passing.rs index 362a1862f9d4c..7424cfaa7202b 100644 --- a/tests/ui/abi/abi-sysv64-arg-passing.rs +++ b/tests/ui/abi/abi-sysv64-arg-passing.rs @@ -106,7 +106,8 @@ mod tests { Some(u64), } - #[link(name = "rust_test_helpers", kind = "static")] + #[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] + #[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "sysv64" { pub fn rust_int8_to_int32(_: i8) -> i32; pub fn rust_dbg_extern_identity_u8(v: u8) -> u8; diff --git a/tests/ui/abi/anon-extern-mod.rs b/tests/ui/abi/anon-extern-mod.rs index 134542b9cff38..2c7f6a5621b27 100644 --- a/tests/ui/abi/anon-extern-mod.rs +++ b/tests/ui/abi/anon-extern-mod.rs @@ -1,6 +1,7 @@ //@ run-pass -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { fn rust_get_test_int() -> isize; } diff --git a/tests/ui/abi/c-stack-as-value.rs b/tests/ui/abi/c-stack-as-value.rs index 10933bdb2781f..02d5df9b8e6ac 100644 --- a/tests/ui/abi/c-stack-as-value.rs +++ b/tests/ui/abi/c-stack-as-value.rs @@ -1,7 +1,8 @@ //@ run-pass mod rustrt { - #[link(name = "rust_test_helpers", kind = "static")] + #[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] + #[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn rust_get_test_int() -> isize; } diff --git a/tests/ui/abi/cabi-int-widening.rs b/tests/ui/abi/cabi-int-widening.rs index e211b98983731..ab57bc2f64065 100644 --- a/tests/ui/abi/cabi-int-widening.rs +++ b/tests/ui/abi/cabi-int-widening.rs @@ -1,6 +1,7 @@ //@ run-pass -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { fn rust_int8_to_int32(_: i8) -> i32; } diff --git a/tests/ui/abi/cross-crate/auxiliary/anon-extern-mod-cross-crate-1.rs b/tests/ui/abi/cross-crate/auxiliary/anon-extern-mod-cross-crate-1.rs index 559c40546a8a5..b8326e34e2f43 100644 --- a/tests/ui/abi/cross-crate/auxiliary/anon-extern-mod-cross-crate-1.rs +++ b/tests/ui/abi/cross-crate/auxiliary/anon-extern-mod-cross-crate-1.rs @@ -1,6 +1,7 @@ #![crate_name = "anonexternmod"] -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn rust_get_test_int() -> isize; } diff --git a/tests/ui/abi/extern/auxiliary/extern-crosscrate-source.rs b/tests/ui/abi/extern/auxiliary/extern-crosscrate-source.rs index 6b21877109664..9c073291f397d 100644 --- a/tests/ui/abi/extern/auxiliary/extern-crosscrate-source.rs +++ b/tests/ui/abi/extern/auxiliary/extern-crosscrate-source.rs @@ -1,7 +1,8 @@ #![crate_name = "externcallback"] #![crate_type = "lib"] -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn rust_dbg_call( cb: extern "C" fn(u64) -> u64, diff --git a/tests/ui/abi/extern/extern-call-deep.rs b/tests/ui/abi/extern/extern-call-deep.rs index 40457ae57207d..bcfedbe37e61b 100644 --- a/tests/ui/abi/extern/extern-call-deep.rs +++ b/tests/ui/abi/extern/extern-call-deep.rs @@ -1,7 +1,8 @@ //@ run-pass //@ ignore-emscripten blows the JS stack -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn rust_dbg_call( cb: extern "C" fn(u64) -> u64, diff --git a/tests/ui/abi/extern/extern-call-deep2.rs b/tests/ui/abi/extern/extern-call-deep2.rs index 91ca28d80c80f..e4c9a3bebafcd 100644 --- a/tests/ui/abi/extern/extern-call-deep2.rs +++ b/tests/ui/abi/extern/extern-call-deep2.rs @@ -3,7 +3,8 @@ use std::thread; -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn rust_dbg_call( cb: extern "C" fn(u64) -> u64, diff --git a/tests/ui/abi/extern/extern-call-indirect.rs b/tests/ui/abi/extern/extern-call-indirect.rs index ef1e8ae5e760d..504e6a6af5ce0 100644 --- a/tests/ui/abi/extern/extern-call-indirect.rs +++ b/tests/ui/abi/extern/extern-call-indirect.rs @@ -1,6 +1,7 @@ //@ run-pass -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn rust_dbg_call( cb: extern "C" fn(u64) -> u64, diff --git a/tests/ui/abi/extern/extern-call-scrub.rs b/tests/ui/abi/extern/extern-call-scrub.rs index 7df3a8f04ef1f..9fbda16467239 100644 --- a/tests/ui/abi/extern/extern-call-scrub.rs +++ b/tests/ui/abi/extern/extern-call-scrub.rs @@ -6,7 +6,8 @@ use std::thread; -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn rust_dbg_call( cb: extern "C" fn(u64) -> u64, diff --git a/tests/ui/abi/extern/extern-pass-FiveU16s.rs b/tests/ui/abi/extern/extern-pass-FiveU16s.rs index 5f1307beb28e6..60f32c4b1500d 100644 --- a/tests/ui/abi/extern/extern-pass-FiveU16s.rs +++ b/tests/ui/abi/extern/extern-pass-FiveU16s.rs @@ -16,7 +16,8 @@ pub struct FiveU16s { five: u16, } -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn rust_dbg_extern_identity_FiveU16s(v: FiveU16s) -> FiveU16s; } diff --git a/tests/ui/abi/extern/extern-pass-TwoU16s.rs b/tests/ui/abi/extern/extern-pass-TwoU16s.rs index 8bde553050a40..6f907f5fa1d15 100644 --- a/tests/ui/abi/extern/extern-pass-TwoU16s.rs +++ b/tests/ui/abi/extern/extern-pass-TwoU16s.rs @@ -10,7 +10,8 @@ pub struct TwoU16s { two: u16, } -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn rust_dbg_extern_identity_TwoU16s(v: TwoU16s) -> TwoU16s; } diff --git a/tests/ui/abi/extern/extern-pass-TwoU32s.rs b/tests/ui/abi/extern/extern-pass-TwoU32s.rs index fc90eb6945c7e..c4a6438741528 100644 --- a/tests/ui/abi/extern/extern-pass-TwoU32s.rs +++ b/tests/ui/abi/extern/extern-pass-TwoU32s.rs @@ -10,7 +10,8 @@ pub struct TwoU32s { two: u32, } -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn rust_dbg_extern_identity_TwoU32s(v: TwoU32s) -> TwoU32s; } diff --git a/tests/ui/abi/extern/extern-pass-TwoU64s.rs b/tests/ui/abi/extern/extern-pass-TwoU64s.rs index 603de2e49ab2d..dd077c930196f 100644 --- a/tests/ui/abi/extern/extern-pass-TwoU64s.rs +++ b/tests/ui/abi/extern/extern-pass-TwoU64s.rs @@ -10,7 +10,8 @@ pub struct TwoU64s { two: u64, } -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn rust_dbg_extern_identity_TwoU64s(v: TwoU64s) -> TwoU64s; } diff --git a/tests/ui/abi/extern/extern-pass-TwoU8s.rs b/tests/ui/abi/extern/extern-pass-TwoU8s.rs index a712d79a98dd2..49fc0f6bb6a66 100644 --- a/tests/ui/abi/extern/extern-pass-TwoU8s.rs +++ b/tests/ui/abi/extern/extern-pass-TwoU8s.rs @@ -10,7 +10,8 @@ pub struct TwoU8s { two: u8, } -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn rust_dbg_extern_identity_TwoU8s(v: TwoU8s) -> TwoU8s; } diff --git a/tests/ui/abi/extern/extern-pass-char.rs b/tests/ui/abi/extern/extern-pass-char.rs index 24196c54b50d8..200c308994786 100644 --- a/tests/ui/abi/extern/extern-pass-char.rs +++ b/tests/ui/abi/extern/extern-pass-char.rs @@ -2,7 +2,8 @@ // Test a function that takes/returns a u8. -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn rust_dbg_extern_identity_u8(v: u8) -> u8; } diff --git a/tests/ui/abi/extern/extern-pass-double.rs b/tests/ui/abi/extern/extern-pass-double.rs index e883ebe9815ee..cf3de204d5b9a 100644 --- a/tests/ui/abi/extern/extern-pass-double.rs +++ b/tests/ui/abi/extern/extern-pass-double.rs @@ -1,6 +1,7 @@ //@ run-pass -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn rust_dbg_extern_identity_double(v: f64) -> f64; } diff --git a/tests/ui/abi/extern/extern-pass-empty.rs b/tests/ui/abi/extern/extern-pass-empty.rs index 1ad52b128ad93..5c445a6384cc3 100644 --- a/tests/ui/abi/extern/extern-pass-empty.rs +++ b/tests/ui/abi/extern/extern-pass-empty.rs @@ -25,7 +25,8 @@ struct ManyInts { #[repr(C)] struct Empty; -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { fn rust_dbg_extern_empty_struct(v1: ManyInts, e: Empty, v2: ManyInts); } diff --git a/tests/ui/abi/extern/extern-pass-u32.rs b/tests/ui/abi/extern/extern-pass-u32.rs index 0daff4e9f42c4..69adcbcfa92c7 100644 --- a/tests/ui/abi/extern/extern-pass-u32.rs +++ b/tests/ui/abi/extern/extern-pass-u32.rs @@ -2,7 +2,8 @@ // Test a function that takes/returns a u32. -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn rust_dbg_extern_identity_u32(v: u32) -> u32; } diff --git a/tests/ui/abi/extern/extern-pass-u64.rs b/tests/ui/abi/extern/extern-pass-u64.rs index f1cd6bf59c2ec..4df57f41af0da 100644 --- a/tests/ui/abi/extern/extern-pass-u64.rs +++ b/tests/ui/abi/extern/extern-pass-u64.rs @@ -2,7 +2,8 @@ // Test a call to a function that takes/returns a u64. -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn rust_dbg_extern_identity_u64(v: u64) -> u64; } diff --git a/tests/ui/abi/extern/extern-return-FiveU16s.rs b/tests/ui/abi/extern/extern-return-FiveU16s.rs index d8ae8b2661c5a..1175585b66bd5 100644 --- a/tests/ui/abi/extern/extern-return-FiveU16s.rs +++ b/tests/ui/abi/extern/extern-return-FiveU16s.rs @@ -9,7 +9,8 @@ pub struct FiveU16s { five: u16, } -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn rust_dbg_extern_return_FiveU16s() -> FiveU16s; } diff --git a/tests/ui/abi/extern/extern-return-TwoU16s.rs b/tests/ui/abi/extern/extern-return-TwoU16s.rs index bf909a8db24c7..8355f51e68ac9 100644 --- a/tests/ui/abi/extern/extern-return-TwoU16s.rs +++ b/tests/ui/abi/extern/extern-return-TwoU16s.rs @@ -6,7 +6,8 @@ pub struct TwoU16s { two: u16, } -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn rust_dbg_extern_return_TwoU16s() -> TwoU16s; } diff --git a/tests/ui/abi/extern/extern-return-TwoU32s.rs b/tests/ui/abi/extern/extern-return-TwoU32s.rs index c528da8cfc464..ee59dfb2f61ae 100644 --- a/tests/ui/abi/extern/extern-return-TwoU32s.rs +++ b/tests/ui/abi/extern/extern-return-TwoU32s.rs @@ -6,7 +6,8 @@ pub struct TwoU32s { two: u32, } -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn rust_dbg_extern_return_TwoU32s() -> TwoU32s; } diff --git a/tests/ui/abi/extern/extern-return-TwoU64s.rs b/tests/ui/abi/extern/extern-return-TwoU64s.rs index d4f9540ec7b35..70f7b8b854024 100644 --- a/tests/ui/abi/extern/extern-return-TwoU64s.rs +++ b/tests/ui/abi/extern/extern-return-TwoU64s.rs @@ -6,7 +6,8 @@ pub struct TwoU64s { two: u64, } -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn rust_dbg_extern_return_TwoU64s() -> TwoU64s; } diff --git a/tests/ui/abi/extern/extern-return-TwoU8s.rs b/tests/ui/abi/extern/extern-return-TwoU8s.rs index 228b27396249b..c436ec18833d1 100644 --- a/tests/ui/abi/extern/extern-return-TwoU8s.rs +++ b/tests/ui/abi/extern/extern-return-TwoU8s.rs @@ -6,7 +6,8 @@ pub struct TwoU8s { two: u8, } -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn rust_dbg_extern_return_TwoU8s() -> TwoU8s; } diff --git a/tests/ui/abi/foreign/auxiliary/foreign_lib.rs b/tests/ui/abi/foreign/auxiliary/foreign_lib.rs index 74a95b96e9fc4..06e9f58a74a78 100644 --- a/tests/ui/abi/foreign/auxiliary/foreign_lib.rs +++ b/tests/ui/abi/foreign/auxiliary/foreign_lib.rs @@ -1,7 +1,8 @@ #![crate_name = "foreign_lib"] pub mod rustrt { - #[link(name = "rust_test_helpers", kind = "static")] + #[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] + #[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn rust_get_test_int() -> isize; } diff --git a/tests/ui/abi/foreign/foreign-fn-with-byval.rs b/tests/ui/abi/foreign/foreign-fn-with-byval.rs index 9908ec2d2c01a..79c15136a6b63 100644 --- a/tests/ui/abi/foreign/foreign-fn-with-byval.rs +++ b/tests/ui/abi/foreign/foreign-fn-with-byval.rs @@ -8,7 +8,8 @@ pub struct S { z: u64, } -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn get_x(x: S) -> u64; pub fn get_y(x: S) -> u64; diff --git a/tests/ui/abi/issue-28676.rs b/tests/ui/abi/issue-28676.rs index 2abb4ce52b3b3..2f3cf97fe8a35 100644 --- a/tests/ui/abi/issue-28676.rs +++ b/tests/ui/abi/issue-28676.rs @@ -15,7 +15,8 @@ pub struct Quad { mod rustrt { use super::Quad; - #[link(name = "rust_test_helpers", kind = "static")] + #[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] + #[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn get_c_many_params( _: *const (), diff --git a/tests/ui/abi/issues/issue-62350-sysv-neg-reg-counts.rs b/tests/ui/abi/issues/issue-62350-sysv-neg-reg-counts.rs index 314db42280d99..7b3dd2e8b376c 100644 --- a/tests/ui/abi/issues/issue-62350-sysv-neg-reg-counts.rs +++ b/tests/ui/abi/issues/issue-62350-sysv-neg-reg-counts.rs @@ -13,7 +13,8 @@ pub struct QuadFloats { mod rustrt { use super::QuadFloats; - #[link(name = "rust_test_helpers", kind = "static")] + #[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] + #[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn get_c_exhaust_sysv64_ints( _: *const (), diff --git a/tests/ui/abi/issues/issue-97463-broken-abi-leaked-uninit-data.rs b/tests/ui/abi/issues/issue-97463-broken-abi-leaked-uninit-data.rs index f694205174889..afc521106df9d 100644 --- a/tests/ui/abi/issues/issue-97463-broken-abi-leaked-uninit-data.rs +++ b/tests/ui/abi/issues/issue-97463-broken-abi-leaked-uninit-data.rs @@ -2,7 +2,8 @@ #![allow(dead_code)] #![allow(improper_ctypes)] -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn issue_97463_leak_uninit_data(a: u32, b: u32, c: u32) -> u16; } diff --git a/tests/ui/abi/mir/mir_codegen_calls_variadic.rs b/tests/ui/abi/mir/mir_codegen_calls_variadic.rs index 0c1a59b38d3eb..04fa9b49cd06e 100644 --- a/tests/ui/abi/mir/mir_codegen_calls_variadic.rs +++ b/tests/ui/abi/mir/mir_codegen_calls_variadic.rs @@ -1,6 +1,7 @@ //@ run-pass -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { fn rust_interesting_average(_: i64, ...) -> f64; } diff --git a/tests/ui/abi/numbers-arithmetic/i128-ffi.rs b/tests/ui/abi/numbers-arithmetic/i128-ffi.rs index 26d65e8bbf853..dc9f56abb1ae9 100644 --- a/tests/ui/abi/numbers-arithmetic/i128-ffi.rs +++ b/tests/ui/abi/numbers-arithmetic/i128-ffi.rs @@ -8,7 +8,8 @@ //@ ignore-windows //@ ignore-32bit -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { fn identity(f: u128) -> u128; fn square(f: i128) -> i128; diff --git a/tests/ui/abi/segfault-no-out-of-stack.rs b/tests/ui/abi/segfault-no-out-of-stack.rs index b122079d36f76..bc2e59b346346 100644 --- a/tests/ui/abi/segfault-no-out-of-stack.rs +++ b/tests/ui/abi/segfault-no-out-of-stack.rs @@ -10,7 +10,8 @@ use std::env; use std::ffi::c_char; use std::process::{Command, ExitStatus}; -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { fn rust_get_null_ptr() -> *mut c_char; } diff --git a/tests/ui/abi/stack-probes.rs b/tests/ui/abi/stack-probes.rs index 4d0301411e0fd..b03aee1b9ed2f 100644 --- a/tests/ui/abi/stack-probes.rs +++ b/tests/ui/abi/stack-probes.rs @@ -17,7 +17,8 @@ use std::mem::MaybeUninit; use std::process::Command; use std::thread; -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { #[link_name = "rust_dbg_extern_identity_u64"] fn black_box(u: u64); diff --git a/tests/ui/abi/statics/static-mut-foreign.rs b/tests/ui/abi/statics/static-mut-foreign.rs index 40cd57637e6cd..01eba2739190d 100644 --- a/tests/ui/abi/statics/static-mut-foreign.rs +++ b/tests/ui/abi/statics/static-mut-foreign.rs @@ -8,7 +8,8 @@ use std::ffi::c_int; -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { static mut rust_dbg_static_mut: c_int; pub fn rust_dbg_static_mut_check_four(); diff --git a/tests/ui/abi/struct-enums/struct-return.rs b/tests/ui/abi/struct-enums/struct-return.rs index 5fe80d5f670b2..0ba0a59d5dab0 100644 --- a/tests/ui/abi/struct-enums/struct-return.rs +++ b/tests/ui/abi/struct-enums/struct-return.rs @@ -39,7 +39,8 @@ pub struct CharCharFloat { mod rustrt { use super::{CharCharDouble, CharCharFloat, Floats, Quad}; - #[link(name = "rust_test_helpers", kind = "static")] + #[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] + #[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn rust_dbg_abi_1(q: Quad) -> Quad; pub fn rust_dbg_abi_2(f: Floats) -> Floats; diff --git a/tests/ui/abi/union/union-c-interop.rs b/tests/ui/abi/union/union-c-interop.rs index 05eac446a9182..e4376ea889360 100644 --- a/tests/ui/abi/union/union-c-interop.rs +++ b/tests/ui/abi/union/union-c-interop.rs @@ -16,7 +16,8 @@ union LARGE_INTEGER { QuadPart: u64, } -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { fn increment_all_parts(_: LARGE_INTEGER) -> LARGE_INTEGER; } diff --git a/tests/ui/abi/variadic-ffi.rs b/tests/ui/abi/variadic-ffi.rs index 42ff8d0dbb36e..af2f9b8c4bf40 100644 --- a/tests/ui/abi/variadic-ffi.rs +++ b/tests/ui/abi/variadic-ffi.rs @@ -5,7 +5,8 @@ use std::ffi::VaList; -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { fn rust_interesting_average(_: u64, ...) -> f64; diff --git a/tests/ui/float/target-has-reliable-nightly-float.rs b/tests/ui/float/target-has-reliable-nightly-float.rs index 399f101f49ae2..cef050a8c7037 100644 --- a/tests/ui/float/target-has-reliable-nightly-float.rs +++ b/tests/ui/float/target-has-reliable-nightly-float.rs @@ -21,7 +21,7 @@ pub fn has_f128_math() {} fn main() { if cfg!(target_arch = "aarch64") && cfg!(target_os = "linux") && - cfg!(not(target_env = "musl")) { + cfg!(not(any(target_env = "musl", target_env = "pauthtest"))) { // Aarch64+GNU+Linux is one target that has support for all features, so use it to spot // check that the compiler does indeed enable these gates. diff --git a/tests/ui/link-native-libs/lib-defaults.rs b/tests/ui/link-native-libs/lib-defaults.rs index 4e38adb643dbf..8d7e11b9d7299 100644 --- a/tests/ui/link-native-libs/lib-defaults.rs +++ b/tests/ui/link-native-libs/lib-defaults.rs @@ -5,7 +5,8 @@ //@ run-pass //@ compile-flags: -lrust_test_helpers -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn rust_dbg_extern_identity_u32(x: u32) -> u32; } diff --git a/tests/ui/linking/auxiliary/aux-25185-1.rs b/tests/ui/linking/auxiliary/aux-25185-1.rs index 032d7d5de348b..eed03e569d336 100644 --- a/tests/ui/linking/auxiliary/aux-25185-1.rs +++ b/tests/ui/linking/auxiliary/aux-25185-1.rs @@ -2,7 +2,8 @@ #![crate_type = "rlib"] -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { pub fn rust_dbg_extern_identity_u32(u: u32) -> u32; } diff --git a/tests/ui/macros/macros-in-extern.rs b/tests/ui/macros/macros-in-extern.rs index 97780650d1126..a9a36bbcf7a8d 100644 --- a/tests/ui/macros/macros-in-extern.rs +++ b/tests/ui/macros/macros-in-extern.rs @@ -41,7 +41,8 @@ fn main() { assert_eq!(unsafe { f2() }, 2); } -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { returns_isize!(rust_get_test_int); takes_u32_returns_u32!(rust_dbg_extern_identity_u32); diff --git a/tests/ui/proc-macro/macros-in-extern.rs b/tests/ui/proc-macro/macros-in-extern.rs index f39322d126e04..2d96339c2528e 100644 --- a/tests/ui/proc-macro/macros-in-extern.rs +++ b/tests/ui/proc-macro/macros-in-extern.rs @@ -9,7 +9,8 @@ fn main() { assert_eq!(unsafe { rust_dbg_extern_identity_u32(0xDEADBEEF) }, 0xDEADBEEF); } -#[link(name = "rust_test_helpers", kind = "static")] +#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] +#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] extern "C" { #[empty_attr] fn some_definitely_unknown_symbol_which_should_be_removed(); diff --git a/tests/ui/process/nofile-limit.rs b/tests/ui/process/nofile-limit.rs index f5246856b80f2..e2f0224625a3a 100644 --- a/tests/ui/process/nofile-limit.rs +++ b/tests/ui/process/nofile-limit.rs @@ -8,6 +8,9 @@ //@ no-prefer-dynamic //@ compile-flags: -Ctarget-feature=+crt-static -Crpath=no -Crelocation-model=static //@ ignore-backends: gcc +// aarch64-unknown-linux-pauthtest requires dynamic linking, which makes use of file descriptors. +// Setting RLIMIT_NOFILE would result in the binary failing even before main is reached. +//@ ignore-aarch64-unknown-linux-pauthtest #![feature(exit_status_error)] #![feature(rustc_private)] From 05a389dbcfb8eda15bbace2eb82fdbf18b1f049d Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Wed, 29 Apr 2026 09:51:04 +0000 Subject: [PATCH 12/38] Override static libraries and warn on linking against that kind of libs --- compiler/rustc_metadata/src/diagnostics.rs | 15 ++++++ compiler/rustc_metadata/src/native_libs.rs | 22 +++++++++ .../aarch64-unknown-linux-pauthtest.md | 4 +- tests/incremental/auxiliary/issue-54059.rs | 3 +- .../pauth-static-link-warning/helper.c | 1 + .../pauth-static-link-warning/main.rs | 10 ++++ .../main_cmd_line.rs | 9 ++++ .../pauth-static-link-warning/rmake.rs | 46 +++++++++++++++++++ tests/ui/abi/abi-sysv64-arg-passing.rs | 3 +- tests/ui/abi/anon-extern-mod.rs | 3 +- tests/ui/abi/c-stack-as-value.rs | 3 +- tests/ui/abi/cabi-int-widening.rs | 3 +- .../anon-extern-mod-cross-crate-1.rs | 3 +- .../auxiliary/extern-crosscrate-source.rs | 3 +- tests/ui/abi/extern/extern-call-deep.rs | 3 +- tests/ui/abi/extern/extern-call-deep2.rs | 3 +- tests/ui/abi/extern/extern-call-indirect.rs | 3 +- tests/ui/abi/extern/extern-call-scrub.rs | 3 +- tests/ui/abi/extern/extern-pass-FiveU16s.rs | 3 +- tests/ui/abi/extern/extern-pass-TwoU16s.rs | 3 +- tests/ui/abi/extern/extern-pass-TwoU32s.rs | 3 +- tests/ui/abi/extern/extern-pass-TwoU64s.rs | 3 +- tests/ui/abi/extern/extern-pass-TwoU8s.rs | 3 +- tests/ui/abi/extern/extern-pass-char.rs | 3 +- tests/ui/abi/extern/extern-pass-double.rs | 3 +- tests/ui/abi/extern/extern-pass-empty.rs | 3 +- tests/ui/abi/extern/extern-pass-u32.rs | 3 +- tests/ui/abi/extern/extern-pass-u64.rs | 3 +- tests/ui/abi/extern/extern-return-FiveU16s.rs | 3 +- tests/ui/abi/extern/extern-return-TwoU16s.rs | 3 +- tests/ui/abi/extern/extern-return-TwoU32s.rs | 3 +- tests/ui/abi/extern/extern-return-TwoU64s.rs | 3 +- tests/ui/abi/extern/extern-return-TwoU8s.rs | 3 +- tests/ui/abi/foreign/auxiliary/foreign_lib.rs | 3 +- tests/ui/abi/foreign/foreign-fn-with-byval.rs | 3 +- tests/ui/abi/issue-28676.rs | 3 +- .../issues/issue-62350-sysv-neg-reg-counts.rs | 3 +- ...sue-97463-broken-abi-leaked-uninit-data.rs | 3 +- .../ui/abi/mir/mir_codegen_calls_variadic.rs | 3 +- tests/ui/abi/numbers-arithmetic/i128-ffi.rs | 3 +- tests/ui/abi/segfault-no-out-of-stack.rs | 3 +- tests/ui/abi/stack-probes.rs | 3 +- tests/ui/abi/statics/static-mut-foreign.rs | 3 +- tests/ui/abi/struct-enums/struct-return.rs | 3 +- tests/ui/abi/union/union-c-interop.rs | 3 +- tests/ui/abi/variadic-ffi.rs | 3 +- tests/ui/link-native-libs/lib-defaults.rs | 3 +- tests/ui/linking/auxiliary/aux-25185-1.rs | 3 +- tests/ui/macros/macros-in-extern.rs | 3 +- tests/ui/proc-macro/macros-in-extern.rs | 3 +- 50 files changed, 149 insertions(+), 87 deletions(-) create mode 100644 tests/run-make/pauth-static-link-warning/helper.c create mode 100644 tests/run-make/pauth-static-link-warning/main.rs create mode 100644 tests/run-make/pauth-static-link-warning/main_cmd_line.rs create mode 100644 tests/run-make/pauth-static-link-warning/rmake.rs diff --git a/compiler/rustc_metadata/src/diagnostics.rs b/compiler/rustc_metadata/src/diagnostics.rs index 5c33fab5011d1..659406f8c84e9 100644 --- a/compiler/rustc_metadata/src/diagnostics.rs +++ b/compiler/rustc_metadata/src/diagnostics.rs @@ -704,3 +704,18 @@ pub(crate) struct MitigationLessStrictInDependency { pub mitigation_level: String, pub extern_crate: Symbol, } + +#[derive(Diagnostic)] +pub(crate) enum StaticLinkingNotSupported<'a> { + #[diag( + "static linking of `{$lib_name}` is not supported on `{$target}`; using dynamic linking instead" + )] + #[help("remove `kind = \"static\"` and ensure a shared library is available")] + UserRequested { lib_name: Symbol, target: &'a str }, + + #[diag( + "library `{$lib_name}` is linked statically by a dependency, but `{$target}` requires dynamic linking; using dynamic linking instead" + )] + #[help("ensure a shared library is available")] + FromDependency { lib_name: Symbol, target: &'a str }, +} diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index e467cf9bf105b..1b7a2532a27fc 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -197,6 +197,28 @@ pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> Vec } } collector.process_command_line(); + for lib in &mut collector.libs { + if tcx.sess.target.env == Env::Pauthtest { + if let NativeLibKind::Static { .. } = lib.kind { + if !tcx.sess.opts.unstable_opts.ui_testing { + let diag = if lib.foreign_module.is_none() { + diagnostics::StaticLinkingNotSupported::UserRequested { + lib_name: lib.name, + target: tcx.sess.target.llvm_target.as_ref(), + } + } else { + diagnostics::StaticLinkingNotSupported::FromDependency { + lib_name: lib.name, + target: tcx.sess.target.llvm_target.as_ref(), + } + }; + tcx.dcx().emit_warn(diag); + } + + lib.kind = NativeLibKind::Dylib { as_needed: None }; + } + } + } collector.libs } diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md index 3630c66c3c60f..07b9b5f4ed2e3 100644 --- a/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md +++ b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md @@ -391,8 +391,9 @@ The following categories are supported (all present in tree): * End-to-end execution tests * Rust-driven quicksort (pauth-quicksort-rust-driver) * C-driven quicksort (pauth-quicksort-c-driver) -* UI error reporting (the target does not support `+crt-static`) +* UI error/warning reporting (the target does not support static linking) * crt-static-pauthtest.rs + * pauth-static-link-warning All tests from `assembly-llvm`, `codegen-llvm`, `codegen-units`, `coverage`, `crashes`, `incremental`, `library`, `mir-opt`, `run-make`, `ui` and @@ -413,6 +414,7 @@ x.py test --target aarch64-unknown-linux-pauthtest --force-rerun assembly-llvm \ tests/codegen-llvm/pauth/pauth-init-fini.rs \ tests/run-make/pauth-quicksort-rust-driver \ tests/run-make/pauth-quicksort-c-driver \ + tests/run-make/pauth-static-link-warning \ tests/ui/statics/crt-static-pauthtest.rs ``` diff --git a/tests/incremental/auxiliary/issue-54059.rs b/tests/incremental/auxiliary/issue-54059.rs index 9c535ce0c7b90..feefe533d7fa9 100644 --- a/tests/incremental/auxiliary/issue-54059.rs +++ b/tests/incremental/auxiliary/issue-54059.rs @@ -40,8 +40,7 @@ proc_macro_expr_impl! { } } -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn rust_dbg_extern_identity_u64(v: u64) -> u64; } diff --git a/tests/run-make/pauth-static-link-warning/helper.c b/tests/run-make/pauth-static-link-warning/helper.c new file mode 100644 index 0000000000000..fb2bfd5cb990e --- /dev/null +++ b/tests/run-make/pauth-static-link-warning/helper.c @@ -0,0 +1 @@ +int helper_function() { return 42; } diff --git a/tests/run-make/pauth-static-link-warning/main.rs b/tests/run-make/pauth-static-link-warning/main.rs new file mode 100644 index 0000000000000..0323db8838559 --- /dev/null +++ b/tests/run-make/pauth-static-link-warning/main.rs @@ -0,0 +1,10 @@ +#[link(name = "helper", kind = "static")] +extern "C" { + fn helper_function() -> i32; +} + +fn main() { + unsafe { + assert!(42 == helper_function()); + } +} diff --git a/tests/run-make/pauth-static-link-warning/main_cmd_line.rs b/tests/run-make/pauth-static-link-warning/main_cmd_line.rs new file mode 100644 index 0000000000000..a0edd66ce9910 --- /dev/null +++ b/tests/run-make/pauth-static-link-warning/main_cmd_line.rs @@ -0,0 +1,9 @@ +extern "C" { + fn helper_function() -> i32; +} + +fn main() { + unsafe { + assert!(42 == helper_function()); + } +} diff --git a/tests/run-make/pauth-static-link-warning/rmake.rs b/tests/run-make/pauth-static-link-warning/rmake.rs new file mode 100644 index 0000000000000..74fe1f0bd6d5d --- /dev/null +++ b/tests/run-make/pauth-static-link-warning/rmake.rs @@ -0,0 +1,46 @@ +// Make sure that for `aarch64-unknown-linux-pauthtest` compiler emits warning when static +// libraries are linked. Test both foreign module linked from #[link] directive and command line +// invocations. + +//@ only-aarch64-unknown-linux-pauthtest +// ignore-tidy-linelength + +use run_make_support::{cc, env_var, regex, run, rustc}; + +fn main() { + let input = "helper"; + let input_name = format!("{input}.c"); + let lib_name = format!("{}{input}.{}", "lib", "a"); + // Build a static library + cc().out_exe(&lib_name) + .input(&input_name) + .args(&["-target", "aarch64-unknown-linux-pauthtest", "-march=armv8.3-a+pauth", "-c"]) + .run(); + + // Check against foreign module warning: #[link(name = "helper", kind = "static")] + let stderr_foreign_module = rustc() + .target("aarch64-unknown-linux-pauthtest") + .input("main.rs") + .linker(&env_var("CC")) + .link_args(&env_var("CC_DEFAULT_FLAGS")) + .arg("-L.") + .run() + .stderr_utf8(); + run("main"); + let re_foreign_moule = regex::Regex::new( r"(?s)warning: library `helper`.*linked statically.*aarch64-unknown-linux-pauthtest.*requires dynamic linking.*using dynamic linking instead").unwrap(); + assert!(re_foreign_moule.is_match(&stderr_foreign_module)); + + // Check against command line warning: -lstatic=helper + let stderr_command_line = rustc() + .target("aarch64-unknown-linux-pauthtest") + .input("main_cmd_line.rs") + .linker(&env_var("CC")) + .link_args(&env_var("CC_DEFAULT_FLAGS")) + .arg("-L.") + .arg("-lstatic=helper") + .run() + .stderr_utf8(); + run("main_cmd_line"); + let re_cmd_line = regex::Regex::new( r"(?s)warning: static linking of `helper`.*is not supported on.*aarch64-unknown-linux-pauthtest.*using dynamic linking instead").unwrap(); + assert!(re_cmd_line.is_match(&stderr_command_line)); +} diff --git a/tests/ui/abi/abi-sysv64-arg-passing.rs b/tests/ui/abi/abi-sysv64-arg-passing.rs index 7424cfaa7202b..362a1862f9d4c 100644 --- a/tests/ui/abi/abi-sysv64-arg-passing.rs +++ b/tests/ui/abi/abi-sysv64-arg-passing.rs @@ -106,8 +106,7 @@ mod tests { Some(u64), } - #[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] - #[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] + #[link(name = "rust_test_helpers", kind = "static")] extern "sysv64" { pub fn rust_int8_to_int32(_: i8) -> i32; pub fn rust_dbg_extern_identity_u8(v: u8) -> u8; diff --git a/tests/ui/abi/anon-extern-mod.rs b/tests/ui/abi/anon-extern-mod.rs index 2c7f6a5621b27..134542b9cff38 100644 --- a/tests/ui/abi/anon-extern-mod.rs +++ b/tests/ui/abi/anon-extern-mod.rs @@ -1,7 +1,6 @@ //@ run-pass -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { fn rust_get_test_int() -> isize; } diff --git a/tests/ui/abi/c-stack-as-value.rs b/tests/ui/abi/c-stack-as-value.rs index 02d5df9b8e6ac..10933bdb2781f 100644 --- a/tests/ui/abi/c-stack-as-value.rs +++ b/tests/ui/abi/c-stack-as-value.rs @@ -1,8 +1,7 @@ //@ run-pass mod rustrt { - #[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] - #[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] + #[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn rust_get_test_int() -> isize; } diff --git a/tests/ui/abi/cabi-int-widening.rs b/tests/ui/abi/cabi-int-widening.rs index ab57bc2f64065..e211b98983731 100644 --- a/tests/ui/abi/cabi-int-widening.rs +++ b/tests/ui/abi/cabi-int-widening.rs @@ -1,7 +1,6 @@ //@ run-pass -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { fn rust_int8_to_int32(_: i8) -> i32; } diff --git a/tests/ui/abi/cross-crate/auxiliary/anon-extern-mod-cross-crate-1.rs b/tests/ui/abi/cross-crate/auxiliary/anon-extern-mod-cross-crate-1.rs index b8326e34e2f43..559c40546a8a5 100644 --- a/tests/ui/abi/cross-crate/auxiliary/anon-extern-mod-cross-crate-1.rs +++ b/tests/ui/abi/cross-crate/auxiliary/anon-extern-mod-cross-crate-1.rs @@ -1,7 +1,6 @@ #![crate_name = "anonexternmod"] -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn rust_get_test_int() -> isize; } diff --git a/tests/ui/abi/extern/auxiliary/extern-crosscrate-source.rs b/tests/ui/abi/extern/auxiliary/extern-crosscrate-source.rs index 9c073291f397d..6b21877109664 100644 --- a/tests/ui/abi/extern/auxiliary/extern-crosscrate-source.rs +++ b/tests/ui/abi/extern/auxiliary/extern-crosscrate-source.rs @@ -1,8 +1,7 @@ #![crate_name = "externcallback"] #![crate_type = "lib"] -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn rust_dbg_call( cb: extern "C" fn(u64) -> u64, diff --git a/tests/ui/abi/extern/extern-call-deep.rs b/tests/ui/abi/extern/extern-call-deep.rs index bcfedbe37e61b..40457ae57207d 100644 --- a/tests/ui/abi/extern/extern-call-deep.rs +++ b/tests/ui/abi/extern/extern-call-deep.rs @@ -1,8 +1,7 @@ //@ run-pass //@ ignore-emscripten blows the JS stack -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn rust_dbg_call( cb: extern "C" fn(u64) -> u64, diff --git a/tests/ui/abi/extern/extern-call-deep2.rs b/tests/ui/abi/extern/extern-call-deep2.rs index e4c9a3bebafcd..91ca28d80c80f 100644 --- a/tests/ui/abi/extern/extern-call-deep2.rs +++ b/tests/ui/abi/extern/extern-call-deep2.rs @@ -3,8 +3,7 @@ use std::thread; -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn rust_dbg_call( cb: extern "C" fn(u64) -> u64, diff --git a/tests/ui/abi/extern/extern-call-indirect.rs b/tests/ui/abi/extern/extern-call-indirect.rs index 504e6a6af5ce0..ef1e8ae5e760d 100644 --- a/tests/ui/abi/extern/extern-call-indirect.rs +++ b/tests/ui/abi/extern/extern-call-indirect.rs @@ -1,7 +1,6 @@ //@ run-pass -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn rust_dbg_call( cb: extern "C" fn(u64) -> u64, diff --git a/tests/ui/abi/extern/extern-call-scrub.rs b/tests/ui/abi/extern/extern-call-scrub.rs index 9fbda16467239..7df3a8f04ef1f 100644 --- a/tests/ui/abi/extern/extern-call-scrub.rs +++ b/tests/ui/abi/extern/extern-call-scrub.rs @@ -6,8 +6,7 @@ use std::thread; -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn rust_dbg_call( cb: extern "C" fn(u64) -> u64, diff --git a/tests/ui/abi/extern/extern-pass-FiveU16s.rs b/tests/ui/abi/extern/extern-pass-FiveU16s.rs index 60f32c4b1500d..5f1307beb28e6 100644 --- a/tests/ui/abi/extern/extern-pass-FiveU16s.rs +++ b/tests/ui/abi/extern/extern-pass-FiveU16s.rs @@ -16,8 +16,7 @@ pub struct FiveU16s { five: u16, } -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn rust_dbg_extern_identity_FiveU16s(v: FiveU16s) -> FiveU16s; } diff --git a/tests/ui/abi/extern/extern-pass-TwoU16s.rs b/tests/ui/abi/extern/extern-pass-TwoU16s.rs index 6f907f5fa1d15..8bde553050a40 100644 --- a/tests/ui/abi/extern/extern-pass-TwoU16s.rs +++ b/tests/ui/abi/extern/extern-pass-TwoU16s.rs @@ -10,8 +10,7 @@ pub struct TwoU16s { two: u16, } -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn rust_dbg_extern_identity_TwoU16s(v: TwoU16s) -> TwoU16s; } diff --git a/tests/ui/abi/extern/extern-pass-TwoU32s.rs b/tests/ui/abi/extern/extern-pass-TwoU32s.rs index c4a6438741528..fc90eb6945c7e 100644 --- a/tests/ui/abi/extern/extern-pass-TwoU32s.rs +++ b/tests/ui/abi/extern/extern-pass-TwoU32s.rs @@ -10,8 +10,7 @@ pub struct TwoU32s { two: u32, } -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn rust_dbg_extern_identity_TwoU32s(v: TwoU32s) -> TwoU32s; } diff --git a/tests/ui/abi/extern/extern-pass-TwoU64s.rs b/tests/ui/abi/extern/extern-pass-TwoU64s.rs index dd077c930196f..603de2e49ab2d 100644 --- a/tests/ui/abi/extern/extern-pass-TwoU64s.rs +++ b/tests/ui/abi/extern/extern-pass-TwoU64s.rs @@ -10,8 +10,7 @@ pub struct TwoU64s { two: u64, } -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn rust_dbg_extern_identity_TwoU64s(v: TwoU64s) -> TwoU64s; } diff --git a/tests/ui/abi/extern/extern-pass-TwoU8s.rs b/tests/ui/abi/extern/extern-pass-TwoU8s.rs index 49fc0f6bb6a66..a712d79a98dd2 100644 --- a/tests/ui/abi/extern/extern-pass-TwoU8s.rs +++ b/tests/ui/abi/extern/extern-pass-TwoU8s.rs @@ -10,8 +10,7 @@ pub struct TwoU8s { two: u8, } -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn rust_dbg_extern_identity_TwoU8s(v: TwoU8s) -> TwoU8s; } diff --git a/tests/ui/abi/extern/extern-pass-char.rs b/tests/ui/abi/extern/extern-pass-char.rs index 200c308994786..24196c54b50d8 100644 --- a/tests/ui/abi/extern/extern-pass-char.rs +++ b/tests/ui/abi/extern/extern-pass-char.rs @@ -2,8 +2,7 @@ // Test a function that takes/returns a u8. -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn rust_dbg_extern_identity_u8(v: u8) -> u8; } diff --git a/tests/ui/abi/extern/extern-pass-double.rs b/tests/ui/abi/extern/extern-pass-double.rs index cf3de204d5b9a..e883ebe9815ee 100644 --- a/tests/ui/abi/extern/extern-pass-double.rs +++ b/tests/ui/abi/extern/extern-pass-double.rs @@ -1,7 +1,6 @@ //@ run-pass -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn rust_dbg_extern_identity_double(v: f64) -> f64; } diff --git a/tests/ui/abi/extern/extern-pass-empty.rs b/tests/ui/abi/extern/extern-pass-empty.rs index 5c445a6384cc3..1ad52b128ad93 100644 --- a/tests/ui/abi/extern/extern-pass-empty.rs +++ b/tests/ui/abi/extern/extern-pass-empty.rs @@ -25,8 +25,7 @@ struct ManyInts { #[repr(C)] struct Empty; -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { fn rust_dbg_extern_empty_struct(v1: ManyInts, e: Empty, v2: ManyInts); } diff --git a/tests/ui/abi/extern/extern-pass-u32.rs b/tests/ui/abi/extern/extern-pass-u32.rs index 69adcbcfa92c7..0daff4e9f42c4 100644 --- a/tests/ui/abi/extern/extern-pass-u32.rs +++ b/tests/ui/abi/extern/extern-pass-u32.rs @@ -2,8 +2,7 @@ // Test a function that takes/returns a u32. -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn rust_dbg_extern_identity_u32(v: u32) -> u32; } diff --git a/tests/ui/abi/extern/extern-pass-u64.rs b/tests/ui/abi/extern/extern-pass-u64.rs index 4df57f41af0da..f1cd6bf59c2ec 100644 --- a/tests/ui/abi/extern/extern-pass-u64.rs +++ b/tests/ui/abi/extern/extern-pass-u64.rs @@ -2,8 +2,7 @@ // Test a call to a function that takes/returns a u64. -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn rust_dbg_extern_identity_u64(v: u64) -> u64; } diff --git a/tests/ui/abi/extern/extern-return-FiveU16s.rs b/tests/ui/abi/extern/extern-return-FiveU16s.rs index 1175585b66bd5..d8ae8b2661c5a 100644 --- a/tests/ui/abi/extern/extern-return-FiveU16s.rs +++ b/tests/ui/abi/extern/extern-return-FiveU16s.rs @@ -9,8 +9,7 @@ pub struct FiveU16s { five: u16, } -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn rust_dbg_extern_return_FiveU16s() -> FiveU16s; } diff --git a/tests/ui/abi/extern/extern-return-TwoU16s.rs b/tests/ui/abi/extern/extern-return-TwoU16s.rs index 8355f51e68ac9..bf909a8db24c7 100644 --- a/tests/ui/abi/extern/extern-return-TwoU16s.rs +++ b/tests/ui/abi/extern/extern-return-TwoU16s.rs @@ -6,8 +6,7 @@ pub struct TwoU16s { two: u16, } -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn rust_dbg_extern_return_TwoU16s() -> TwoU16s; } diff --git a/tests/ui/abi/extern/extern-return-TwoU32s.rs b/tests/ui/abi/extern/extern-return-TwoU32s.rs index ee59dfb2f61ae..c528da8cfc464 100644 --- a/tests/ui/abi/extern/extern-return-TwoU32s.rs +++ b/tests/ui/abi/extern/extern-return-TwoU32s.rs @@ -6,8 +6,7 @@ pub struct TwoU32s { two: u32, } -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn rust_dbg_extern_return_TwoU32s() -> TwoU32s; } diff --git a/tests/ui/abi/extern/extern-return-TwoU64s.rs b/tests/ui/abi/extern/extern-return-TwoU64s.rs index 70f7b8b854024..d4f9540ec7b35 100644 --- a/tests/ui/abi/extern/extern-return-TwoU64s.rs +++ b/tests/ui/abi/extern/extern-return-TwoU64s.rs @@ -6,8 +6,7 @@ pub struct TwoU64s { two: u64, } -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn rust_dbg_extern_return_TwoU64s() -> TwoU64s; } diff --git a/tests/ui/abi/extern/extern-return-TwoU8s.rs b/tests/ui/abi/extern/extern-return-TwoU8s.rs index c436ec18833d1..228b27396249b 100644 --- a/tests/ui/abi/extern/extern-return-TwoU8s.rs +++ b/tests/ui/abi/extern/extern-return-TwoU8s.rs @@ -6,8 +6,7 @@ pub struct TwoU8s { two: u8, } -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn rust_dbg_extern_return_TwoU8s() -> TwoU8s; } diff --git a/tests/ui/abi/foreign/auxiliary/foreign_lib.rs b/tests/ui/abi/foreign/auxiliary/foreign_lib.rs index 06e9f58a74a78..74a95b96e9fc4 100644 --- a/tests/ui/abi/foreign/auxiliary/foreign_lib.rs +++ b/tests/ui/abi/foreign/auxiliary/foreign_lib.rs @@ -1,8 +1,7 @@ #![crate_name = "foreign_lib"] pub mod rustrt { - #[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] - #[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] + #[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn rust_get_test_int() -> isize; } diff --git a/tests/ui/abi/foreign/foreign-fn-with-byval.rs b/tests/ui/abi/foreign/foreign-fn-with-byval.rs index 79c15136a6b63..9908ec2d2c01a 100644 --- a/tests/ui/abi/foreign/foreign-fn-with-byval.rs +++ b/tests/ui/abi/foreign/foreign-fn-with-byval.rs @@ -8,8 +8,7 @@ pub struct S { z: u64, } -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn get_x(x: S) -> u64; pub fn get_y(x: S) -> u64; diff --git a/tests/ui/abi/issue-28676.rs b/tests/ui/abi/issue-28676.rs index 2f3cf97fe8a35..2abb4ce52b3b3 100644 --- a/tests/ui/abi/issue-28676.rs +++ b/tests/ui/abi/issue-28676.rs @@ -15,8 +15,7 @@ pub struct Quad { mod rustrt { use super::Quad; - #[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] - #[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] + #[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn get_c_many_params( _: *const (), diff --git a/tests/ui/abi/issues/issue-62350-sysv-neg-reg-counts.rs b/tests/ui/abi/issues/issue-62350-sysv-neg-reg-counts.rs index 7b3dd2e8b376c..314db42280d99 100644 --- a/tests/ui/abi/issues/issue-62350-sysv-neg-reg-counts.rs +++ b/tests/ui/abi/issues/issue-62350-sysv-neg-reg-counts.rs @@ -13,8 +13,7 @@ pub struct QuadFloats { mod rustrt { use super::QuadFloats; - #[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] - #[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] + #[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn get_c_exhaust_sysv64_ints( _: *const (), diff --git a/tests/ui/abi/issues/issue-97463-broken-abi-leaked-uninit-data.rs b/tests/ui/abi/issues/issue-97463-broken-abi-leaked-uninit-data.rs index afc521106df9d..f694205174889 100644 --- a/tests/ui/abi/issues/issue-97463-broken-abi-leaked-uninit-data.rs +++ b/tests/ui/abi/issues/issue-97463-broken-abi-leaked-uninit-data.rs @@ -2,8 +2,7 @@ #![allow(dead_code)] #![allow(improper_ctypes)] -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn issue_97463_leak_uninit_data(a: u32, b: u32, c: u32) -> u16; } diff --git a/tests/ui/abi/mir/mir_codegen_calls_variadic.rs b/tests/ui/abi/mir/mir_codegen_calls_variadic.rs index 04fa9b49cd06e..0c1a59b38d3eb 100644 --- a/tests/ui/abi/mir/mir_codegen_calls_variadic.rs +++ b/tests/ui/abi/mir/mir_codegen_calls_variadic.rs @@ -1,7 +1,6 @@ //@ run-pass -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { fn rust_interesting_average(_: i64, ...) -> f64; } diff --git a/tests/ui/abi/numbers-arithmetic/i128-ffi.rs b/tests/ui/abi/numbers-arithmetic/i128-ffi.rs index dc9f56abb1ae9..26d65e8bbf853 100644 --- a/tests/ui/abi/numbers-arithmetic/i128-ffi.rs +++ b/tests/ui/abi/numbers-arithmetic/i128-ffi.rs @@ -8,8 +8,7 @@ //@ ignore-windows //@ ignore-32bit -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { fn identity(f: u128) -> u128; fn square(f: i128) -> i128; diff --git a/tests/ui/abi/segfault-no-out-of-stack.rs b/tests/ui/abi/segfault-no-out-of-stack.rs index bc2e59b346346..b122079d36f76 100644 --- a/tests/ui/abi/segfault-no-out-of-stack.rs +++ b/tests/ui/abi/segfault-no-out-of-stack.rs @@ -10,8 +10,7 @@ use std::env; use std::ffi::c_char; use std::process::{Command, ExitStatus}; -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { fn rust_get_null_ptr() -> *mut c_char; } diff --git a/tests/ui/abi/stack-probes.rs b/tests/ui/abi/stack-probes.rs index b03aee1b9ed2f..4d0301411e0fd 100644 --- a/tests/ui/abi/stack-probes.rs +++ b/tests/ui/abi/stack-probes.rs @@ -17,8 +17,7 @@ use std::mem::MaybeUninit; use std::process::Command; use std::thread; -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { #[link_name = "rust_dbg_extern_identity_u64"] fn black_box(u: u64); diff --git a/tests/ui/abi/statics/static-mut-foreign.rs b/tests/ui/abi/statics/static-mut-foreign.rs index 01eba2739190d..40cd57637e6cd 100644 --- a/tests/ui/abi/statics/static-mut-foreign.rs +++ b/tests/ui/abi/statics/static-mut-foreign.rs @@ -8,8 +8,7 @@ use std::ffi::c_int; -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { static mut rust_dbg_static_mut: c_int; pub fn rust_dbg_static_mut_check_four(); diff --git a/tests/ui/abi/struct-enums/struct-return.rs b/tests/ui/abi/struct-enums/struct-return.rs index 0ba0a59d5dab0..5fe80d5f670b2 100644 --- a/tests/ui/abi/struct-enums/struct-return.rs +++ b/tests/ui/abi/struct-enums/struct-return.rs @@ -39,8 +39,7 @@ pub struct CharCharFloat { mod rustrt { use super::{CharCharDouble, CharCharFloat, Floats, Quad}; - #[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] - #[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] + #[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn rust_dbg_abi_1(q: Quad) -> Quad; pub fn rust_dbg_abi_2(f: Floats) -> Floats; diff --git a/tests/ui/abi/union/union-c-interop.rs b/tests/ui/abi/union/union-c-interop.rs index e4376ea889360..05eac446a9182 100644 --- a/tests/ui/abi/union/union-c-interop.rs +++ b/tests/ui/abi/union/union-c-interop.rs @@ -16,8 +16,7 @@ union LARGE_INTEGER { QuadPart: u64, } -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { fn increment_all_parts(_: LARGE_INTEGER) -> LARGE_INTEGER; } diff --git a/tests/ui/abi/variadic-ffi.rs b/tests/ui/abi/variadic-ffi.rs index af2f9b8c4bf40..42ff8d0dbb36e 100644 --- a/tests/ui/abi/variadic-ffi.rs +++ b/tests/ui/abi/variadic-ffi.rs @@ -5,8 +5,7 @@ use std::ffi::VaList; -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { fn rust_interesting_average(_: u64, ...) -> f64; diff --git a/tests/ui/link-native-libs/lib-defaults.rs b/tests/ui/link-native-libs/lib-defaults.rs index 8d7e11b9d7299..4e38adb643dbf 100644 --- a/tests/ui/link-native-libs/lib-defaults.rs +++ b/tests/ui/link-native-libs/lib-defaults.rs @@ -5,8 +5,7 @@ //@ run-pass //@ compile-flags: -lrust_test_helpers -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn rust_dbg_extern_identity_u32(x: u32) -> u32; } diff --git a/tests/ui/linking/auxiliary/aux-25185-1.rs b/tests/ui/linking/auxiliary/aux-25185-1.rs index eed03e569d336..032d7d5de348b 100644 --- a/tests/ui/linking/auxiliary/aux-25185-1.rs +++ b/tests/ui/linking/auxiliary/aux-25185-1.rs @@ -2,8 +2,7 @@ #![crate_type = "rlib"] -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { pub fn rust_dbg_extern_identity_u32(u: u32) -> u32; } diff --git a/tests/ui/macros/macros-in-extern.rs b/tests/ui/macros/macros-in-extern.rs index a9a36bbcf7a8d..97780650d1126 100644 --- a/tests/ui/macros/macros-in-extern.rs +++ b/tests/ui/macros/macros-in-extern.rs @@ -41,8 +41,7 @@ fn main() { assert_eq!(unsafe { f2() }, 2); } -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { returns_isize!(rust_get_test_int); takes_u32_returns_u32!(rust_dbg_extern_identity_u32); diff --git a/tests/ui/proc-macro/macros-in-extern.rs b/tests/ui/proc-macro/macros-in-extern.rs index 2d96339c2528e..f39322d126e04 100644 --- a/tests/ui/proc-macro/macros-in-extern.rs +++ b/tests/ui/proc-macro/macros-in-extern.rs @@ -9,8 +9,7 @@ fn main() { assert_eq!(unsafe { rust_dbg_extern_identity_u32(0xDEADBEEF) }, 0xDEADBEEF); } -#[cfg_attr(target_env = "pauthtest", link(name = "rust_test_helpers", kind = "dylib"))] -#[cfg_attr(not(target_env = "pauthtest"), link(name = "rust_test_helpers", kind = "static"))] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { #[empty_attr] fn some_definitely_unknown_symbol_which_should_be_removed(); From 128d77db0d9e427e2ccb3cdf4f500c7d1161081a Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Tue, 5 May 2026 07:44:46 +0000 Subject: [PATCH 13/38] Correctly set f128 math support for pauthtest --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 951094d9aa369..84a516ffe48af 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -387,8 +387,6 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) { cfg.has_reliable_f128 = match (target_arch, target_os) { // Unsupported https://github.com/llvm/llvm-project/issues/121122 (Arch::AmdGpu, _) => false, - // Pauthtest musl does not support 128-bit floating point math. - (Arch::AArch64, _) if *target_env == Env::Pauthtest => false, // Unsupported (Arch::Arm64EC, _) => false, // Selection bug . This issue is closed @@ -422,8 +420,8 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) { // (ld is `f64`), anything other than Linux (Windows and MacOS use `f64`), and `x86` // (ld is 80-bit extended precision). // - // musl does not implement the symbols required for f128 math at all. - _ if *target_env == Env::Musl => false, + // musl and pauthtest do not implement the symbols required for f128 math at all. + _ if (*target_env == Env::Musl || *target_env == Env::Pauthtest) => false, (Arch::X86_64, _) => false, (_, Os::Linux) if target_pointer_width == 64 => true, _ => false, From b8198cc829cb26b58ff0707092a25d5154a5059e Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Mon, 11 May 2026 12:57:23 +0000 Subject: [PATCH 14/38] Use target_env = "musl" & target_abi = "pauthtest" --- compiler/rustc_codegen_llvm/src/attributes.rs | 6 ++++-- compiler/rustc_codegen_llvm/src/base.rs | 8 ++++---- compiler/rustc_codegen_llvm/src/builder.rs | 4 ++-- compiler/rustc_codegen_llvm/src/common.rs | 4 ++-- compiler/rustc_codegen_llvm/src/consts.rs | 8 +++++--- compiler/rustc_codegen_llvm/src/intrinsic.rs | 4 ++-- compiler/rustc_codegen_llvm/src/llvm_util.rs | 4 ++-- compiler/rustc_metadata/src/native_libs.rs | 2 +- compiler/rustc_session/src/session.rs | 8 ++++---- compiler/rustc_target/src/spec/mod.rs | 3 ++- .../src/spec/targets/aarch64_unknown_linux_pauthtest.rs | 7 ++++--- .../platform-support/aarch64-unknown-linux-pauthtest.md | 2 +- src/librustdoc/clean/cfg.rs | 1 - tests/ui/check-cfg/well-known-values.stderr | 4 ++-- tests/ui/float/target-has-reliable-nightly-float.rs | 2 +- 15 files changed, 36 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index e283a2a6fdcf9..98a66b89847a8 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -12,7 +12,9 @@ use rustc_session::config::{ }; use rustc_span::sym; use rustc_symbol_mangling::mangle_internal_symbol; -use rustc_target::spec::{Arch, Env, FramePointer, SanitizerSet, StackProbeType, StackProtector}; +use rustc_target::spec::{ + Arch, CfgAbi, FramePointer, SanitizerSet, StackProbeType, StackProtector, +}; use smallvec::SmallVec; use crate::common::pauth_fn_attrs; @@ -644,7 +646,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( } } - if sess.target.env == Env::Pauthtest { + if sess.target.cfg_abi == CfgAbi::Pauthtest { for &ptrauth_attr in pauth_fn_attrs() { to_add.push(llvm::CreateAttrString(cx.llcx, ptrauth_attr)); } diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 74cb57a859399..949a6bb77c429 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -25,7 +25,7 @@ use rustc_middle::mono::Visibility; use rustc_middle::ty::TyCtxt; use rustc_session::config::{DebugInfo, Offload}; use rustc_span::Symbol; -use rustc_target::spec::{Env, SanitizerSet}; +use rustc_target::spec::{CfgAbi, SanitizerSet}; use super::ModuleLlvm; use crate::attributes; @@ -127,7 +127,7 @@ pub(crate) fn compile_codegen_unit( let mut attrs = attributes::sanitize_attrs(&cx, tcx, SanitizerFnAttrs::default()); // When pointer authentication is enabled, ensure that the ptrauth-* attributes are // also attached to the entry wrapper. - if cx.sess().target.env == Env::Pauthtest { + if cx.sess().target.cfg_abi == CfgAbi::Pauthtest { for &ptrauth_attr in pauth_fn_attrs() { attrs.push(llvm::CreateAttrString(cx.llcx, ptrauth_attr)); } @@ -148,14 +148,14 @@ pub(crate) fn compile_codegen_unit( cx.add_objc_module_flags(); } - if cx.sess().target.env == Env::Pauthtest { + if cx.sess().target.cfg_abi == CfgAbi::Pauthtest { // FIXME(jchlanda): In LLVM/Clang, there are also `aarch64-elf-pauthabi-platform` // and `aarch64-elf-pauthabi-version` module flags. These are emitted into the // PAuth core info section of the resulting ELF, which the linker uses to enforce // binary compatibility. // // We intentionally do not emit these flags now, since only a subset of features - // included in pauthtest ABI is currently supported. By default, the absence of + // included in clang's pauthtest is currently supported. By default, the absence of // this info is treated as compatible with any binary. // // Please note, that this would cause compatibility issues, specifically runtime diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 52ffac2146258..2973344cb9ee1 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -26,7 +26,7 @@ use rustc_sanitizers::{cfi, kcfi}; use rustc_session::config::OptLevel; use rustc_span::Span; use rustc_target::callconv::{FnAbi, PassMode}; -use rustc_target::spec::{Arch, Env, HasTargetSpec, SanitizerSet, Target}; +use rustc_target::spec::{Arch, CfgAbi, HasTargetSpec, SanitizerSet, Target}; use smallvec::SmallVec; use tracing::{debug, instrument}; @@ -2042,7 +2042,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { llfn: &'ll Value, fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, ) -> Option> { - if self.sess().target.env != Env::Pauthtest { + if self.sess().target.cfg_abi != CfgAbi::Pauthtest { return None; } // Pointer authentication support is currently limited to extern "C" calls; filter out other diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 4c9af0e56312a..f1cb891a12f6e 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -16,7 +16,7 @@ use rustc_middle::bug; use rustc_middle::mir::interpret::{GlobalAlloc, PointerArithmetic, Scalar}; use rustc_middle::ty::{Instance, TyCtxt}; use rustc_session::cstore::DllImport; -use rustc_target::spec::Env; +use rustc_target::spec::CfgAbi; use tracing::debug; use crate::consts::{IsInitOrFini, IsStatic, const_alloc_to_llvm}; @@ -46,7 +46,7 @@ pub(crate) fn maybe_sign_fn_ptr<'ll, 'tcx>( llfn: &'ll llvm::Value, pac: PacMetadata, ) -> &'ll llvm::Value { - if cx.sess().target.env != Env::Pauthtest { + if cx.sess().target.cfg_abi != CfgAbi::Pauthtest { return llfn; } diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 7ec338a263376..a8b0d79ae6985 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -17,7 +17,7 @@ use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; use rustc_middle::ty::{self, Instance}; use rustc_middle::{bug, span_bug}; use rustc_span::Symbol; -use rustc_target::spec::{Arch, Env}; +use rustc_target::spec::{Arch, CfgAbi}; use tracing::{debug, instrument, trace}; use crate::common::CodegenCx; @@ -123,7 +123,9 @@ pub(crate) fn const_alloc_to_llvm<'ll>( // Under pointer authentication, function pointers stored in init/fini arrays need special // handling. let pac_metadata = Some( - if cx.sess().target.env == Env::Pauthtest && matches!(is_init_fini, IsInitOrFini::Yes) { + if cx.sess().target.cfg_abi == CfgAbi::Pauthtest + && matches!(is_init_fini, IsInitOrFini::Yes) + { PacMetadata { // Must correspond to ptrauth_key_init_fini_pointer from `ptrauth.h`. key: 0, @@ -217,7 +219,7 @@ fn check_and_apply_linkage<'ll, 'tcx>( let fn_sig = sig.with(*header); let fn_abi = cx.fn_abi_of_fn_ptr(fn_sig, ty::List::empty()); // Decide if the initializer needs to be signed - if cx.sess().target.env == Env::Pauthtest + if cx.sess().target.cfg_abi == CfgAbi::Pauthtest && matches!(fn_sig.abi(), ExternAbi::C { .. } | ExternAbi::System { .. }) { should_sign = true; diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 170a35f15d1e0..382604ac2cfd5 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -28,7 +28,7 @@ use rustc_session::lint::builtin::DEPRECATED_LLVM_INTRINSIC; use rustc_span::{ErrorGuaranteed, Span, Symbol, sym}; use rustc_symbol_mangling::{mangle_internal_symbol, symbol_name_for_instance_in_crate}; use rustc_target::callconv::PassMode; -use rustc_target::spec::{Arch, Env}; +use rustc_target::spec::{Arch, CfgAbi}; use tracing::debug; use crate::abi::FnAbiLlvmExt; @@ -1698,7 +1698,7 @@ fn get_rust_try_fn<'a, 'll, 'tcx>( hir::Safety::Unsafe, )); let rust_try = gen_fn(cx, "__rust_try", rust_fn_sig, codegen); - if cx.sess().target.env == Env::Pauthtest { + if cx.sess().target.cfg_abi == CfgAbi::Pauthtest { let attrs: Vec<&Attribute> = pauth_fn_attrs().iter().map(|name| llvm::CreateAttrString(cx.llcx, name)).collect(); let (_ty, rust_try_fn) = rust_try; diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 84a516ffe48af..73b7f699b606d 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -420,8 +420,8 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) { // (ld is `f64`), anything other than Linux (Windows and MacOS use `f64`), and `x86` // (ld is 80-bit extended precision). // - // musl and pauthtest do not implement the symbols required for f128 math at all. - _ if (*target_env == Env::Musl || *target_env == Env::Pauthtest) => false, + // musl does not implement the symbols required for f128 math at all. + _ if *target_env == Env::Musl => false, (Arch::X86_64, _) => false, (_, Os::Linux) if target_pointer_width == 64 => true, _ => false, diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 1b7a2532a27fc..c044f7233cc08 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -198,7 +198,7 @@ pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> Vec } collector.process_command_line(); for lib in &mut collector.libs { - if tcx.sess.target.env == Env::Pauthtest { + if tcx.sess.target.cfg_abi == CfgAbi::Pauthtest { if let NativeLibKind::Static { .. } = lib.kind { if !tcx.sess.opts.unstable_opts.ui_testing { let diag = if lib.foreign_module.is_none() { diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 66d4620641190..b15a1139cc9e0 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -29,9 +29,9 @@ use rustc_span::source_map::{FilePathMapping, SourceMap}; use rustc_span::{RealFileName, Span, Symbol}; use rustc_target::asm::InlineAsmArch; use rustc_target::spec::{ - Arch, CodeModel, DebuginfoKind, Env, Os, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, - SmallDataThresholdSupport, SplitDebuginfo, StackProtector, SymbolVisibility, Target, - TargetTuple, TlsModel, apple, + Arch, CfgAbi, CodeModel, DebuginfoKind, Os, PanicStrategy, RelocModel, RelroLevel, + SanitizerSet, SmallDataThresholdSupport, SplitDebuginfo, StackProtector, SymbolVisibility, + Target, TargetTuple, TlsModel, apple, }; use crate::code_stats::CodeStats; @@ -1230,7 +1230,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } // Using static linking is prohibited on pauthtest target - if sess.crt_static(None) && sess.target.env == Env::Pauthtest { + if sess.crt_static(None) && sess.target.cfg_abi == CfgAbi::Pauthtest { sess.dcx().emit_err(errors::CannotEnableCrtStaticPointerAuth); } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index dd077f1e89586..7925435bc7e3f 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -2043,7 +2043,6 @@ crate::target_spec_enum! { P1 = "p1", P2 = "p2", P3 = "p3", - Pauthtest = "pauthtest", Uclibc = "uclibc", V5 = "v5", Unspecified = "", @@ -2074,6 +2073,7 @@ crate::target_spec_enum! { Ilp32e = "ilp32e", Llvm = "llvm", MacAbi = "macabi", + Pauthtest = "pauthtest", Sim = "sim", SoftFloat = "softfloat", Spe = "spe", @@ -3415,6 +3415,7 @@ impl Target { CfgAbi::Ilp32 | CfgAbi::Llvm | CfgAbi::MacAbi + | CfgAbi::Pauthtest | CfgAbi::Sim | CfgAbi::Uwp | CfgAbi::Unspecified diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_pauthtest.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_pauthtest.rs index b578b6c5cacf2..ff28391070667 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_pauthtest.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_pauthtest.rs @@ -1,6 +1,6 @@ use crate::spec::{ - Arch, Env, FramePointer, LinkSelfContainedDefault, StackProbeType, Target, TargetMetadata, - TargetOptions, base, + Arch, CfgAbi, Env, FramePointer, LinkSelfContainedDefault, StackProbeType, Target, + TargetMetadata, TargetOptions, base, }; pub(crate) fn target() -> Target { @@ -17,7 +17,8 @@ pub(crate) fn target() -> Target { arch: Arch::AArch64, options: TargetOptions { - env: Env::Pauthtest, + env: Env::Musl, + cfg_abi: CfgAbi::Pauthtest, // `pauthtest` requires v8.3a, which includes lse, no need for outline-atomics features: "+v8.3a,+pauth".into(), max_atomic_width: Some(128), diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md index 07b9b5f4ed2e3..7841bdfcee6d7 100644 --- a/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md +++ b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md @@ -165,7 +165,7 @@ index 0564f2e..a8a0d1a 100644 // clause, and if this is fixed that test in theory can be run on macOS! if cfg!(target_vendor = "apple") { self.ip() -+ } else if cfg!(target_env = "pauthtest") { ++ } else if cfg!(target_abi = "pauthtest") { + // NOTE: ip here is an unsigned (raw) pointer, so we must not use + // uw::_Unwind_FindEnclosingFunction. + // diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 4c5cbf7f4942b..170f1439ecc9c 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -643,7 +643,6 @@ fn human_readable_target_env(env: Symbol) -> Option<&'static str> { P1 => "WASIp1", P2 => "WASIp2", P3 => "WASIp3", - Pauthtest => "pauthtest", Relibc => "relibc", Sgx => "SGX", Sim => "Simulator", diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index c9c6b00ab1ae4..ebcb439b73498 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -129,7 +129,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_abi = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_abi` are: ``, `abi64`, `abiv2`, `abiv2hf`, `eabi`, `eabihf`, `elfv1`, `elfv2`, `fortanix`, `ilp32`, `ilp32e`, `llvm`, `macabi`, `sim`, `softfloat`, `spe`, `uwp`, `vec-extabi`, and `x32` + = note: expected values for `target_abi` are: ``, `abi64`, `abiv2`, `abiv2hf`, `eabi`, `eabihf`, `elfv1`, `elfv2`, `fortanix`, `ilp32`, `ilp32e`, `llvm`, `macabi`, `pauthtest`, `sim`, `softfloat`, `spe`, `uwp`, `vec-extabi`, and `x32` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -156,7 +156,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_env = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_env` are: ``, `gnu`, `macabi`, `mlibc`, `msvc`, `musl`, `newlib`, `nto70`, `nto71`, `nto71_iosock`, `nto80`, `ohos`, `p1`, `p2`, `p3`, `pauthtest`, `relibc`, `sgx`, `sim`, `uclibc`, and `v5` + = note: expected values for `target_env` are: ``, `gnu`, `macabi`, `mlibc`, `msvc`, `musl`, `newlib`, `nto70`, `nto71`, `nto71_iosock`, `nto80`, `ohos`, `p1`, `p2`, `p3`, `relibc`, `sgx`, `sim`, `uclibc`, and `v5` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` diff --git a/tests/ui/float/target-has-reliable-nightly-float.rs b/tests/ui/float/target-has-reliable-nightly-float.rs index cef050a8c7037..399f101f49ae2 100644 --- a/tests/ui/float/target-has-reliable-nightly-float.rs +++ b/tests/ui/float/target-has-reliable-nightly-float.rs @@ -21,7 +21,7 @@ pub fn has_f128_math() {} fn main() { if cfg!(target_arch = "aarch64") && cfg!(target_os = "linux") && - cfg!(not(any(target_env = "musl", target_env = "pauthtest"))) { + cfg!(not(target_env = "musl")) { // Aarch64+GNU+Linux is one target that has support for all features, so use it to spot // check that the compiler does indeed enable these gates. From 4ee476c06e9f8cfa7e89818035abc5793292628c Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Mon, 1 Jun 2026 10:42:39 +0000 Subject: [PATCH 15/38] PR feedback: Introduce --- compiler/rustc_session/src/session.rs | 4 +++- .../platform-support/aarch64-unknown-linux-pauthtest.md | 8 ++++---- src/tools/compiletest/src/directives/directive_names.rs | 4 ++-- ...-aarch64_unknown_linux_pauthtest.rs => pauth-basic.rs} | 0 4 files changed, 9 insertions(+), 7 deletions(-) rename tests/assembly-llvm/{targets/targets-aarch64_unknown_linux_pauthtest.rs => pauth-basic.rs} (100%) diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index b15a1139cc9e0..986dababf770a 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1229,7 +1229,9 @@ fn validate_commandline_args_with_session_available(sess: &Session) { sess.dcx().emit_err(errors::CannotEnableCrtStaticLinux); } - // Using static linking is prohibited on pauthtest target + // FIXME(jchlanda) Pauthtest does not support static linking. It must be dynamically linked, + // with a dynamic linker acting as the ELF interpreter that can resolve pauth relocations and + // enforce pointer authentication constraints. if sess.crt_static(None) && sess.target.cfg_abi == CfgAbi::Pauthtest { sess.dcx().emit_err(errors::CannotEnableCrtStaticPointerAuth); } diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md index 7841bdfcee6d7..558bc409e08d2 100644 --- a/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md +++ b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md @@ -88,8 +88,8 @@ clang \ "$@" ``` -The Rust compiler validates the name of the configured C compiler, so when using -a wrapper its name must contain `clang`. A recommended name is +Bootstrap validates the name of the configured C compiler, so when using a +wrapper its name must contain `clang`. A recommended name is `aarch64-unknown-linux-pauthtest-clang`. Update the script to set `--sysroot`, `-resource-dir`, `--dynamic-linker` and `--rpath` correctly by replacing `` with the directory produced by the build scripts and the @@ -381,7 +381,7 @@ Cross-compilation has been successfully performed on both This target can be tested as normal with `x.py`. The following categories are supported (all present in tree): * Assembly tests - * targets-aarch64_unknown_linux_pauthtest.rs + * pauth-basic.rs * LLVM IR/codegen tests * pauth-extern-c.rs * pauth-extern-c-direct-indirect-call.rs @@ -406,7 +406,7 @@ named for convenience): x.py test --target aarch64-unknown-linux-pauthtest --force-rerun assembly-llvm \ codegen-llvm codegen-units coverage crashes incremental library mir-opt \ run-make ui ui-fulldeps \ - tests/assembly-llvm/targets/targets-aarch64_unknown_linux_pauthtest.rs \ + tests/assembly-llvm/pauth-basic.rs \ tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs \ tests/codegen-llvm/pauth/pauth-extern-c.rs \ tests/codegen-llvm/pauth/pauth-extern-c-direct-indirect-call.rs \ diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs index 786e780b7418b..7c4469afda282 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -49,7 +49,6 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "ignore-aarch64", "ignore-aarch64-pc-windows-msvc", "ignore-aarch64-unknown-linux-gnu", - "ignore-aarch64-unknown-linux-pauthtest", "ignore-aix", "ignore-android", "ignore-apple", @@ -107,6 +106,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "ignore-nvptx64-nvidia-cuda", "ignore-openbsd", "ignore-parallel-frontend", + "ignore-pauthtest", "ignore-powerpc", "ignore-powerpc64", "ignore-remote", @@ -217,7 +217,6 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-aarch64", "only-aarch64-apple-darwin", "only-aarch64-unknown-linux-gnu", - "only-aarch64-unknown-linux-pauthtest", "only-aarch64-unknown-uefi", "only-apple", "only-arm", @@ -249,6 +248,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-musl", "only-nightly", "only-nvptx64", + "only-pauthtest", "only-powerpc", "only-riscv32", "only-riscv64", diff --git a/tests/assembly-llvm/targets/targets-aarch64_unknown_linux_pauthtest.rs b/tests/assembly-llvm/pauth-basic.rs similarity index 100% rename from tests/assembly-llvm/targets/targets-aarch64_unknown_linux_pauthtest.rs rename to tests/assembly-llvm/pauth-basic.rs From 9da0a3b8521731a997626b3af3040b71436b4f6b Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Mon, 1 Jun 2026 12:02:07 +0000 Subject: [PATCH 16/38] PR feedback: Attributes --- compiler/rustc_codegen_llvm/src/base.rs | 4 ++++ tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs | 2 +- .../codegen-llvm/pauth/pauth-extern-c-direct-indirect-call.rs | 2 +- tests/codegen-llvm/pauth/pauth-extern-c.rs | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 949a6bb77c429..6a78ca04a1e8e 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -127,6 +127,10 @@ pub(crate) fn compile_codegen_unit( let mut attrs = attributes::sanitize_attrs(&cx, tcx, SanitizerFnAttrs::default()); // When pointer authentication is enabled, ensure that the ptrauth-* attributes are // also attached to the entry wrapper. + // + // FIXME(jchlanda) If it ever becomes necessary to ensure that all compiler + // generated functions receive the ptrauth-* attributes, `declare_fn` or + // `declare_raw_fn` could be used to provide those. if cx.sess().target.cfg_abi == CfgAbi::Pauthtest { for &ptrauth_attr in pauth_fn_attrs() { attrs.push(llvm::CreateAttrString(cx.llcx, ptrauth_attr)); diff --git a/tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs b/tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs index 0d3d164c51444..882cc4c6db971 100644 --- a/tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs +++ b/tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs @@ -1,4 +1,4 @@ -//@ only-aarch64-unknown-linux-pauthtest +//@ only-pauthtest //@ compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 // Make sure that compiler generated functions (main wrapper and __rust_try) also have ptrauth // attributes set correctly. Rustc only generates __rust_try at O0, so use that opt level for the diff --git a/tests/codegen-llvm/pauth/pauth-extern-c-direct-indirect-call.rs b/tests/codegen-llvm/pauth/pauth-extern-c-direct-indirect-call.rs index aec39c2a3034a..643b428339b73 100644 --- a/tests/codegen-llvm/pauth/pauth-extern-c-direct-indirect-call.rs +++ b/tests/codegen-llvm/pauth/pauth-extern-c-direct-indirect-call.rs @@ -1,6 +1,6 @@ //@ add-minicore // ignore-tidy-linelength -//@ only-aarch64-unknown-linux-pauthtest +//@ only-pauthtest //@ revisions: O0_PAUTH O3_PAUTH //@ [O0_PAUTH] needs-llvm-components: aarch64 diff --git a/tests/codegen-llvm/pauth/pauth-extern-c.rs b/tests/codegen-llvm/pauth/pauth-extern-c.rs index 2c5612cff4be4..ac12729f80699 100644 --- a/tests/codegen-llvm/pauth/pauth-extern-c.rs +++ b/tests/codegen-llvm/pauth/pauth-extern-c.rs @@ -1,5 +1,5 @@ // ignore-tidy-linelength -//@ only-aarch64-unknown-linux-pauthtest +//@ only-pauthtest //@ add-minicore //@ revisions: O0_PAUTH O3_PAUTH O0_PAUTH-ELF-GOT O3_PAUTH-ELF-GOT O0_NO_PAUTH O3_NO_PAUTH From 124bfe437adaf799d41d3226ff1681209a0acc82 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Mon, 1 Jun 2026 10:44:25 +0000 Subject: [PATCH 17/38] PR feedback: Init/Fini --- compiler/rustc_codegen_llvm/src/consts.rs | 2 ++ tests/auxiliary/minicore.rs | 3 +++ tests/codegen-llvm/pauth/pauth-init-fini.rs | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index a8b0d79ae6985..f5e6f45723451 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -170,6 +170,8 @@ fn codegen_static_initializer<'ll, 'tcx>( ) -> Result<(&'ll Value, ConstAllocation<'tcx>), ErrorHandled> { let alloc = cx.tcx.eval_static_initializer(def_id)?; let attrs = cx.tcx.codegen_fn_attrs(def_id); + // FIXME(jchlanda) Decide if this could be better served by `ctor` crate. See the discussion + // here: let is_in_init_fini: IsInitOrFini = attrs .link_section .map(|link_section| { diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index dcf88e7e9e286..e50b9b785ad48 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -8,7 +8,10 @@ //! items. For identical error output, any `diagnostic` attributes (e.g. `on_unimplemented`) //! should also be replicated here. //! - Be careful of adding new features and things that are only available for a subset of targets. +//! - `Sync` is only provided such that the minimal set of impls required by tests is met (not +//! exhaustive covering of all possible function pointer signatures). //! + //! # References //! //! This is partially adapted from `rustc_codegen_cranelift`: diff --git a/tests/codegen-llvm/pauth/pauth-init-fini.rs b/tests/codegen-llvm/pauth/pauth-init-fini.rs index 0f089b22fd73b..db327644d96cf 100644 --- a/tests/codegen-llvm/pauth/pauth-init-fini.rs +++ b/tests/codegen-llvm/pauth/pauth-init-fini.rs @@ -1,6 +1,6 @@ //@ add-minicore // ignore-tidy-linelength -//@ only-aarch64-unknown-linux-pauthtest +//@ only-pauthtest //@ revisions: O0_PAUTH O3_PAUTH //@ [O0_PAUTH] needs-llvm-components: aarch64 From 23a7ecb97cffca9e5b72eaa5b5c2196699488036 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Mon, 1 Jun 2026 10:46:26 +0000 Subject: [PATCH 18/38] PR feedback: Teach bootstrap --- src/bootstrap/src/core/config/target_selection.rs | 2 +- tests/assembly-llvm/asm/aarch64-outline-atomics.rs | 2 +- tests/assembly-llvm/pauth-basic.rs | 2 +- tests/codegen-llvm/cffi/c-variadic.rs | 2 +- tests/codegen-llvm/inline-always-works-always.rs | 2 +- tests/codegen-llvm/issues/issue-73258.rs | 4 ++-- tests/codegen-llvm/pauth/pauth-extern-weak-global.rs | 2 +- tests/run-make/c-link-to-rust-va-list-fn/rmake.rs | 4 ++-- tests/run-make/pauth-quicksort-c-driver/rmake.rs | 2 +- tests/run-make/pauth-quicksort-rust-driver/rmake.rs | 2 +- tests/ui/process/nofile-limit.rs | 2 +- tests/ui/statics/crt-static-pauthtest.rs | 2 +- 12 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/bootstrap/src/core/config/target_selection.rs b/src/bootstrap/src/core/config/target_selection.rs index 5f87e81e2ccb7..365d6eff28ccf 100644 --- a/src/bootstrap/src/core/config/target_selection.rs +++ b/src/bootstrap/src/core/config/target_selection.rs @@ -79,7 +79,7 @@ impl TargetSelection { } pub fn is_pauthtest(&self) -> bool { - self.triple == "aarch64-unknown-linux-pauthtest" + self.contains("pauthtest") } pub fn is_windows(&self) -> bool { diff --git a/tests/assembly-llvm/asm/aarch64-outline-atomics.rs b/tests/assembly-llvm/asm/aarch64-outline-atomics.rs index 690db72b1322b..226f177dfb093 100644 --- a/tests/assembly-llvm/asm/aarch64-outline-atomics.rs +++ b/tests/assembly-llvm/asm/aarch64-outline-atomics.rs @@ -4,7 +4,7 @@ //@ only-linux // aarch64-unknown-linux-pauthtest requires armv8.3-a, which includes Large System Extensions, // providing hardware implementations of atomic operations. -//@ ignore-aarch64-unknown-linux-pauthtest +//@ ignore-pauthtest #![crate_type = "rlib"] diff --git a/tests/assembly-llvm/pauth-basic.rs b/tests/assembly-llvm/pauth-basic.rs index 6dbc4aebf35d9..e240e1317f3a3 100644 --- a/tests/assembly-llvm/pauth-basic.rs +++ b/tests/assembly-llvm/pauth-basic.rs @@ -1,6 +1,6 @@ //@ add-minicore //@ assembly-output: emit-asm -//@ only-aarch64-unknown-linux-pauthtest +//@ only-pauthtest //@ revisions: aarch64_unknown_linux_pauthtest //@ [aarch64_unknown_linux_pauthtest] compile-flags: --target=aarch64-unknown-linux-pauthtest //@ [aarch64_unknown_linux_pauthtest] needs-llvm-components: aarch64 diff --git a/tests/codegen-llvm/cffi/c-variadic.rs b/tests/codegen-llvm/cffi/c-variadic.rs index b609a2720f913..63ed4b211a9a2 100644 --- a/tests/codegen-llvm/cffi/c-variadic.rs +++ b/tests/codegen-llvm/cffi/c-variadic.rs @@ -3,7 +3,7 @@ // Pauthtest generates pointer authentication metadata for call instructions // and wraps function pointers in ConstPtrAuth. Disable this test for this target // to avoid clutter from pointer authentication complexity. -//@ ignore-aarch64-unknown-linux-pauthtest +//@ ignore-pauthtest #![crate_type = "lib"] #![feature(c_variadic)] diff --git a/tests/codegen-llvm/inline-always-works-always.rs b/tests/codegen-llvm/inline-always-works-always.rs index 91a13ab1a7da3..e3dfbc819f9af 100644 --- a/tests/codegen-llvm/inline-always-works-always.rs +++ b/tests/codegen-llvm/inline-always-works-always.rs @@ -3,7 +3,7 @@ //@[SIZE-OPT] compile-flags: -Copt-level=s //@[SPEED-OPT] compile-flags: -Copt-level=3 // Pointer authenticated calls are not guaranteed to be inlined. -//@ ignore-aarch64-unknown-linux-pauthtest +//@ ignore-pauthtest #![crate_type = "rlib"] diff --git a/tests/codegen-llvm/issues/issue-73258.rs b/tests/codegen-llvm/issues/issue-73258.rs index ff4c1aa8a0603..4962c53853aac 100644 --- a/tests/codegen-llvm/issues/issue-73258.rs +++ b/tests/codegen-llvm/issues/issue-73258.rs @@ -3,8 +3,8 @@ #![crate_type = "lib"] // Adapted from -// We explicitly match against `call{{.*}}(` because the aarch64-unknown-linux-pauthtest target -// emits `ptrauth-calls` attribute, which would otherwise make a plain `call` match ambiguous. +// We explicitly match against `call{{.*}}(` because the emitted call may carry attributes (e.g. +// `ptrauth-calls`), which would otherwise make a plain `call` pattern too permissive. #[derive(Clone, Copy)] #[repr(u8)] diff --git a/tests/codegen-llvm/pauth/pauth-extern-weak-global.rs b/tests/codegen-llvm/pauth/pauth-extern-weak-global.rs index d39bfb0118a4b..a83298dd5725c 100644 --- a/tests/codegen-llvm/pauth/pauth-extern-weak-global.rs +++ b/tests/codegen-llvm/pauth/pauth-extern-weak-global.rs @@ -1,5 +1,5 @@ // ignore-tidy-linelength -//@ only-aarch64-unknown-linux-pauthtest +//@ only-pauthtest //@ revisions: O0_PAUTH O3_PAUTH O0_NO_PAUTH O3_NO_PAUTH //@ add-minicore diff --git a/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs b/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs index fb5d5209a17db..7be68faccc714 100644 --- a/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs +++ b/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs @@ -6,8 +6,8 @@ //@ needs-target-std //@ ignore-android: FIXME(#142855) //@ ignore-sgx: (x86 machine code cannot be directly executed) -//@ ignore-aarch64-unknown-linux-pauthtest: (it requires non-trivial compilation of c sources, -// and only supports dynamic linking, ignore the test). +//@ ignore-pauthtest: (it requires non-trivial compilation of c sources, and only supports dynamic +// linking, ignore the test). use run_make_support::{cc, extra_c_flags, run, rustc, static_lib_name}; diff --git a/tests/run-make/pauth-quicksort-c-driver/rmake.rs b/tests/run-make/pauth-quicksort-c-driver/rmake.rs index 26e4b4f1feaf9..3cf978a2f6da5 100644 --- a/tests/run-make/pauth-quicksort-c-driver/rmake.rs +++ b/tests/run-make/pauth-quicksort-c-driver/rmake.rs @@ -4,7 +4,7 @@ // function; while c - provides the implementation of quicksort algorithm and is the user of the // data and comparator. -//@ only-aarch64-unknown-linux-pauthtest +//@ only-pauthtest use run_make_support::{cc, env_var, rfs, run, run_fail, rustc}; diff --git a/tests/run-make/pauth-quicksort-rust-driver/rmake.rs b/tests/run-make/pauth-quicksort-rust-driver/rmake.rs index 7eeb8b2b3944c..e77081d70039c 100644 --- a/tests/run-make/pauth-quicksort-rust-driver/rmake.rs +++ b/tests/run-make/pauth-quicksort-rust-driver/rmake.rs @@ -4,7 +4,7 @@ // In this test rust is the driver - providing the data and the comparison function; while c - // provides the implementation of quicksort algorithm and is the user of the data and comparator. -//@ only-aarch64-unknown-linux-pauthtest +//@ only-pauthtest use run_make_support::{cc, env_var, rfs, run, run_fail, rustc}; diff --git a/tests/ui/process/nofile-limit.rs b/tests/ui/process/nofile-limit.rs index e2f0224625a3a..96b298cc78105 100644 --- a/tests/ui/process/nofile-limit.rs +++ b/tests/ui/process/nofile-limit.rs @@ -10,7 +10,7 @@ //@ ignore-backends: gcc // aarch64-unknown-linux-pauthtest requires dynamic linking, which makes use of file descriptors. // Setting RLIMIT_NOFILE would result in the binary failing even before main is reached. -//@ ignore-aarch64-unknown-linux-pauthtest +//@ ignore-pauthtest #![feature(exit_status_error)] #![feature(rustc_private)] diff --git a/tests/ui/statics/crt-static-pauthtest.rs b/tests/ui/statics/crt-static-pauthtest.rs index 2e2da67cbff13..bc5badc600d88 100644 --- a/tests/ui/statics/crt-static-pauthtest.rs +++ b/tests/ui/statics/crt-static-pauthtest.rs @@ -1,6 +1,6 @@ //@ compile-flags: -C target-feature=+crt-static --target aarch64-unknown-linux-pauthtest //@ needs-llvm-components: aarch64 -//@ only-aarch64-unknown-linux-pauthtest +//@ only-pauthtest #![feature(no_core)] From 88fe032fdcf11ceb8ee437de9b028a8b8547f197 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Mon, 1 Jun 2026 10:47:25 +0000 Subject: [PATCH 19/38] PR feedback: Override static --- compiler/rustc_metadata/src/native_libs.rs | 3 +++ tests/incremental/auxiliary/issue-54059.rs | 2 +- tests/run-make/pauth-static-link-warning/rmake.rs | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index c044f7233cc08..c9e4ce60e1a01 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -198,6 +198,9 @@ pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> Vec } collector.process_command_line(); for lib in &mut collector.libs { + // FIXME(jchlanda) Pauthtest does not support static linking. It must be dynamically linked, + // with a dynamic linker acting as the ELF interpreter that can resolve pauth relocations + // and enforce pointer authentication constraints. if tcx.sess.target.cfg_abi == CfgAbi::Pauthtest { if let NativeLibKind::Static { .. } = lib.kind { if !tcx.sess.opts.unstable_opts.ui_testing { diff --git a/tests/incremental/auxiliary/issue-54059.rs b/tests/incremental/auxiliary/issue-54059.rs index feefe533d7fa9..6bbc94149e822 100644 --- a/tests/incremental/auxiliary/issue-54059.rs +++ b/tests/incremental/auxiliary/issue-54059.rs @@ -40,7 +40,7 @@ proc_macro_expr_impl! { } } -#[link(name = "rust_test_helpers", kind = "static")] +#[link(name="rust_test_helpers")] extern "C" { pub fn rust_dbg_extern_identity_u64(v: u64) -> u64; } diff --git a/tests/run-make/pauth-static-link-warning/rmake.rs b/tests/run-make/pauth-static-link-warning/rmake.rs index 74fe1f0bd6d5d..8d4fb18fe33f9 100644 --- a/tests/run-make/pauth-static-link-warning/rmake.rs +++ b/tests/run-make/pauth-static-link-warning/rmake.rs @@ -2,7 +2,7 @@ // libraries are linked. Test both foreign module linked from #[link] directive and command line // invocations. -//@ only-aarch64-unknown-linux-pauthtest +//@ only-pauthtest // ignore-tidy-linelength use run_make_support::{cc, env_var, regex, run, rustc}; From 52ccc75ef4bd77e0968807af67a118cf40adb2bf Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Mon, 1 Jun 2026 10:48:23 +0000 Subject: [PATCH 20/38] PR feedback: Use target_env --- compiler/rustc_codegen_llvm/src/attributes.rs | 4 ++-- compiler/rustc_codegen_llvm/src/base.rs | 6 +++--- compiler/rustc_codegen_llvm/src/builder.rs | 4 ++-- compiler/rustc_codegen_llvm/src/common.rs | 4 ++-- compiler/rustc_codegen_llvm/src/consts.rs | 6 +++--- compiler/rustc_codegen_llvm/src/intrinsic.rs | 4 ++-- compiler/rustc_target/src/spec/mod.rs | 9 ++++++--- .../src/spec/targets/aarch64_unknown_linux_pauthtest.rs | 3 ++- 8 files changed, 22 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 98a66b89847a8..ee6035359d649 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -13,7 +13,7 @@ use rustc_session::config::{ use rustc_span::sym; use rustc_symbol_mangling::mangle_internal_symbol; use rustc_target::spec::{ - Arch, CfgAbi, FramePointer, SanitizerSet, StackProbeType, StackProtector, + Arch, FramePointer, LlvmAbi, SanitizerSet, StackProbeType, StackProtector, }; use smallvec::SmallVec; @@ -646,7 +646,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( } } - if sess.target.cfg_abi == CfgAbi::Pauthtest { + if sess.target.llvm_abiname == LlvmAbi::Pauthtest { for &ptrauth_attr in pauth_fn_attrs() { to_add.push(llvm::CreateAttrString(cx.llcx, ptrauth_attr)); } diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 6a78ca04a1e8e..bb4ae6e64560a 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -25,7 +25,7 @@ use rustc_middle::mono::Visibility; use rustc_middle::ty::TyCtxt; use rustc_session::config::{DebugInfo, Offload}; use rustc_span::Symbol; -use rustc_target::spec::{CfgAbi, SanitizerSet}; +use rustc_target::spec::{LlvmAbi, SanitizerSet}; use super::ModuleLlvm; use crate::attributes; @@ -131,7 +131,7 @@ pub(crate) fn compile_codegen_unit( // FIXME(jchlanda) If it ever becomes necessary to ensure that all compiler // generated functions receive the ptrauth-* attributes, `declare_fn` or // `declare_raw_fn` could be used to provide those. - if cx.sess().target.cfg_abi == CfgAbi::Pauthtest { + if cx.sess().target.llvm_abiname == LlvmAbi::Pauthtest { for &ptrauth_attr in pauth_fn_attrs() { attrs.push(llvm::CreateAttrString(cx.llcx, ptrauth_attr)); } @@ -152,7 +152,7 @@ pub(crate) fn compile_codegen_unit( cx.add_objc_module_flags(); } - if cx.sess().target.cfg_abi == CfgAbi::Pauthtest { + if cx.sess().target.llvm_abiname == LlvmAbi::Pauthtest { // FIXME(jchlanda): In LLVM/Clang, there are also `aarch64-elf-pauthabi-platform` // and `aarch64-elf-pauthabi-version` module flags. These are emitted into the // PAuth core info section of the resulting ELF, which the linker uses to enforce diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 2973344cb9ee1..5717247351ed6 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -26,7 +26,7 @@ use rustc_sanitizers::{cfi, kcfi}; use rustc_session::config::OptLevel; use rustc_span::Span; use rustc_target::callconv::{FnAbi, PassMode}; -use rustc_target::spec::{Arch, CfgAbi, HasTargetSpec, SanitizerSet, Target}; +use rustc_target::spec::{Arch, HasTargetSpec, LlvmAbi, SanitizerSet, Target}; use smallvec::SmallVec; use tracing::{debug, instrument}; @@ -2042,7 +2042,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { llfn: &'ll Value, fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, ) -> Option> { - if self.sess().target.cfg_abi != CfgAbi::Pauthtest { + if self.sess().target.llvm_abiname != LlvmAbi::Pauthtest { return None; } // Pointer authentication support is currently limited to extern "C" calls; filter out other diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index f1cb891a12f6e..1c26738b7cf9c 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -16,7 +16,7 @@ use rustc_middle::bug; use rustc_middle::mir::interpret::{GlobalAlloc, PointerArithmetic, Scalar}; use rustc_middle::ty::{Instance, TyCtxt}; use rustc_session::cstore::DllImport; -use rustc_target::spec::CfgAbi; +use rustc_target::spec::LlvmAbi; use tracing::debug; use crate::consts::{IsInitOrFini, IsStatic, const_alloc_to_llvm}; @@ -46,7 +46,7 @@ pub(crate) fn maybe_sign_fn_ptr<'ll, 'tcx>( llfn: &'ll llvm::Value, pac: PacMetadata, ) -> &'ll llvm::Value { - if cx.sess().target.cfg_abi != CfgAbi::Pauthtest { + if cx.sess().target.llvm_abiname != LlvmAbi::Pauthtest { return llfn; } diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index f5e6f45723451..a4d52c18c890b 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -17,7 +17,7 @@ use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; use rustc_middle::ty::{self, Instance}; use rustc_middle::{bug, span_bug}; use rustc_span::Symbol; -use rustc_target::spec::{Arch, CfgAbi}; +use rustc_target::spec::{Arch, LlvmAbi}; use tracing::{debug, instrument, trace}; use crate::common::CodegenCx; @@ -123,7 +123,7 @@ pub(crate) fn const_alloc_to_llvm<'ll>( // Under pointer authentication, function pointers stored in init/fini arrays need special // handling. let pac_metadata = Some( - if cx.sess().target.cfg_abi == CfgAbi::Pauthtest + if cx.sess().target.llvm_abiname == LlvmAbi::Pauthtest && matches!(is_init_fini, IsInitOrFini::Yes) { PacMetadata { @@ -221,7 +221,7 @@ fn check_and_apply_linkage<'ll, 'tcx>( let fn_sig = sig.with(*header); let fn_abi = cx.fn_abi_of_fn_ptr(fn_sig, ty::List::empty()); // Decide if the initializer needs to be signed - if cx.sess().target.cfg_abi == CfgAbi::Pauthtest + if cx.sess().target.llvm_abiname == LlvmAbi::Pauthtest && matches!(fn_sig.abi(), ExternAbi::C { .. } | ExternAbi::System { .. }) { should_sign = true; diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 382604ac2cfd5..196d2df9e28de 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -28,7 +28,7 @@ use rustc_session::lint::builtin::DEPRECATED_LLVM_INTRINSIC; use rustc_span::{ErrorGuaranteed, Span, Symbol, sym}; use rustc_symbol_mangling::{mangle_internal_symbol, symbol_name_for_instance_in_crate}; use rustc_target::callconv::PassMode; -use rustc_target::spec::{Arch, CfgAbi}; +use rustc_target::spec::{Arch, LlvmAbi}; use tracing::debug; use crate::abi::FnAbiLlvmExt; @@ -1698,7 +1698,7 @@ fn get_rust_try_fn<'a, 'll, 'tcx>( hir::Safety::Unsafe, )); let rust_try = gen_fn(cx, "__rust_try", rust_fn_sig, codegen); - if cx.sess().target.cfg_abi == CfgAbi::Pauthtest { + if cx.sess().target.llvm_abiname == LlvmAbi::Pauthtest { let attrs: Vec<&Attribute> = pauth_fn_attrs().iter().map(|name| llvm::CreateAttrString(cx.llcx, name)).collect(); let (_ty, rust_try_fn) = rust_try; diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 7925435bc7e3f..f00b6e079c76e 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -2114,6 +2114,8 @@ crate::target_spec_enum! { // PowerPC ElfV1 = "elfv1", ElfV2 = "elfv2", + // Pointer authentication: Pauthtest + Pauthtest = "pauthtest", Unspecified = "", } @@ -3400,9 +3402,10 @@ impl Target { ) } Arch::AArch64 => { - check!( - self.llvm_abiname == LlvmAbi::Unspecified, - "`llvm_abiname` is unused on aarch64" + check_matches!( + self.llvm_abiname, + LlvmAbi::Unspecified | LlvmAbi::Pauthtest, + "invalid llvm ABI for aarch64" ); check!(self.llvm_floatabi.is_none(), "`llvm_floatabi` is unused on aarch64"); // FIXME: Ensure that target_abi = "ilp32" correlates with actually using that ABI. diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_pauthtest.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_pauthtest.rs index ff28391070667..739694ea1509d 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_pauthtest.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_pauthtest.rs @@ -1,5 +1,5 @@ use crate::spec::{ - Arch, CfgAbi, Env, FramePointer, LinkSelfContainedDefault, StackProbeType, Target, + Arch, CfgAbi, Env, FramePointer, LinkSelfContainedDefault, LlvmAbi, StackProbeType, Target, TargetMetadata, TargetOptions, base, }; @@ -19,6 +19,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { env: Env::Musl, cfg_abi: CfgAbi::Pauthtest, + llvm_abiname: LlvmAbi::Pauthtest, // `pauthtest` requires v8.3a, which includes lse, no need for outline-atomics features: "+v8.3a,+pauth".into(), max_atomic_width: Some(128), From f31f522f45c4abb91125d8537919c83df8a5d03b Mon Sep 17 00:00:00 2001 From: khyperia <953151+khyperia@users.noreply.github.com> Date: Sat, 27 Jun 2026 17:49:30 +0200 Subject: [PATCH 21/38] Support DefKind::InlineConst in UnevaluatedConst --- compiler/rustc_ast_lowering/src/lib.rs | 2 +- .../rustc_borrowck/src/universal_regions.rs | 2 +- compiler/rustc_hir/src/def.rs | 37 -------- compiler/rustc_hir_analysis/src/collect.rs | 6 +- .../src/hir_ty_lowering/mod.rs | 12 +-- compiler/rustc_hir_analysis/src/lib.rs | 8 +- compiler/rustc_hir_typeck/src/loops.rs | 5 ++ compiler/rustc_metadata/src/rmeta/encoder.rs | 2 +- compiler/rustc_middle/src/hir/map.rs | 8 +- compiler/rustc_middle/src/ty/context.rs | 9 +- .../src/ty/context/impl_interner.rs | 2 +- compiler/rustc_middle/src/ty/util.rs | 33 +++++++- .../rustc_mir_transform/src/trivial_const.rs | 9 +- compiler/rustc_resolve/src/def_collector.rs | 84 +++++-------------- compiler/rustc_resolve/src/lib.rs | 9 -- compiler/rustc_ty_utils/src/opaque_types.rs | 17 ++-- compiler/rustc_ty_utils/src/sig_types.rs | 38 +++++---- .../mgca/double-inline-const.rs | 11 +++ .../mgca/double-inline-const.stderr | 15 ++++ 19 files changed, 151 insertions(+), 158 deletions(-) create mode 100644 tests/ui/const-generics/mgca/double-inline-const.rs create mode 100644 tests/ui/const-generics/mgca/double-inline-const.stderr diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 06e83a7486100..ceee45db20544 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -2791,7 +2791,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } ExprKind::ConstBlock(anon_const) => { let def_id = self.local_def_id(anon_const.id); - assert_eq!(DefKind::AnonConst, self.tcx.def_kind(def_id)); + assert_eq!(DefKind::InlineConst, self.tcx.def_kind(def_id)); self.lower_anon_const_to_const_arg(anon_const, span) } _ => overly_complex_const(self), diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index 804d2757cf998..b0adbcd6db4ef 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -615,7 +615,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { BodyOwnerKind::Const { .. } | BodyOwnerKind::Static(..) => { match tcx.def_kind(self.mir_def) { - DefKind::InlineConst => { + DefKind::InlineConst if !tcx.is_type_system_inline_const(self.mir_def) => { // This is required for `AscribeUserType` canonical query, which will call // `type_of(inline_const_def_id)`. That `type_of` would inject erased lifetimes // into borrowck, which is ICE #78174. diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index e473db48b04c2..d0300d63d1213 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -448,43 +448,6 @@ impl DefKind { | DefKind::ExternCrate => false, } } - - /// Returns `true` if `self` is a kind of definition that does not have its own - /// type-checking context, i.e. closure, coroutine or inline const. - #[inline] - pub fn is_typeck_child(self) -> bool { - match self { - DefKind::Closure | DefKind::InlineConst | DefKind::SyntheticCoroutineBody => true, - DefKind::Mod - | DefKind::Struct - | DefKind::Union - | DefKind::Enum - | DefKind::Variant - | DefKind::Trait - | DefKind::TyAlias - | DefKind::ForeignTy - | DefKind::TraitAlias - | DefKind::AssocTy - | DefKind::TyParam - | DefKind::Fn - | DefKind::Const { .. } - | DefKind::ConstParam - | DefKind::Static { .. } - | DefKind::Ctor(_, _) - | DefKind::AssocFn - | DefKind::AssocConst { .. } - | DefKind::Macro(_) - | DefKind::ExternCrate - | DefKind::Use - | DefKind::ForeignMod - | DefKind::AnonConst - | DefKind::OpaqueTy - | DefKind::Field - | DefKind::LifetimeParam - | DefKind::GlobalAsm - | DefKind::Impl { .. } => false, - } - } } /// The resolution of a path or export. diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 275fed00a4f8a..05648bf7c2ece 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -15,7 +15,7 @@ //! crate as a kind of pass. This should eventually be factored away. use std::cell::Cell; -use std::{assert_matches, iter}; +use std::{assert_matches, debug_assert_matches, iter}; use rustc_abi::{ExternAbi, Size}; use rustc_ast::Recovered; @@ -1635,10 +1635,12 @@ fn const_param_default<'tcx>( } fn anon_const_kind<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> ty::AnonConstKind { + debug_assert_matches!(tcx.def_kind(def), DefKind::AnonConst | DefKind::InlineConst); let hir_id = tcx.local_def_id_to_hir_id(def); let const_arg_id = tcx.parent_hir_id(hir_id); match tcx.hir_node(const_arg_id) { - hir::Node::ConstArg(_) => { + hir::Node::ConstArg(const_arg) => { + debug_assert_matches!(const_arg.kind, hir::ConstArgKind::Anon(hir::AnonConst { def_id, .. }) if *def_id == def); let parent_hir_node = tcx.hir_node(tcx.parent_hir_id(const_arg_id)); if tcx.features().generic_const_exprs() { ty::AnonConstKind::GCE diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 95a91f1444404..eb9ccb719a84a 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -482,7 +482,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let tcx = self.tcx(); let parent_def_id = self.item_def_id(); if let Res::Def(DefKind::ConstParam, _) = res - && tcx.def_kind(parent_def_id) == DefKind::AnonConst + && matches!(tcx.def_kind(parent_def_id), DefKind::AnonConst | DefKind::InlineConst) && let ty::AnonConstKind::MCG = tcx.anon_const_kind(parent_def_id) { let folder = ForbidParamUsesFolder { @@ -511,15 +511,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Inline consts and closures can be nested inside anon consts that forbid generic // params (e.g. an enum discriminant). Walk up the def parent chain to find the // nearest enclosing AnonConst and use that to determine the context. + let parent_def_id = tcx.typeck_root_def_id(parent_def_id.into()); + let anon_const_def_id = match tcx.def_kind(parent_def_id) { DefKind::AnonConst => parent_def_id, - DefKind::InlineConst | DefKind::Closure => { - let root = tcx.typeck_root_def_id(parent_def_id.into()); - match tcx.def_kind(root) { - DefKind::AnonConst => root.expect_local(), - _ => return None, - } - } + DefKind::InlineConst if tcx.is_type_system_inline_const(parent_def_id) => parent_def_id, _ => return None, }; diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index c5a6ce75c0e41..ff00e18783706 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -186,9 +186,13 @@ pub fn check_crate(tcx: TyCtxt<'_>) { } _ => (), } - // Skip `AnonConst`s because we feed their `type_of`. + // Skip `AnonConst`s and type system `InlineConst`s because we feed their `type_of` in + // `feed_anon_const_type`. // Also skip items for which typeck forwards to parent typeck. - if !(matches!(def_kind, DefKind::AnonConst) || def_kind.is_typeck_child()) { + if !(def_kind == DefKind::AnonConst + || def_kind == DefKind::InlineConst && tcx.is_type_system_inline_const(item_def_id) + || tcx.is_typeck_child(item_def_id.to_def_id())) + { tcx.ensure_ok().typeck(item_def_id); } // Ensure we generate the new `DefId` before finishing `check_crate`. diff --git a/compiler/rustc_hir_typeck/src/loops.rs b/compiler/rustc_hir_typeck/src/loops.rs index 21aad64f58d38..ebd8842bd753f 100644 --- a/compiler/rustc_hir_typeck/src/loops.rs +++ b/compiler/rustc_hir_typeck/src/loops.rs @@ -84,6 +84,11 @@ pub(crate) fn check<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir CheckLoopVisitor { tcx, cx_stack: vec![Normal], block_breaks: Default::default() }; let cx = match tcx.def_kind(def_id) { DefKind::AnonConst => AnonConst, + DefKind::InlineConst => { + // only type system inline consts are typeck roots + debug_assert!(tcx.is_type_system_inline_const(def_id)); + ConstBlock + } _ => Fn, }; check.with_context(cx, |v| v.visit_body(body)); diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index c232f595d4229..0f63c8469953d 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1624,7 +1624,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { <- tcx.explicit_implied_const_bounds(def_id).skip_binder()); } } - if let DefKind::AnonConst = def_kind { + if let DefKind::AnonConst | DefKind::InlineConst = def_kind { record!(self.tables.anon_const_kind[def_id] <- self.tcx.anon_const_kind(def_id)); } if should_encode_const_of_item(self.tcx, def_id, def_kind) { diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index 3cab936c45c1f..a37a9d7b4a4e2 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -22,7 +22,7 @@ use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, with_metavar_spans}; use crate::hir::{ModuleItems, ProjectedMaybeOwner, nested_filter}; use crate::middle::debugger_visualizer::DebuggerVisualizerFile; use crate::query::{IntoQueryKey, LocalCrate}; -use crate::ty::TyCtxt; +use crate::ty::{self, TyCtxt}; /// An iterator that walks up the ancestor tree of a given `HirId`. /// Constructed using `tcx.hir_parent_iter(hir_id)`. @@ -1115,6 +1115,12 @@ impl<'tcx> TyCtxt<'tcx> { } } + pub fn is_type_system_inline_const(self, def_id: impl IntoQueryKey) -> bool { + let def_id = def_id.into_query_key(); + debug_assert_eq!(self.def_kind(def_id), DefKind::InlineConst); + self.anon_const_kind(def_id) != ty::AnonConstKind::NonTypeSystem + } + pub fn hir_maybe_get_struct_pattern_shorthand_field(self, expr: &Expr<'_>) -> Option { let local = match expr { Expr { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 569a1d5786095..3d7aef0b273f1 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -601,7 +601,14 @@ impl<'tcx> TyCtxt<'tcx> { /// effect. However, we do not want this as a general capability, so this interface restricts /// to the only allowed case. pub fn feed_anon_const_type(self, key: LocalDefId, value: ty::EarlyBinder<'tcx, Ty<'tcx>>) { - debug_assert_eq!(self.def_kind(key), DefKind::AnonConst); + if cfg!(debug_assertions) { + match self.def_kind(key) { + DefKind::AnonConst => (), + DefKind::InlineConst => assert!(self.is_type_system_inline_const(key)), + def_kind => bug!("unexpected DefKind in feed_anon_const_type: {def_kind:?}"), + } + } + TyCtxtFeed { tcx: self, key }.type_of(value) } diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 60c2acdb4c4f9..86a6c752d14fe 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -247,7 +247,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { DefKind::OpaqueTy => ty::AliasTermKind::OpaqueTy { def_id }, DefKind::TyAlias => ty::AliasTermKind::FreeTy { def_id }, DefKind::Const { .. } => ty::AliasTermKind::FreeConst { def_id }, - DefKind::AnonConst | DefKind::Ctor(_, CtorKind::Const) => { + DefKind::AnonConst | DefKind::InlineConst | DefKind::Ctor(_, CtorKind::Const) => { ty::AliasTermKind::AnonConst { def_id } } kind => bug!("unexpected DefKind in AliasTy: {kind:?}"), diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index ca3b3f0bbec5c..2c91555e3835f 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -597,7 +597,38 @@ impl<'tcx> TyCtxt<'tcx> { /// Returns `true` if `def_id` refers to a definition that does not have its own /// type-checking context, i.e. closure, coroutine or inline const. pub fn is_typeck_child(self, def_id: DefId) -> bool { - self.def_kind(def_id).is_typeck_child() + match self.def_kind(def_id) { + DefKind::InlineConst => !self.is_type_system_inline_const(def_id), + DefKind::Closure | DefKind::SyntheticCoroutineBody => true, + DefKind::Mod + | DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Variant + | DefKind::Trait + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::TraitAlias + | DefKind::AssocTy + | DefKind::TyParam + | DefKind::Fn + | DefKind::Const { .. } + | DefKind::ConstParam + | DefKind::Static { .. } + | DefKind::Ctor(_, _) + | DefKind::AssocFn + | DefKind::AssocConst { .. } + | DefKind::Macro(_) + | DefKind::ExternCrate + | DefKind::Use + | DefKind::ForeignMod + | DefKind::AnonConst + | DefKind::OpaqueTy + | DefKind::Field + | DefKind::LifetimeParam + | DefKind::GlobalAsm + | DefKind::Impl { .. } => false, + } } /// Returns `true` if `def_id` refers to a trait (i.e., `trait Foo { ... }`). diff --git a/compiler/rustc_mir_transform/src/trivial_const.rs b/compiler/rustc_mir_transform/src/trivial_const.rs index 25d15c12a7b3b..4b66c757729d3 100644 --- a/compiler/rustc_mir_transform/src/trivial_const.rs +++ b/compiler/rustc_mir_transform/src/trivial_const.rs @@ -52,11 +52,10 @@ where F: FnOnce() -> B, B: Deref>, { - if !matches!( - tcx.def_kind(def), - DefKind::AssocConst { .. } | DefKind::Const { .. } | DefKind::AnonConst - ) { - return None; + match tcx.def_kind(def) { + DefKind::AssocConst { .. } | DefKind::Const { .. } | DefKind::AnonConst => (), + DefKind::InlineConst if tcx.is_type_system_inline_const(def) => (), + _ => return None, } // If there are impossible predicates then MIR passes will replace the body with diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index a5049339382f7..de3f8c380fc44 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -17,8 +17,7 @@ use tracing::{debug, instrument}; use crate::macros::MacroRulesScopeRef; use crate::{ - ConstArgContext, ImplTraitContext, InvocationParent, ParentScope, Resolver, with_owner, - with_owner_tables, + ImplTraitContext, InvocationParent, ParentScope, Resolver, with_owner, with_owner_tables, }; pub(crate) fn collect_definitions<'ra>( @@ -115,12 +114,6 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { self.invocation_parent.impl_trait_context = orig_itc; } - fn with_const_arg(&mut self, ctxt: ConstArgContext, f: F) { - let orig = mem::replace(&mut self.invocation_parent.const_arg_context, ctxt); - f(self); - self.invocation_parent.const_arg_context = orig; - } - fn collect_field(&mut self, field: &'a FieldDef, index: Option) { let index = |this: &Self| { index.unwrap_or_else(|| { @@ -430,6 +423,9 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { } fn visit_anon_const(&mut self, constant: &'a AnonConst) { + // Note that `visit_anon_const` is skipped for AnonConst nodes wrapped in an + // ExprKind::ConstBlock - these are handled in visit_expr, and are DefKind::InlineConst. + // `MgcaDisambiguation::Direct` is set even when MGCA is disabled, so // to avoid affecting stable we have to feature gate the not creating // anon consts @@ -441,16 +437,12 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { } match constant.mgca_disambiguation { - MgcaDisambiguation::Direct => self.with_const_arg(ConstArgContext::Direct, |this| { - visit::walk_anon_const(this, constant); - }), + MgcaDisambiguation::Direct => visit::walk_anon_const(self, constant), MgcaDisambiguation::AnonConst => { - self.with_const_arg(ConstArgContext::NonDirect, |this| { - let parent = this - .create_def(constant.id, None, DefKind::AnonConst, constant.value.span) - .def_id(); - this.with_parent(parent, |this| visit::walk_anon_const(this, constant)); - }) + let parent = self + .create_def(constant.id, None, DefKind::AnonConst, constant.value.span) + .def_id(); + self.with_parent(parent, |this| visit::walk_anon_const(this, constant)); } }; } @@ -459,60 +451,28 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { fn visit_expr(&mut self, expr: &'a Expr) { debug!(?self.invocation_parent); - let parent_def = match &expr.kind { + match &expr.kind { ExprKind::MacCall(..) => { self.visit_macro_invoc(expr.id); self.visit_invoc(expr.id); - return; } ExprKind::Closure(..) | ExprKind::Gen(..) => { - self.create_def(expr.id, None, DefKind::Closure, expr.span).def_id() + let def = self.create_def(expr.id, None, DefKind::Closure, expr.span).def_id(); + self.with_parent(def, |this| visit::walk_expr(this, expr)); } ExprKind::ConstBlock(constant) => { - // Under `min_generic_const_args` a `const { }` block sometimes - // corresponds to an anon const rather than an inline const. - let def_kind = match self.invocation_parent.const_arg_context { - ConstArgContext::Direct => DefKind::AnonConst, - ConstArgContext::NonDirect => DefKind::InlineConst, - }; - - return self.with_const_arg(ConstArgContext::NonDirect, |this| { - for attr in &expr.attrs { - visit::walk_attribute(this, attr); - } - - let def = - this.create_def(constant.id, None, def_kind, constant.value.span).def_id(); - this.with_parent(def, |this| visit::walk_anon_const(this, constant)); - }); - } - - // Avoid overwriting `const_arg_context` as we may want to treat const blocks - // as being anon consts if we are inside a const argument. - ExprKind::Struct(_) | ExprKind::Call(..) | ExprKind::Tup(..) | ExprKind::Array(..) => { - return visit::walk_expr(self, expr); - } - // FIXME(mgca): we may want to handle block labels in some manner - ExprKind::Block(block, _) if let [stmt] = block.stmts.as_slice() => match stmt.kind { - // FIXME(mgca): this probably means that mac calls that expand - // to semi'd const blocks are handled differently to just writing - // out a semi'd const block. - StmtKind::Expr(..) | StmtKind::MacCall(..) => return visit::walk_expr(self, expr), - - // Fallback to normal behaviour - StmtKind::Let(..) | StmtKind::Item(..) | StmtKind::Semi(..) | StmtKind::Empty => { - self.invocation_parent.parent_def + for attr in &expr.attrs { + visit::walk_attribute(self, attr); } - }, - _ => self.invocation_parent.parent_def, - }; - - self.with_const_arg(ConstArgContext::NonDirect, |this| { - // Note in some cases the `parent_def` here may be the existing parent - // and this is actually a no-op `with_parent` call. - this.with_parent(parent_def, |this| visit::walk_expr(this, expr)) - }) + let def = self + .create_def(constant.id, None, DefKind::InlineConst, constant.value.span) + .def_id(); + // use specifically walk_anon_const, not walk_expr, to skip self.visit_anon_const + self.with_parent(def, |this| visit::walk_anon_const(this, constant)); + } + _ => visit::walk_expr(self, expr), + } } fn visit_ty(&mut self, ty: &'a Ty) { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index a20a760f229ae..e7fdfcde0d572 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -186,7 +186,6 @@ struct InvocationParent { parent_def: LocalDefId, impl_trait_context: ImplTraitContext, in_attr: bool, - const_arg_context: ConstArgContext, owner: NodeId, } @@ -195,7 +194,6 @@ impl InvocationParent { parent_def: CRATE_DEF_ID, impl_trait_context: ImplTraitContext::Existential, in_attr: false, - const_arg_context: ConstArgContext::NonDirect, owner: CRATE_NODE_ID, }; } @@ -207,13 +205,6 @@ enum ImplTraitContext { InBinding, } -#[derive(Copy, Clone, Debug)] -enum ConstArgContext { - Direct, - /// Either inside of an `AnonConst` or not inside a const argument at all. - NonDirect, -} - /// Used for tracking import use types which will be used for redundant import checking. /// /// ### Used::Scope Example diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index 5920a1e900d19..bac5d47e98676 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -317,6 +317,10 @@ fn opaque_types_defined_by<'tcx>( tcx: TyCtxt<'tcx>, item: LocalDefId, ) -> &'tcx ty::List { + // Closures and coroutines are type checked with their parent + // Note that we also support `SyntheticCoroutineBody` since we create + // a MIR body for the def kind, and some MIR passes (like promotion) + // may require doing analysis using its typing env. if tcx.is_typeck_child(item.to_def_id()) { return tcx.opaque_types_defined_by(tcx.local_parent(item)); } @@ -332,7 +336,11 @@ fn opaque_types_defined_by<'tcx>( | DefKind::Static { .. } | DefKind::Const { .. } | DefKind::AssocConst { .. } - | DefKind::AnonConst => { + | DefKind::AnonConst + | DefKind::InlineConst => { + // Non-type-system inline consts should be caught by `if tcx.is_typeck_child` above + debug_assert!(kind != DefKind::InlineConst || tcx.is_type_system_inline_const(item)); + collector.collect_taits_declared_in_body(); } DefKind::AssocTy | DefKind::TyAlias | DefKind::GlobalAsm => {} @@ -345,15 +353,8 @@ fn opaque_types_defined_by<'tcx>( | DefKind::Trait | DefKind::ForeignTy | DefKind::TraitAlias - - // Closures and coroutines are type checked with their parent - // Note that we also support `SyntheticCoroutineBody` since we create - // a MIR body for the def kind, and some MIR passes (like promotion) - // may require doing analysis using its typing env. | DefKind::Closure - | DefKind::InlineConst | DefKind::SyntheticCoroutineBody - | DefKind::TyParam | DefKind::ConstParam | DefKind::Ctor(_, _) diff --git a/compiler/rustc_ty_utils/src/sig_types.rs b/compiler/rustc_ty_utils/src/sig_types.rs index ebec1888e6a95..f60a03fa71b26 100644 --- a/compiler/rustc_ty_utils/src/sig_types.rs +++ b/compiler/rustc_ty_utils/src/sig_types.rs @@ -21,6 +21,24 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( ) -> V::Result { let kind = tcx.def_kind(item); trace!(?kind); + let mut visit_alias = || { + if let Some(ty) = tcx.hir_node_by_def_id(item).ty() { + // If the type of the item uses `_`, we're gonna error out anyway, but + // typeck (which type_of invokes below), will call back into opaque_types_defined_by + // causing a cycle. So we just bail out in this case. + if ty.is_suggestable_infer_ty() { + return V::Result::output(); + } + // Associated types in traits don't necessarily have a type that we can visit + try_visit!( + visitor.visit(ty.span, tcx.type_of(item).instantiate_identity().skip_norm_wip()) + ); + } + for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) { + try_visit!(visitor.visit(span, pred.skip_norm_wip())); + } + V::Result::output() + }; match kind { // Walk over the signature of the function DefKind::AssocFn | DefKind::Fn => { @@ -47,24 +65,8 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( | DefKind::Static { .. } | DefKind::Const { .. } | DefKind::AssocConst { .. } - | DefKind::AnonConst => { - if let Some(ty) = tcx.hir_node_by_def_id(item).ty() { - // If the type of the item uses `_`, we're gonna error out anyway, but - // typeck (which type_of invokes below), will call back into opaque_types_defined_by - // causing a cycle. So we just bail out in this case. - if ty.is_suggestable_infer_ty() { - return V::Result::output(); - } - // Associated types in traits don't necessarily have a type that we can visit - try_visit!( - visitor - .visit(ty.span, tcx.type_of(item).instantiate_identity().skip_norm_wip()) - ); - } - for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) { - try_visit!(visitor.visit(span, pred.skip_norm_wip())); - } - } + | DefKind::AnonConst => return visit_alias(), + DefKind::InlineConst if tcx.is_type_system_inline_const(item) => return visit_alias(), DefKind::OpaqueTy => { for (pred, span) in tcx .explicit_item_bounds(item) diff --git a/tests/ui/const-generics/mgca/double-inline-const.rs b/tests/ui/const-generics/mgca/double-inline-const.rs new file mode 100644 index 0000000000000..9bfbd1819c695 --- /dev/null +++ b/tests/ui/const-generics/mgca/double-inline-const.rs @@ -0,0 +1,11 @@ +#![feature(min_generic_const_args)] + +struct S; + +impl S { + const Q: usize = 2; + fn foo(_: S<{ const { const { Self::Q } } }>) {} + //~^ ERROR generic `Self` types are currently not permitted in anonymous constants +} + +fn main() {} diff --git a/tests/ui/const-generics/mgca/double-inline-const.stderr b/tests/ui/const-generics/mgca/double-inline-const.stderr new file mode 100644 index 0000000000000..bc73e3d93575e --- /dev/null +++ b/tests/ui/const-generics/mgca/double-inline-const.stderr @@ -0,0 +1,15 @@ +error: generic `Self` types are currently not permitted in anonymous constants + --> $DIR/double-inline-const.rs:7:35 + | +LL | fn foo(_: S<{ const { const { Self::Q } } }>) {} + | ^^^^ + | +note: not a concrete type + --> $DIR/double-inline-const.rs:5:22 + | +LL | impl S { + | ^^^^ + = help: add `#![feature(generic_const_args)]` to allow generic expressions as the RHS of const items + +error: aborting due to 1 previous error + From 9aaea562307a3b1866ba53d895fc13ab5073ea66 Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Sat, 27 Jun 2026 16:59:46 -0300 Subject: [PATCH 22/38] Recover deferred closure calls after errors --- compiler/rustc_hir_typeck/src/callee.rs | 17 +++++--- .../src/error_reporting/traits/suggestions.rs | 10 +++++ ...rred-closure-call-recovery-issue-157951.rs | 10 +++++ ...-closure-call-recovery-issue-157951.stderr | 39 +++++++++++++++++++ 4 files changed, 70 insertions(+), 6 deletions(-) create mode 100644 tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.rs create mode 100644 tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.stderr diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 57ab29ac752ad..57734d9be582d 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -9,11 +9,11 @@ use rustc_hir::{self as hir, HirId, LangItem, find_attr}; use rustc_hir_analysis::autoderef::Autoderef; use rustc_infer::infer::BoundRegionConversionTime; use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode}; +use rustc_middle::bug; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, }; use rustc_middle::ty::{self, FnSig, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, Unnormalized}; -use rustc_middle::{bug, span_bug}; use rustc_span::def_id::LocalDefId; use rustc_span::{Ident, Span, sym}; use rustc_target::spec::{AbiMap, AbiMapping}; @@ -1161,11 +1161,16 @@ impl<'a, 'tcx> DeferredCallResolution<'tcx> { ); } None => { - span_bug!( - self.call_expr.span, - "Expected to find a suitable `Fn`/`FnMut`/`FnOnce` implementation for `{}`", - self.closure_ty - ) + let guar = fcx.tainted_by_errors().unwrap_or_else(|| { + fcx.dcx().span_delayed_bug( + self.call_expr.span, + format!( + "Expected to find a suitable `Fn`/`FnMut`/`FnOnce` implementation for `{}`", + self.closure_ty + ), + ) + }); + fcx.write_resolution(self.call_expr.hir_id, Err(guar)); } } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index c3f4a09b2d431..754fbe24a8464 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -2555,6 +2555,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err.children.clear(); let mut span = obligation.cause.span; + let mut is_async_fn_return = false; if let DefKind::Closure = self.tcx.def_kind(obligation.cause.body_id) && let parent = self.tcx.local_parent(obligation.cause.body_id) && let DefKind::Fn | DefKind::AssocFn = self.tcx.def_kind(parent) @@ -2570,10 +2571,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // and // async fn foo() -> dyn Display Box span = fn_sig.decl.output.span(); + is_async_fn_return = true; err.span(span); } let body = self.tcx.hir_body_owned_by(obligation.cause.body_id); + if !is_async_fn_return + && let Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(closure), .. }) = + self.tcx.hir_node_by_def_id(obligation.cause.body_id) + && matches!(closure.fn_decl.output, hir::FnRetTy::DefaultReturn(_)) + { + return true; + } + let mut visitor = ReturnsVisitor::default(); visitor.visit_body(&body); diff --git a/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.rs b/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.rs new file mode 100644 index 0000000000000..d1e2a25b29521 --- /dev/null +++ b/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.rs @@ -0,0 +1,10 @@ +//@ compile-flags: -Znext-solver=globally +//@ check-fail + +fn main() { + let f = |f: dyn Fn()| f; + //~^ ERROR the size for values of type `(dyn Fn() + 'static)` cannot be known at compilation time + //~| ERROR return type cannot be a trait object without pointer indirection + f(); + //~^ ERROR this function takes 1 argument but 0 arguments were supplied +} diff --git a/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.stderr b/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.stderr new file mode 100644 index 0000000000000..a20e5f331d31a --- /dev/null +++ b/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.stderr @@ -0,0 +1,39 @@ +error[E0277]: the size for values of type `(dyn Fn() + 'static)` cannot be known at compilation time + --> $DIR/deferred-closure-call-recovery-issue-157951.rs:5:17 + | +LL | let f = |f: dyn Fn()| f; + | ^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Fn() + 'static)` + = help: unsized fn params are gated as an unstable feature +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | let f = |f: &dyn Fn()| f; + | + + +error[E0746]: return type cannot be a trait object without pointer indirection + --> $DIR/deferred-closure-call-recovery-issue-157951.rs:5:27 + | +LL | let f = |f: dyn Fn()| f; + | ^ doesn't have a size known at compile-time + +error[E0057]: this function takes 1 argument but 0 arguments were supplied + --> $DIR/deferred-closure-call-recovery-issue-157951.rs:8:5 + | +LL | f(); + | ^-- argument #1 of type `(dyn Fn() + 'static)` is missing + | +note: closure defined here + --> $DIR/deferred-closure-call-recovery-issue-157951.rs:5:13 + | +LL | let f = |f: dyn Fn()| f; + | ^^^^^^^^^^^^^ +help: provide the argument + | +LL | f(/* (dyn Fn() + 'static) */); + | ++++++++++++++++++++++++++ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0057, E0277, E0746. +For more information about an error, try `rustc --explain E0057`. From a89705b027241bcd6a63684da1ab46c587461c2c Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Mon, 22 Jun 2026 10:28:15 -0400 Subject: [PATCH 23/38] Avoid panics bubbling out to proc macros Currently, rustc can emit a FatalError diagnostic during parsing of literals and tokenstreams. These are handled under the hood as a panic, which means that proc-macro code needed to catch_unwind if it wanted to fallibly parse some code. These still emit diagnostics, so in practice this isn't a full fix, but it at least makes the interface on the macro side a bit more uniform. This is primarily motivated by wasm proc macros which can't use catch_unwind and so this lets the test's output be the same with and without them. --- .../rustc_expand/src/proc_macro_server.rs | 21 +++++++----- .../auxiliary/nonfatal-parsing-body.rs | 34 ++++++++++--------- tests/ui/proc-macro/nonfatal-parsing.stderr | 18 +++++----- tests/ui/proc-macro/nonfatal-parsing.stdout | 6 +++- 4 files changed, 45 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 41e550b8d6a3a..65af690611919 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -490,9 +490,11 @@ impl server::Server for Rustc<'_, '_> { fn literal_from_str(&mut self, s: &str) -> Result, String> { let name = FileName::proc_macro_source_code(s); - let mut parser = + let mut parser = rustc_errors::catch_fatal_errors(|| { new_parser_from_source_str(self.psess(), name, s.to_owned(), StripTokens::Nothing) - .map_err(cancel_diags_into_string)?; + }) + .map_err(|_| String::from("failed to parse to literal"))? + .map_err(cancel_diags_into_string)?; let first_span = parser.token.span.data(); let minus_present = parser.eat(exp!(Minus)); @@ -569,12 +571,15 @@ impl server::Server for Rustc<'_, '_> { } fn ts_from_str(&mut self, src: &str) -> Result { - source_str_to_stream( - self.psess(), - FileName::proc_macro_source_code(src), - src.to_string(), - Some(self.call_site), - ) + rustc_errors::catch_fatal_errors(|| { + source_str_to_stream( + self.psess(), + FileName::proc_macro_source_code(src), + src.to_string(), + Some(self.call_site), + ) + }) + .map_err(|_| String::from("failed to parse to tokenstream"))? .map_err(cancel_diags_into_string) } diff --git a/tests/ui/proc-macro/auxiliary/nonfatal-parsing-body.rs b/tests/ui/proc-macro/auxiliary/nonfatal-parsing-body.rs index 258f77067ce9c..b29c40770b0e7 100644 --- a/tests/ui/proc-macro/auxiliary/nonfatal-parsing-body.rs +++ b/tests/ui/proc-macro/auxiliary/nonfatal-parsing-body.rs @@ -7,12 +7,14 @@ use proc_macro::*; use self::Mode::*; // FIXME: all cases should become `NormalOk` or `NormalErr` +// +// And .stderr should be empty (no diagnostics should get emitted from fallible parsing in the proc +// macro). #[derive(PartialEq, Clone, Copy)] enum Mode { NormalOk, NormalErr, OtherError, - OtherWithPanic, } fn print_unspanned(s: &str) -> Result @@ -43,12 +45,11 @@ where assert!(t.is_err()); } OtherError => { - print_unspanned::(s); - } - OtherWithPanic => { - if catch_unwind(|| print_unspanned::(s)).is_ok() { - eprintln!("{s} did not panic"); - } + let t = print_unspanned::(s); + // For now we assert OK here, but in the future this should all move to NormalErr. + // Any case that's failing this should go to NormalErr, probably after verifying that a + // diagnostic did get emitted. + assert!(t.is_ok()); } } } @@ -136,9 +137,9 @@ pub fn run() { // FIXME: all of the cases below should return an Err and emit no diagnostics, but don't yet. // emits diagnostics and returns LexError - lit("r'r'", OtherError); - lit("c'r'", OtherError); - lit("\u{2000}", OtherError); + lit("r'r'", NormalErr); + lit("c'r'", NormalErr); + lit("\u{2000}", NormalErr); // emits diagnostic and returns a seemingly valid tokenstream stream("r'r'", OtherError); @@ -146,8 +147,8 @@ pub fn run() { stream("\u{2000}", OtherError); for parse in [stream as fn(&str, Mode), lit] { - // emits diagnostic(s), then panics - parse("r#", OtherWithPanic); + // emits diagnostic(s), then returns LexError + parse("r#", NormalErr); // emits diagnostic(s), then returns Ok(Literal { kind: ErrWithGuar, .. }) parse("0b2", OtherError); @@ -158,9 +159,10 @@ pub fn run() { "' '", OtherError, ); - parse(&format!("r{0}\"a\"{0}", "#".repeat(256)), OtherWithPanic); - - // emits diagnostic, then, when parsing as a lit, returns LexError, otherwise ErrWithGuar - parse("/*a*/ 0b2 //", OtherError); + parse(&format!("r{0}\"a\"{0}", "#".repeat(256)), NormalErr); } + + // emits diagnostic, then, when parsing as a lit, returns LexError, otherwise ErrWithGuar + lit("/*a*/ 0b2 //", NormalErr); + stream("/*a*/ 0b2 //", OtherError); } diff --git a/tests/ui/proc-macro/nonfatal-parsing.stderr b/tests/ui/proc-macro/nonfatal-parsing.stderr index cb6e243eebc9b..f015794e2ce67 100644 --- a/tests/ui/proc-macro/nonfatal-parsing.stderr +++ b/tests/ui/proc-macro/nonfatal-parsing.stderr @@ -130,15 +130,6 @@ LL | nonfatal_parsing::run!(); | = note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info) -error: invalid digit for a base 2 literal - --> $DIR/nonfatal-parsing.rs:15:5 - | -LL | nonfatal_parsing::run!(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info) - error: found invalid character; only `#` is allowed in raw string delimitation: \u{0} --> :1:1 | @@ -199,6 +190,15 @@ error: invalid digit for a base 2 literal LL | /*a*/ 0b2 // | ^ +error: invalid digit for a base 2 literal + --> $DIR/nonfatal-parsing.rs:15:5 + | +LL | nonfatal_parsing::run!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info) + error: aborting due to 22 previous errors For more information about this error, try `rustc --explain E0768`. diff --git a/tests/ui/proc-macro/nonfatal-parsing.stdout b/tests/ui/proc-macro/nonfatal-parsing.stdout index a46ef66f0f9d0..fcefde22541ba 100644 --- a/tests/ui/proc-macro/nonfatal-parsing.stdout +++ b/tests/ui/proc-macro/nonfatal-parsing.stdout @@ -52,15 +52,19 @@ Err(LexError("not a literal")) Ok(TokenStream [Ident { ident: "r", span: Span }, Literal { kind: Char, symbol: "r", suffix: None, span: Span }]) Ok(TokenStream [Ident { ident: "c", span: Span }, Literal { kind: Char, symbol: "r", suffix: None, span: Span }]) Ok(TokenStream []) +Err(LexError("failed to parse to tokenstream")) Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b2", suffix: None, span: Span }]) Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b", suffix: Some("f32"), span: Span }]) Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b0.0", suffix: Some("f32"), span: Span }]) Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "'''", suffix: None, span: Span }]) Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "'\n'", suffix: None, span: Span }]) -Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b2", suffix: None, span: Span }]) +Err(LexError("failed to parse to tokenstream")) +Err(LexError("failed to parse to literal")) Ok(Literal { kind: ErrWithGuar, symbol: "0b2", suffix: None, span: Span }) Ok(Literal { kind: ErrWithGuar, symbol: "0b", suffix: Some("f32"), span: Span }) Ok(Literal { kind: ErrWithGuar, symbol: "0b0.0", suffix: Some("f32"), span: Span }) Ok(Literal { kind: ErrWithGuar, symbol: "'''", suffix: None, span: Span }) Ok(Literal { kind: ErrWithGuar, symbol: "'\n'", suffix: None, span: Span }) +Err(LexError("failed to parse to literal")) Err(LexError("comment or whitespace around literal")) +Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b2", suffix: None, span: Span }]) From bd938c5bfb48fe83ef8f2f54f562c2394c17d89f Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Sat, 27 Jun 2026 01:13:26 -0400 Subject: [PATCH 24/38] Include default-stability info in rustdoc JSON. --- src/librustdoc/json/conversions.rs | 69 ++++++-- src/rustdoc-json-types/lib.rs | 39 ++++- src/tools/jsondoclint/src/validator.rs | 43 ++++- src/tools/jsondoclint/src/validator/tests.rs | 154 +++++++++++++++++- .../attrs/stability/default_body.rs | 76 +++++++++ 5 files changed, 364 insertions(+), 17 deletions(-) create mode 100644 tests/rustdoc-json/attrs/stability/default_body.rs diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 882220a0cae03..7083d730de58c 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -270,6 +270,18 @@ impl FromClean for Stability { } } +impl FromClean for Box { + fn from_clean(stab: &hir::DefaultBodyStability, _renderer: &JsonRenderer<'_>) -> Self { + let hir::StabilityLevel::Unstable { .. } = stab.level else { + bug!( + "unexpected stable default-body stability, \ + there's no stable equivalent of `#[rustc_default_body_unstable]`" + ) + }; + Box::new(ProvidedDefaultUnstable { feature: stab.feature.to_string() }) + } +} + impl FromClean for Option> { fn from_clean(generic_args: &clean::GenericArgs, renderer: &JsonRenderer<'_>) -> Self { use clean::GenericArgs::*; @@ -353,18 +365,23 @@ fn from_clean_item(item: &clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum EnumItem(e) => ItemEnum::Enum(e.into_json(renderer)), VariantItem(v) => ItemEnum::Variant(v.into_json(renderer)), FunctionItem(f) => { - ItemEnum::Function(from_clean_function(f, true, header.unwrap(), renderer)) + ItemEnum::Function(from_clean_function(f, true, None, header.unwrap(), renderer)) } ForeignFunctionItem(f, _) => { - ItemEnum::Function(from_clean_function(f, false, header.unwrap(), renderer)) + ItemEnum::Function(from_clean_function(f, false, None, header.unwrap(), renderer)) } TraitItem(t) => ItemEnum::Trait(t.into_json(renderer)), TraitAliasItem(t) => ItemEnum::TraitAlias(t.into_json(renderer)), - MethodItem(m, _) => { - ItemEnum::Function(from_clean_function(m, true, header.unwrap(), renderer)) - } + MethodItem(m, _) => ItemEnum::Function(from_clean_function( + m, + true, + default_body_stability_for_def_id(renderer.tcx, item.item_id.expect_def_id()) + .map(|stab| stab.into_json(renderer)), + header.unwrap(), + renderer, + )), RequiredMethodItem(m, _) => { - ItemEnum::Function(from_clean_function(m, false, header.unwrap(), renderer)) + ItemEnum::Function(from_clean_function(m, false, None, header.unwrap(), renderer)) } ImplItem(i) => ItemEnum::Impl(i.into_json(renderer)), StaticItem(s) => ItemEnum::Static(from_clean_static(s, rustc_hir::Safety::Safe, renderer)), @@ -385,23 +402,41 @@ fn from_clean_item(item: &clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum }) } // FIXME(generic_const_items): Add support for generic associated consts. - RequiredAssocConstItem(_generics, ty) => { - ItemEnum::AssocConst { type_: ty.into_json(renderer), value: None } - } + RequiredAssocConstItem(_generics, ty) => ItemEnum::AssocConst { + type_: ty.into_json(renderer), + value: None, + default_unstable: None, + }, // FIXME(generic_const_items): Add support for generic associated consts. - ProvidedAssocConstItem(ci) | ImplAssocConstItem(ci) => ItemEnum::AssocConst { + ProvidedAssocConstItem(ci) => ItemEnum::AssocConst { type_: ci.type_.into_json(renderer), value: Some(ci.kind.expr(renderer.tcx)), + default_unstable: default_body_stability_for_def_id( + renderer.tcx, + item.item_id.expect_def_id(), + ) + .map(|stab| stab.into_json(renderer)), + }, + ImplAssocConstItem(ci) => ItemEnum::AssocConst { + type_: ci.type_.into_json(renderer), + value: Some(ci.kind.expr(renderer.tcx)), + default_unstable: None, }, RequiredAssocTypeItem(g, b) => ItemEnum::AssocType { generics: g.into_json(renderer), bounds: b.into_json(renderer), type_: None, + default_unstable: None, }, AssocTypeItem(t, b) => ItemEnum::AssocType { generics: t.generics.into_json(renderer), bounds: b.into_json(renderer), type_: Some(t.item_type.as_ref().unwrap_or(&t.type_).into_json(renderer)), + default_unstable: default_body_stability_for_def_id( + renderer.tcx, + item.item_id.expect_def_id(), + ) + .map(|stab| stab.into_json(renderer)), }, // `convert_item` early returns `None` for stripped items, keywords, attributes and // "special" macro rules. @@ -815,6 +850,7 @@ impl FromClean for Impl { pub(crate) fn from_clean_function( clean::Function { decl, generics }: &clean::Function, has_body: bool, + default_unstable: Option>, header: rustc_hir::FnHeader, renderer: &JsonRenderer<'_>, ) -> Function { @@ -823,6 +859,7 @@ pub(crate) fn from_clean_function( generics: generics.into_json(renderer), header: header.into_json(renderer), has_body, + default_unstable, } } @@ -972,6 +1009,17 @@ impl FromClean for ItemKind { } } +fn default_body_stability_for_def_id( + tcx: TyCtxt<'_>, + def_id: DefId, +) -> Option { + let stability = tcx.lookup_default_body_stability(def_id)?; + match stability.level { + hir::StabilityLevel::Unstable { .. } => Some(stability), + hir::StabilityLevel::Stable { .. } => None, + } +} + fn const_stability_for_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option { if !tcx.is_conditionally_const(def_id) { // The item cannot be conditionally-const. No const stability here. @@ -1040,6 +1088,7 @@ fn maybe_from_hir_attr(attr: &hir::Attribute, item_id: ItemId, tcx: TyCtxt<'_>) AK::Deprecated { .. } => return Vec::new(), // Handled separately into Item::deprecation. AK::Stability { .. } => return Vec::new(), // Handled separately into Item::stability AK::RustcConstStability { .. } => return Vec::new(), // Handled separately into Item::const_stability. + AK::RustcBodyStability { .. } => return Vec::new(), // Handled separately by `default_unstable`. AK::DocComment { .. } => unreachable!("doc comments stripped out earlier"), diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index e6af2fa04bb10..be4921d888278 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -114,8 +114,8 @@ pub type FxHashMap = HashMap; // re-export for use in src/librustdoc // will instead cause conflicts. See #94591 for more. (This paragraph and the "Latest feature" line // are deliberately not in a doc comment, because they need not be in public docs.) // -// Latest feature: Add `Item::const_stability`. -pub const FORMAT_VERSION: u32 = 59; +// Latest feature: Add default-body stability metadata. +pub const FORMAT_VERSION: u32 = 60; /// The root of the emitted JSON blob. /// @@ -288,6 +288,9 @@ pub struct Item { /// - `#[stable]` and `#[unstable]` attributes: see the [`Self::stability`] field instead. /// - `#[rustc_const_stable]` and `#[rustc_const_unstable]` attributes: /// see the [`Self::const_stability`] field instead. + /// - `#[rustc_default_body_unstable]` attributes: instead see `default_unstable` fields on + /// item kinds that can have unstable default values, such as [`Function::default_unstable`], + /// [`ItemEnum::AssocConst::default_unstable`], and [`ItemEnum::AssocType::default_unstable`]. /// /// Attributes appear in pretty-printed Rust form, regardless of their formatting /// in the original source code. For example: @@ -367,6 +370,19 @@ pub enum StabilityLevel { Unstable, } +/// Information about an unstable default provided by a trait item. +/// +/// Example unstable defaults include: +/// - a stable trait function or method whose body is not stable +/// - a stable trait associated type or const whose default value is not stable +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[cfg_attr(feature = "rkyv_0_8", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "rkyv_0_8", rkyv(derive(Debug)))] +pub struct ProvidedDefaultUnstable { + /// The feature that must be enabled to use the provided default. + pub feature: String, +} + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "rkyv_0_8", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] #[cfg_attr(feature = "rkyv_0_8", rkyv(derive(Debug)))] @@ -379,6 +395,9 @@ pub enum StabilityLevel { /// - `#[stable]` and `#[unstable]`. These are in [`Item::stability`] instead. /// - `#[rustc_const_stable]` and `#[rustc_const_unstable]`. These are in /// [`Item::const_stability`] instead. +/// - `#[rustc_default_body_unstable]`. These are in the `default_unstable` field on the appropriate +/// item kinds: [`Function::default_unstable`], [`ItemEnum::AssocConst::default_unstable`], +/// and [`ItemEnum::AssocType::default_unstable`]. pub enum Attribute { /// `#[non_exhaustive]` NonExhaustive, @@ -875,6 +894,11 @@ pub enum ItemEnum { /// // ^^^^^^^^^^ /// ``` value: Option, + /// Metadata about an unstable default value provided for the associated constant, if any. + /// + /// Empty if the associated constant has no default (see [`ItemEnum::AssocConst::value`]), + /// or if the default value is stable. + default_unstable: Option>, }, /// An associated type of a trait or a type. AssocType { @@ -899,6 +923,11 @@ pub enum ItemEnum { /// ``` #[serde(rename = "type")] type_: Option, + /// Metadata about an unstable default value provided for the associated type, if any. + /// + /// Empty if the associated type has no default (see [`ItemEnum::AssocType::type_`]), + /// or if the default value is stable. + default_unstable: Option>, }, } @@ -1188,6 +1217,12 @@ pub struct Function { pub header: FunctionHeader, /// Whether the function has a body, i.e. an implementation. pub has_body: bool, + /// Metadata about a possible unstable provided default implementation for trait methods. + /// + /// Only populated for function items inside traits. Empty if the trait method + /// does not have a default implementation (see [`Function::has_body`]), + /// or if its default implementation is stable. + pub default_unstable: Option>, } /// Generic parameters accepted by an item and `where` clauses imposed on it and the parameters. diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index 01deafe20b354..76a47c4f4fecf 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -97,7 +97,7 @@ impl<'a> Validator<'a> { ItemEnum::StructField(x) => self.check_struct_field(x), ItemEnum::Enum(x) => self.check_enum(x), ItemEnum::Variant(x) => self.check_variant(x, id), - ItemEnum::Function(x) => self.check_function(x), + ItemEnum::Function(x) => self.check_function(x, id), ItemEnum::Trait(x) => self.check_trait(x, id), ItemEnum::TraitAlias(x) => self.check_trait_alias(x), ItemEnum::Impl(x) => self.check_impl(x, id), @@ -114,12 +114,35 @@ impl<'a> Validator<'a> { ItemEnum::Module(x) => self.check_module(x, id), // FIXME: Why don't these have their own structs? ItemEnum::ExternCrate { .. } => {} - ItemEnum::AssocConst { type_, value: _ } => self.check_type(type_), - ItemEnum::AssocType { generics, bounds, type_ } => { + ItemEnum::AssocConst { type_, value, default_unstable } => { + self.check_type(type_); + if value.is_none() + && let Some(default_unstable) = default_unstable + { + self.fail( + id, + ErrorKind::Custom(format!( + "`default_unstable` must be `None` when `value` is `None`, but \ + assoc const id {} had `default_unstable` with feature `{}`", + id.0, default_unstable.feature + )), + ); + } + } + ItemEnum::AssocType { generics, bounds, type_, default_unstable } => { self.check_generics(generics); bounds.iter().for_each(|b| self.check_generic_bound(b)); if let Some(ty) = type_ { self.check_type(ty); + } else if let Some(default_unstable) = default_unstable { + self.fail( + id, + ErrorKind::Custom(format!( + "`default_unstable` must be `None` when `type_` is `None`, but \ + assoc type id {} had `default_unstable` with feature `{}`", + id.0, default_unstable.feature + )), + ); } } } @@ -194,9 +217,21 @@ impl<'a> Validator<'a> { } } - fn check_function(&mut self, x: &'a Function) { + fn check_function(&mut self, x: &'a Function, id: &Id) { self.check_generics(&x.generics); self.check_function_signature(&x.sig); + if !x.has_body + && let Some(default_unstable) = &x.default_unstable + { + self.fail( + id, + ErrorKind::Custom(format!( + "`default_unstable` must be `None` when `has_body == false`, but \ + function item id {} had `default_unstable` with feature `{}`", + id.0, default_unstable.feature + )), + ); + } } fn check_trait(&mut self, x: &'a Trait, id: &Id) { diff --git a/src/tools/jsondoclint/src/validator/tests.rs b/src/tools/jsondoclint/src/validator/tests.rs index ff2fae157f04c..f9b54a2cc0936 100644 --- a/src/tools/jsondoclint/src/validator/tests.rs +++ b/src/tools/jsondoclint/src/validator/tests.rs @@ -1,5 +1,7 @@ use rustc_hash::FxHashMap; -use rustdoc_json_types::{Abi, FORMAT_VERSION, FunctionHeader, Item, ItemKind, Visibility}; +use rustdoc_json_types::{ + Abi, FORMAT_VERSION, FunctionHeader, Item, ItemKind, ProvidedDefaultUnstable, Visibility, +}; use super::*; use crate::json_find::SelectorPart; @@ -221,6 +223,7 @@ fn errors_on_missing_path() { abi: Abi::Rust, }, has_body: true, + default_unstable: None, }), }, ), @@ -246,6 +249,155 @@ fn errors_on_missing_path() { ); } +fn krate_with_trait_item(inner: ItemEnum) -> Crate { + let item_id = Id(2); + Crate { + root: Id(0), + crate_version: None, + includes_private: false, + index: FxHashMap::from_iter([ + ( + Id(0), + Item { + id: Id(0), + crate_id: 0, + name: Some("root".to_owned()), + span: None, + visibility: Visibility::Public, + docs: None, + links: FxHashMap::default(), + attrs: Vec::new(), + deprecation: None, + stability: None, + const_stability: None, + inner: ItemEnum::Module(Module { + is_crate: true, + items: vec![Id(1)], + is_stripped: false, + }), + }, + ), + ( + Id(1), + Item { + id: Id(1), + crate_id: 0, + name: Some("Trait".to_owned()), + span: None, + visibility: Visibility::Public, + docs: None, + links: FxHashMap::default(), + attrs: Vec::new(), + deprecation: None, + stability: None, + const_stability: None, + inner: ItemEnum::Trait(Trait { + is_auto: false, + is_unsafe: false, + is_dyn_compatible: true, + items: vec![item_id], + generics: Generics { params: vec![], where_predicates: vec![] }, + bounds: vec![], + implementations: vec![], + }), + }, + ), + ( + item_id, + Item { + id: item_id, + crate_id: 0, + name: Some("TraitItem".to_owned()), + span: None, + visibility: Visibility::Public, + docs: None, + links: FxHashMap::default(), + attrs: Vec::new(), + deprecation: None, + stability: None, + const_stability: None, + inner, + }, + ), + ]), + paths: FxHashMap::default(), + external_crates: FxHashMap::default(), + target: rustdoc_json_types::Target { triple: "".to_string(), target_features: vec![] }, + format_version: FORMAT_VERSION, + } +} + +#[test] +fn errors_on_default_unstable_without_function_body() { + let krate = krate_with_trait_item(ItemEnum::Function(Function { + sig: FunctionSignature { inputs: vec![], output: None, is_c_variadic: false }, + generics: Generics { params: vec![], where_predicates: vec![] }, + header: FunctionHeader { + is_const: false, + is_unsafe: false, + is_async: false, + abi: Abi::Rust, + }, + has_body: false, + default_unstable: Some(Box::new(ProvidedDefaultUnstable { feature: "feature".to_owned() })), + })); + + check( + &krate, + &[Error { + id: Id(2), + kind: ErrorKind::Custom( + "`default_unstable` must be `None` when `has_body == false`, but \ + function item id 2 had `default_unstable` with feature `feature`" + .to_owned(), + ), + }], + ); +} + +#[test] +fn errors_on_default_unstable_without_assoc_const_value() { + let krate = krate_with_trait_item(ItemEnum::AssocConst { + type_: Type::Primitive("usize".to_owned()), + value: None, + default_unstable: Some(Box::new(ProvidedDefaultUnstable { feature: "feature".to_owned() })), + }); + + check( + &krate, + &[Error { + id: Id(2), + kind: ErrorKind::Custom( + "`default_unstable` must be `None` when `value` is `None`, but \ + assoc const id 2 had `default_unstable` with feature `feature`" + .to_owned(), + ), + }], + ); +} + +#[test] +fn errors_on_default_unstable_without_assoc_type_default() { + let krate = krate_with_trait_item(ItemEnum::AssocType { + generics: Generics { params: vec![], where_predicates: vec![] }, + bounds: vec![], + type_: None, + default_unstable: Some(Box::new(ProvidedDefaultUnstable { feature: "feature".to_owned() })), + }); + + check( + &krate, + &[Error { + id: Id(2), + kind: ErrorKind::Custom( + "`default_unstable` must be `None` when `type_` is `None`, but \ + assoc type id 2 had `default_unstable` with feature `feature`" + .to_owned(), + ), + }], + ); +} + #[test] #[should_panic = "LOCAL_CRATE_ID is wrong"] fn checks_local_crate_id_is_correct() { diff --git a/tests/rustdoc-json/attrs/stability/default_body.rs b/tests/rustdoc-json/attrs/stability/default_body.rs new file mode 100644 index 0000000000000..5a70e47d6c505 --- /dev/null +++ b/tests/rustdoc-json/attrs/stability/default_body.rs @@ -0,0 +1,76 @@ +#![feature(staged_api, rustc_attrs, associated_type_defaults)] + +#[stable(feature = "default_body_trait_feature", since = "1.0.0")] +pub trait TraitWithDefaults { + //@ is "$.index[?(@.docs=='method with unstable default body')].inner.function.has_body" true + //@ is "$.index[?(@.docs=='method with unstable default body')].inner.function.default_unstable.feature" '"method_default_body_feature"' + //@ is "$.index[?(@.docs=='method with unstable default body')].attrs" [] + /// method with unstable default body + #[stable(feature = "default_body_method_feature", since = "1.1.0")] + #[rustc_default_body_unstable(feature = "method_default_body_feature", issue = "none")] + fn method_with_unstable_default() {} + + //@ is "$.index[?(@.docs=='required method without default body')].inner.function.has_body" false + //@ is "$.index[?(@.docs=='required method without default body')].inner.function.default_unstable" null + /// required method without default body + #[stable(feature = "required_method_feature", since = "1.2.0")] + fn required_method(); + + //@ is "$.index[?(@.docs=='method with stable default body')].inner.function.has_body" true + //@ is "$.index[?(@.docs=='method with stable default body')].inner.function.default_unstable" null + /// method with stable default body + #[stable(feature = "stable_default_method_feature", since = "1.3.0")] + fn method_with_stable_default() {} + + //@ is "$.index[?(@.docs=='associated constant with unstable default value')].inner.assoc_const.value" '"0"' + //@ is "$.index[?(@.docs=='associated constant with unstable default value')].inner.assoc_const.default_unstable.feature" '"assoc_const_default_value_feature"' + //@ is "$.index[?(@.docs=='associated constant with unstable default value')].attrs" [] + /// associated constant with unstable default value + #[stable(feature = "assoc_const_with_unstable_default_feature", since = "1.4.0")] + #[rustc_default_body_unstable(feature = "assoc_const_default_value_feature", issue = "none")] + const UNSTABLE_DEFAULT_CONST: usize = 0; + + //@ is "$.index[?(@.docs=='required associated constant')].inner.assoc_const.value" null + //@ is "$.index[?(@.docs=='required associated constant')].inner.assoc_const.default_unstable" null + /// required associated constant + #[stable(feature = "required_assoc_const_feature", since = "1.5.0")] + const REQUIRED_CONST: usize; + + //@ is "$.index[?(@.docs=='associated type with unstable default type')].inner.assoc_type.default_unstable.feature" '"assoc_type_default_type_feature"' + //@ is "$.index[?(@.docs=='associated type with unstable default type')].attrs" [] + /// associated type with unstable default type + #[stable(feature = "assoc_type_with_unstable_default_feature", since = "1.6.0")] + #[rustc_default_body_unstable(feature = "assoc_type_default_type_feature", issue = "none")] + type UnstableDefaultType = usize; + + //@ is "$.index[?(@.docs=='required associated type')].inner.assoc_type.type" null + //@ is "$.index[?(@.docs=='required associated type')].inner.assoc_type.default_unstable" null + /// required associated type + #[stable(feature = "required_assoc_type_feature", since = "1.7.0")] + type RequiredType; +} + +#[stable(feature = "default_body_impl_target_feature", since = "2.0.0")] +pub struct ImplTarget; + +// Impl items provide their own definitions, so they do not use the trait's unstable defaults. +#[stable(feature = "default_body_impl_feature", since = "2.1.0")] +impl TraitWithDefaults for ImplTarget { + //@ is "$.index[?(@.docs=='impl override for unstable default body')].inner.function.default_unstable" null + /// impl override for unstable default body + fn method_with_unstable_default() {} + + fn required_method() {} + + //@ is "$.index[?(@.docs=='impl override for unstable default value')].inner.assoc_const.default_unstable" null + /// impl override for unstable default value + const UNSTABLE_DEFAULT_CONST: usize = 1; + + const REQUIRED_CONST: usize = 2; + + //@ is "$.index[?(@.docs=='impl override for unstable default type')].inner.assoc_type.default_unstable" null + /// impl override for unstable default type + type UnstableDefaultType = u8; + + type RequiredType = (); +} From e101c8d3f303631d167164a3bc3c65f5ba0ddab3 Mon Sep 17 00:00:00 2001 From: Vastargazing Date: Wed, 6 May 2026 12:50:17 +0300 Subject: [PATCH 25/38] tests: check wasm compiler_builtins object architecture Add a run-make test that checks the `.o` members in the prebuilt `libcompiler_builtins` archive for `wasm32-wasip1` are wasm objects. This guards against building wasm compiler-rt fallbacks with the host C toolchain. --- src/tools/run-make-support/Cargo.toml | 2 +- .../rmake.rs | 54 +++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 tests/run-make/wasm-compiler-builtins-object-arch/rmake.rs diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml index 918f5ef0d5069..c1bff4f94272c 100644 --- a/src/tools/run-make-support/Cargo.toml +++ b/src/tools/run-make-support/Cargo.toml @@ -13,7 +13,7 @@ edition = "2024" bstr = "1.12" gimli = "0.32" libc = "0.2" -object = "0.37" +object = { version = "0.37", features = ["wasm"] } regex = "1.11" serde_json = "1.0" similar = "2.7" diff --git a/tests/run-make/wasm-compiler-builtins-object-arch/rmake.rs b/tests/run-make/wasm-compiler-builtins-object-arch/rmake.rs new file mode 100644 index 0000000000000..0294308d199f2 --- /dev/null +++ b/tests/run-make/wasm-compiler-builtins-object-arch/rmake.rs @@ -0,0 +1,54 @@ +//! Regression test for . +//! +//! The prebuilt `libcompiler_builtins` rlib bundled in the wasm sysroot must +//! contain wasm object files — never host ELF/Mach-O/COFF. Bootstrap could +//! previously pick the host C toolchain for compiler-rt fallbacks on wasm +//! targets and silently embed host objects into the wasm sysroot +//! (fixed in rust-lang/rust#137457). + +//@ only-wasm32 + +use run_make_support::object::read::Object; +use run_make_support::object::read::archive::ArchiveFile; +use run_make_support::object::{self, Architecture}; +use run_make_support::{has_extension, has_prefix, rfs, rustc, shallow_find_files}; + +fn main() { + let libdir = rustc().print("target-libdir").run().stdout_utf8(); + let libdir = libdir.trim(); + + let rlibs = shallow_find_files(libdir, |path| { + has_prefix(path, "libcompiler_builtins") && has_extension(path, "rlib") + }); + assert!(!rlibs.is_empty(), "no libcompiler_builtins rlib found in {libdir}"); + + let data = rfs::read(&rlibs[0]); + let archive = ArchiveFile::parse(&*data).unwrap(); + + let mut checked = 0usize; + for member in archive.members() { + let member = member.unwrap(); + let name = std::str::from_utf8(member.name()).unwrap_or(""); + if name.ends_with(".rmeta") || name.ends_with(".rmeta-link") { + continue; + } + let obj_data = member.data(&*data).unwrap(); + let obj = object::File::parse(obj_data).unwrap_or_else(|e| { + panic!("failed to parse member `{name}` in compiler_builtins rlib: {e}") + }); + let arch = obj.architecture(); + assert!( + matches!(arch, Architecture::Wasm32 | Architecture::Wasm64), + "object `{name}` in compiler_builtins rlib has architecture {arch:?}, \ + expected wasm — see rust-lang/rust#132802", + ); + checked += 1; + } + + assert!( + checked > 0, + "no object members found in compiler_builtins rlib at {} — \ + archive should always contain object files", + rlibs[0].display(), + ); +} From 7f2574f608d339d42035ef200338af0652b4dd44 Mon Sep 17 00:00:00 2001 From: Justin Schilleman Date: Sun, 28 Jun 2026 22:02:48 +0000 Subject: [PATCH 26/38] Move attribute and keyword docs from `std` to `core` * refactor: move attribute and keywords docs files to core * fix references to `std` * tidy fixes * ignore doc tests w/ explicit_tail_calls * revert `unsafe` example and ignore specifically WASM for `become` doc tests * add explicit note about doube including the docs in `core` and `std` * missed refactoring of doc test ignore * conditionally exclude doc-test containing threading for `wasm-wasip1` * Change exclusion to just `wasi` target_os Co-authored-by: Justin Schilleman <97192655+jschillem@users.noreply.github.com> --- library/{std => core}/src/attribute_docs.rs | 0 library/{std => core}/src/keyword_docs.rs | 59 ++++++++++----------- library/core/src/lib.rs | 13 +++++ library/std/src/lib.rs | 21 ++++---- 4 files changed, 53 insertions(+), 40 deletions(-) rename library/{std => core}/src/attribute_docs.rs (100%) rename library/{std => core}/src/keyword_docs.rs (98%) diff --git a/library/std/src/attribute_docs.rs b/library/core/src/attribute_docs.rs similarity index 100% rename from library/std/src/attribute_docs.rs rename to library/core/src/attribute_docs.rs diff --git a/library/std/src/keyword_docs.rs b/library/core/src/keyword_docs.rs similarity index 98% rename from library/std/src/keyword_docs.rs rename to library/core/src/keyword_docs.rs index 5f94a13dad22a..3b0e3f21a0ac4 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/core/src/keyword_docs.rs @@ -195,7 +195,7 @@ mod break_keyword {} /// to be most things that would be reasonable to have in a constant (barring `const fn`s). For /// example, you can't have a [`File`] as a `const`. /// -/// [`File`]: crate::fs::File +/// [`File`]: ../std/fs/struct.File.html /// /// The only lifetime allowed in a constant is `'static`, which is the lifetime that encompasses /// all others in a Rust program. For example, if you wanted to define a constant string, it would @@ -484,7 +484,7 @@ mod extern_keyword {} #[doc(keyword = "false")] // -/// A value of type [`bool`] representing logical **false**. +/// A value of type [`prim@bool`] representing logical **false**. /// /// `false` is the logical opposite of [`true`]. /// @@ -1060,7 +1060,8 @@ mod mod_keyword {} /// /// `move` is often used when [threads] are involved. /// -/// ```rust +#[cfg_attr(target_os = "wasi", doc = "```rust,ignore (thread::spawn not supported)")] +#[cfg_attr(not(target_os = "wasi"), doc = "```rust")] /// let data = vec![1, 2, 3]; /// /// std::thread::spawn(move || { @@ -1235,31 +1236,18 @@ mod ref_keyword {} /// `return` returns from the function immediately (an "early return"): /// /// ```no_run -/// use std::fs::File; -/// use std::io::{Error, ErrorKind, Read, Result}; +/// fn main() -> Result<(), &'static str> { +/// let contents = "Hello, world!"; /// -/// fn main() -> Result<()> { -/// let mut file = match File::open("foo.txt") { -/// Ok(f) => f, -/// Err(e) => return Err(e), -/// }; +/// if contents.contains("impossible!") { +/// return Err("oh no!"); +/// } /// -/// let mut contents = String::new(); -/// let size = match file.read_to_string(&mut contents) { -/// Ok(s) => s, -/// Err(e) => return Err(e), -/// }; -/// -/// if contents.contains("impossible!") { -/// return Err(Error::new(ErrorKind::Other, "oh no!")); -/// } +/// if contents.len() > 9000 { +/// return Err("over 9000!"); +/// } /// -/// if size > 9000 { -/// return Err(Error::new(ErrorKind::Other, "over 9000!")); -/// } -/// -/// assert_eq!(contents, "Hello, world!"); -/// Ok(()) +/// Ok(()) /// } /// ``` /// @@ -1306,7 +1294,11 @@ mod return_keyword {} /// manner to computed goto). /// /// Example of using `become` to implement functional-style `fold`: -/// ``` +#[cfg_attr( + target_family = "wasm", + doc = "```ignore (tail-call target feature not enabled by default on wasm)" +)] +#[cfg_attr(not(target_family = "wasm"), doc = "```")] /// #![feature(explicit_tail_calls)] /// #![expect(incomplete_features)] /// @@ -1360,7 +1352,11 @@ mod return_keyword {} /// (unless it's coerced to a function pointer) /// /// It is possible to tail-call a function pointer: -/// ``` +#[cfg_attr( + target_family = "wasm", + doc = "```ignore (tail-call target feature not enabled by default on wasm)" +)] +#[cfg_attr(not(target_family = "wasm"), doc = "```")] /// #![feature(explicit_tail_calls)] /// #![expect(incomplete_features)] /// @@ -1631,8 +1627,8 @@ mod self_upper_keyword {} /// [`extern`]: keyword.extern.html /// [`mut`]: keyword.mut.html /// [`unsafe`]: keyword.unsafe.html -/// [`Mutex`]: sync::Mutex -/// [`OnceLock`]: sync::OnceLock +/// [`Mutex`]: ../std/sync/struct.Mutex.html +/// [`OnceLock`]: ../std/sync/struct.OnceLock.html /// [`RefCell`]: cell::RefCell /// [atomic]: sync::atomic /// [Reference]: ../reference/items/static-items.html @@ -1959,7 +1955,7 @@ mod trait_keyword {} #[doc(keyword = "true")] // -/// A value of type [`bool`] representing logical **true**. +/// A value of type [`prim@bool`] representing logical **true**. /// /// Logically `true` is not equal to [`false`]. /// @@ -2312,6 +2308,7 @@ mod type_keyword {} /// [`static`]: keyword.static.html /// [`union`]: keyword.union.html /// [`impl`]: keyword.impl.html +/// [`Vec::set_len`]: ../std/vec/struct.Vec.html#method.set_len /// [raw pointers]: ../reference/types/pointer.html /// [memory safety]: ../book/ch19-01-unsafe-rust.html /// [Rustonomicon]: ../nomicon/index.html @@ -2502,7 +2499,7 @@ mod use_keyword {} /// ``` /// /// `where` is available anywhere generic and lifetime parameters are available, -/// as can be seen with the [`Cow`](crate::borrow::Cow) type from the standard +/// as can be seen with the [`Cow`](../std/borrow/enum.Cow.html) type from the standard /// library: /// /// ```rust diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index a26304c46ecea..124e871a32a3a 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -397,4 +397,17 @@ pub mod simd { pub use crate::core_simd::simd::*; } +// Include private modules that exist solely to provide rustdoc +// documentation for built-in attributes. Using `include!` because rustdoc +// only looks for these modules at the crate level. +include!("attribute_docs.rs"); + +// Include a number of private modules that exist solely to provide +// the rustdoc documentation for the existing keywords. Using `include!` +// because rustdoc only looks for these modules at the crate level. +include!("keyword_docs.rs"); + +// Include a number of private modules that exist solely to provide +// the rustdoc documentation for primitive types. Using `include!` +// because rustdoc only looks for these modules at the crate level. include!("primitive_docs.rs"); diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 1b27be8e2dde3..f24ce720e944f 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -778,20 +778,23 @@ pub mod from { pub use core::from::From; } -// Include a number of private modules that exist solely to provide -// the rustdoc documentation for primitive types. Using `include!` -// because rustdoc only looks for these modules at the crate level. -include!("../../core/src/primitive_docs.rs"); +// We include the following files here *again* (they are already included in libcore) +// so that they show up in search results for the std crate, and to avoid breaking +// existing links: + +// documentation for built-in attributes. Using `include!` because rustdoc +// only looks for these modules at the crate level. +include!("../../core/src/attribute_docs.rs"); // Include a number of private modules that exist solely to provide // the rustdoc documentation for the existing keywords. Using `include!` // because rustdoc only looks for these modules at the crate level. -include!("keyword_docs.rs"); +include!("../../core/src/keyword_docs.rs"); -// Include private modules that exist solely to provide rustdoc -// documentation for built-in attributes. Using `include!` because rustdoc -// only looks for these modules at the crate level. -include!("attribute_docs.rs"); +// Include a number of private modules that exist solely to provide +// the rustdoc documentation for primitive types. Using `include!` +// because rustdoc only looks for these modules at the crate level. +include!("../../core/src/primitive_docs.rs"); // This is required to avoid an unstable error when `restricted-std` is not // enabled. The use of #![feature(restricted_std)] in rustc-std-workspace-std From 1a7439cc0ff575fd39e71e1519b3f4c80b9140f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sat, 20 Jun 2026 11:22:40 +0200 Subject: [PATCH 27/38] Fix `flag_if_supported` in `cc-rs` by configuring an `out_dir` --- src/bootstrap/src/utils/cc_detect.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs index d010226f0dfdb..9524d5fd8672f 100644 --- a/src/bootstrap/src/utils/cc_detect.rs +++ b/src/bootstrap/src/utils/cc_detect.rs @@ -36,6 +36,8 @@ fn new_cc_build(build: &Build, target: TargetSelection) -> cc::Build { .opt_level(2) .warnings(false) .debug(false) + // We have to configure out_dir, otherwise flag_if_supported will not work + .out_dir(build.tempdir().join("cc-rs-out-dir")) // Compress debuginfo .flag_if_supported("-gz") .target(&target.triple) From f832372b5e64a8c82fc703c2825b42325bad4f94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sat, 20 Jun 2026 11:23:07 +0200 Subject: [PATCH 28/38] Slightly refactor cc code --- src/bootstrap/src/utils/cc_detect.rs | 14 ++++++-------- src/bootstrap/src/utils/cc_detect/tests.rs | 4 ++-- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs index 9524d5fd8672f..8a4f991bdafa5 100644 --- a/src/bootstrap/src/utils/cc_detect.rs +++ b/src/bootstrap/src/utils/cc_detect.rs @@ -102,17 +102,15 @@ pub fn fill_target_compiler(build: &mut Build, target: TargetSelection) { let config = build.config.target_config.get(&target); if let Some(cc) = config .and_then(|c| c.cc.clone()) - .or_else(|| default_compiler(&mut cfg, Language::C, target, build)) + .or_else(|| default_compiler(&cfg, Language::C, target, build)) { cfg.compiler(cc); } let compiler = cfg.get_compiler(); - let ar = if let ar @ Some(..) = config.and_then(|c| c.ar.clone()) { - ar - } else { - cfg.try_get_archiver().map(|c| PathBuf::from(c.get_program())).ok() - }; + let ar = config + .and_then(|c| c.ar.clone()) + .or_else(|| cfg.try_get_archiver().map(|c| PathBuf::from(c.get_program())).ok()); build.cc.insert(target, compiler.clone()); let mut cflags = build.cc_handled_clags(target, CLang::C); @@ -124,7 +122,7 @@ pub fn fill_target_compiler(build: &mut Build, target: TargetSelection) { cfg.cpp(true); let cxx_configured = if let Some(cxx) = config .and_then(|c| c.cxx.clone()) - .or_else(|| default_compiler(&mut cfg, Language::CPlusPlus, target, build)) + .or_else(|| default_compiler(&cfg, Language::CPlusPlus, target, build)) { cfg.compiler(cxx); true @@ -160,7 +158,7 @@ pub fn fill_target_compiler(build: &mut Build, target: TargetSelection) { /// Determines the default compiler for a given target and language when not explicitly /// configured in `bootstrap.toml`. fn default_compiler( - cfg: &mut cc::Build, + cfg: &cc::Build, compiler: Language, target: TargetSelection, build: &Build, diff --git a/src/bootstrap/src/utils/cc_detect/tests.rs b/src/bootstrap/src/utils/cc_detect/tests.rs index a6233e6b61c1f..30757a39caa1c 100644 --- a/src/bootstrap/src/utils/cc_detect/tests.rs +++ b/src/bootstrap/src/utils/cc_detect/tests.rs @@ -86,7 +86,7 @@ fn test_default_compiler_wasi() { build.wasi_sdk_path = Some(wasi_sdk.clone()); let mut cfg = cc::Build::new(); - if let Some(result) = default_compiler(&mut cfg, Language::C, target.clone(), &build) { + if let Some(result) = default_compiler(&cfg, Language::C, target.clone(), &build) { let expected = { let compiler = format!("{}-clang", target.triple); wasi_sdk.join("bin").join(compiler) @@ -105,7 +105,7 @@ fn test_default_compiler_fallback() { let build = Build::new(config); let target = TargetSelection::from_user("x86_64-unknown-linux-gnu"); let mut cfg = cc::Build::new(); - let result = default_compiler(&mut cfg, Language::C, target, &build); + let result = default_compiler(&cfg, Language::C, target, &build); assert!(result.is_none(), "default_compiler should return None for generic targets"); } From ae0fb2792bd0e992ed558c3e69ed7357e7406a3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sat, 20 Jun 2026 12:54:03 +0200 Subject: [PATCH 29/38] Add missing zlib library to `x86_64-gnu-distcheck` --- src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile index 5ab44df7a8033..48be0b9f96e9d 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile @@ -28,6 +28,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ xz-utils \ libssl-dev \ pkg-config \ + zlib1g-dev \ && rm -rf /var/lib/apt/lists/* COPY scripts/sccache.sh /scripts/ From 65f06572fb81da89a1a87373aa3e6128060210c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 21 Jun 2026 13:42:16 +0200 Subject: [PATCH 30/38] Add `compress-debuginfo` option to bootstrap and fix debuginfo compression with LLD. --- bootstrap.example.toml | 14 ++++++ src/bootstrap/src/core/builder/cargo.rs | 11 +++-- src/bootstrap/src/core/config/config.rs | 14 +++++- src/bootstrap/src/core/config/mod.rs | 48 ++++++++++++++++++++ src/bootstrap/src/core/config/toml/rust.rs | 5 +- src/bootstrap/src/core/config/toml/target.rs | 4 +- src/bootstrap/src/utils/cc_detect.rs | 12 +++-- src/bootstrap/src/utils/change_tracker.rs | 5 ++ 8 files changed, 103 insertions(+), 10 deletions(-) diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 25e2ca5948bfb..a52d2e945536e 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -697,6 +697,13 @@ # FIXME(#61117): Some tests fail when this option is enabled. #rust.debuginfo-level-tests = 0 +# Compress debuginfo of Rust and C/C++ code. +# Valid options: +# - "off" or false: disable compression +# - true: compress debuginfo with the default compression method (currently zlib) +# - "zlib": compress debuginfo with zlib +#rust.compress-debuginfo = "off" + # Should rustc and the standard library be built with split debuginfo? Default # is platform dependent. # @@ -1019,6 +1026,13 @@ # and enabling it causes issues. #split-debuginfo = if linux || windows-gnu { off } else if windows-msvc { packed } else if apple { unpacked } +# Compress debuginfo for Rust and C/C++ code of this target. +# Valid options: +# - "off" or false: disable compression +# - true: compress debuginfo with the default compression method (currently zlib) +# - "zlib": compress debuginfo with zlib +#compress-debuginfo = "off" + # Path to the `llvm-config` binary of the installation of a custom LLVM to link # against. Note that if this is specified we don't compile LLVM at all for this # target. diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index e7fe8bd1f3858..9122815d8db32 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -5,8 +5,8 @@ use std::path::{Path, PathBuf}; use super::{Builder, Kind}; use crate::core::build_steps::test; use crate::core::build_steps::tool::SourceType; -use crate::core::config::SplitDebuginfo; use crate::core::config::flags::Color; +use crate::core::config::{CompressDebuginfo, SplitDebuginfo}; use crate::utils::build_stamp; use crate::utils::helpers::{self, LldThreads, check_cfg_arg, linker_flags}; use crate::{ @@ -340,8 +340,13 @@ impl Cargo { self.rustdocflags.arg(&arg); } - if !builder.config.dry_run() && builder.cc[&target].args().iter().any(|arg| arg == "-gz") { - self.rustflags.arg("-Clink-arg=-gz"); + if !builder.config.dry_run() { + match builder.config.compress_debuginfo(target) { + CompressDebuginfo::Zlib => { + self.rustflags.arg("-Clink-arg=-Wl,--compress-debug-sections=zlib"); + } + CompressDebuginfo::Off => {} + } } // Ignore linker warnings for now. These are complicated to fix and don't affect the build. diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 5a9c7264c006f..ed5dd9c9ee9fa 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -49,8 +49,8 @@ use crate::core::config::toml::target::{ DefaultLinuxLinkerOverride, Target, TomlTarget, default_linux_linker_overrides, }; use crate::core::config::{ - CompilerBuiltins, DebuginfoLevel, DryRun, GccCiMode, LlvmLibunwind, Merge, ReplaceOpt, - RustcLto, SplitDebuginfo, StringOrBool, threads_from_config, + CompilerBuiltins, CompressDebuginfo, DebuginfoLevel, DryRun, GccCiMode, LlvmLibunwind, Merge, + ReplaceOpt, RustcLto, SplitDebuginfo, StringOrBool, threads_from_config, }; use crate::core::download::{ DownloadContext, download_beta_toolchain, is_download_ci_available, maybe_download_rustfmt, @@ -210,6 +210,7 @@ pub struct Config { pub rust_debuginfo_level_std: DebuginfoLevel, pub rust_debuginfo_level_tools: DebuginfoLevel, pub rust_debuginfo_level_tests: DebuginfoLevel, + pub rust_compress_debuginfo: CompressDebuginfo, pub rust_rpath: bool, pub rust_strip: bool, pub rust_frame_pointers: bool, @@ -550,6 +551,7 @@ impl Config { debuginfo_level_std: rust_debuginfo_level_std, debuginfo_level_tools: rust_debuginfo_level_tools, debuginfo_level_tests: rust_debuginfo_level_tests, + compress_debuginfo: rust_compress_debuginfo, backtrace: rust_backtrace, incremental: rust_incremental, randomize_layout: rust_randomize_layout, @@ -1469,6 +1471,7 @@ impl Config { .unwrap_or(vec![CodegenBackendKind::Llvm]), rust_codegen_units: rust_codegen_units.map(threads_from_config), rust_codegen_units_std: rust_codegen_units_std.map(threads_from_config), + rust_compress_debuginfo: rust_compress_debuginfo.unwrap_or_default(), rust_debug_logging: rust_debug_logging .or(rust_rustc_debug_assertions) .unwrap_or(rust_debug == Some(true)), @@ -1925,6 +1928,13 @@ impl Config { .unwrap_or_else(|| SplitDebuginfo::default_for_platform(target)) } + pub fn compress_debuginfo(&self, target: TargetSelection) -> CompressDebuginfo { + self.target_config + .get(&target) + .and_then(|t| t.compress_debuginfo) + .unwrap_or(self.rust_compress_debuginfo) + } + /// Checks if the given target is the same as the host target. pub fn is_host_target(&self, target: TargetSelection) -> bool { self.host_target == target diff --git a/src/bootstrap/src/core/config/mod.rs b/src/bootstrap/src/core/config/mod.rs index 7651def62672a..c29849cf5a5d6 100644 --- a/src/bootstrap/src/core/config/mod.rs +++ b/src/bootstrap/src/core/config/mod.rs @@ -32,6 +32,7 @@ use std::path::PathBuf; use build_helper::exit; pub use config::*; +use serde::de::Unexpected; use serde::{Deserialize, Deserializer}; use serde_derive::Deserialize; pub use target_selection::TargetSelection; @@ -378,6 +379,53 @@ impl std::str::FromStr for SplitDebuginfo { } } +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)] +pub enum CompressDebuginfo { + Zlib, + #[default] + Off, +} + +impl CompressDebuginfo { + fn default_on() -> Self { + Self::Zlib + } +} + +impl std::str::FromStr for CompressDebuginfo { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "zlib" => Ok(CompressDebuginfo::Zlib), + "off" => Ok(CompressDebuginfo::Off), + _ => Err(()), + } + } +} + +impl<'de> Deserialize<'de> for CompressDebuginfo { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use serde::de::Error; + + Ok(match Deserialize::deserialize(deserializer)? { + StringOrBool::Bool(value) => { + if value { + CompressDebuginfo::default_on() + } else { + CompressDebuginfo::Off + } + } + StringOrBool::String(value) => CompressDebuginfo::from_str(&value).map_err(|_| { + D::Error::invalid_value(Unexpected::Str(&value), &"`zlib` or `off`") + })?, + }) + } +} + /// Describes how to handle conflicts in merging two `TomlConfig` #[derive(Copy, Clone, Debug)] pub enum ReplaceOpt { diff --git a/src/bootstrap/src/core/config/toml/rust.rs b/src/bootstrap/src/core/config/toml/rust.rs index a872671343405..f190eec3761ed 100644 --- a/src/bootstrap/src/core/config/toml/rust.rs +++ b/src/bootstrap/src/core/config/toml/rust.rs @@ -5,7 +5,7 @@ use build_helper::ci::CiEnv; use serde::{Deserialize, Deserializer}; use crate::core::config::toml::TomlConfig; -use crate::core::config::{DebuginfoLevel, Merge, ReplaceOpt, StringOrBool}; +use crate::core::config::{CompressDebuginfo, DebuginfoLevel, Merge, ReplaceOpt, StringOrBool}; use crate::{BTreeSet, CodegenBackendKind, HashSet, PathBuf, TargetSelection, define_config, exit}; define_config! { @@ -28,6 +28,7 @@ define_config! { debuginfo_level_std: Option = "debuginfo-level-std", debuginfo_level_tools: Option = "debuginfo-level-tools", debuginfo_level_tests: Option = "debuginfo-level-tests", + compress_debuginfo: Option = "compress-debuginfo", backtrace: Option = "backtrace", incremental: Option = "incremental", default_linker: Option = "default-linker", @@ -322,6 +323,7 @@ pub fn check_incompatible_options_for_ci_rustc( randomize_layout, debug_logging, debuginfo_level_rustc, + compress_debuginfo, llvm_tools, llvm_bitcode_linker, stack_protector, @@ -389,6 +391,7 @@ pub fn check_incompatible_options_for_ci_rustc( err!(current_rust_config.optimize, optimize, "rust"); err!(current_rust_config.randomize_layout, randomize_layout, "rust"); + err!(current_rust_config.compress_debuginfo, compress_debuginfo, "rust"); err!(current_rust_config.debug_logging, debug_logging, "rust"); err!(current_rust_config.debuginfo_level_rustc, debuginfo_level_rustc, "rust"); err!(current_rust_config.rpath, rpath, "rust"); diff --git a/src/bootstrap/src/core/config/toml/target.rs b/src/bootstrap/src/core/config/toml/target.rs index 847b75e696b47..311d9add9a144 100644 --- a/src/bootstrap/src/core/config/toml/target.rs +++ b/src/bootstrap/src/core/config/toml/target.rs @@ -15,7 +15,8 @@ use serde::de::Error; use serde::{Deserialize, Deserializer}; use crate::core::config::{ - CompilerBuiltins, LlvmLibunwind, Merge, ReplaceOpt, SplitDebuginfo, StringOrBool, + CompilerBuiltins, CompressDebuginfo, LlvmLibunwind, Merge, ReplaceOpt, SplitDebuginfo, + StringOrBool, }; use crate::{CodegenBackendKind, HashSet, PathBuf, define_config, exit}; @@ -68,6 +69,7 @@ pub struct Target { pub default_linker_linux_override: DefaultLinuxLinkerOverride, pub linker: Option, pub split_debuginfo: Option, + pub compress_debuginfo: Option, pub sanitizers: Option, pub profiler: Option, pub rpath: Option, diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs index 8a4f991bdafa5..320dfcc4e69ed 100644 --- a/src/bootstrap/src/utils/cc_detect.rs +++ b/src/bootstrap/src/utils/cc_detect.rs @@ -25,7 +25,7 @@ use std::collections::HashSet; use std::iter; use std::path::{Path, PathBuf}; -use crate::core::config::TargetSelection; +use crate::core::config::{CompressDebuginfo, TargetSelection}; use crate::utils::exec::{BootstrapCommand, command}; use crate::{Build, CLang, GitRepo}; @@ -38,10 +38,16 @@ fn new_cc_build(build: &Build, target: TargetSelection) -> cc::Build { .debug(false) // We have to configure out_dir, otherwise flag_if_supported will not work .out_dir(build.tempdir().join("cc-rs-out-dir")) - // Compress debuginfo - .flag_if_supported("-gz") .target(&target.triple) .host(&build.host_target.triple); + + match build.config.compress_debuginfo(target) { + CompressDebuginfo::Zlib => { + cfg.flag_if_supported("-gz"); + } + CompressDebuginfo::Off => {} + } + match build.crt_static(target) { Some(a) => { cfg.static_crt(a); diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 566ad003ebe01..b34c1b55700f8 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -631,4 +631,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "New `--verbose-run-make-subprocess-output` flag for `x.py test` (defaults to true). Set `--verbose-run-make-subprocess-output=false` to suppress verbose subprocess output for passing run-make tests when using `--no-capture`.", }, + ChangeInfo { + change_id: 158169, + severity: ChangeSeverity::Info, + summary: "New option `rust.compress-debuginfo` allows configuring whether Rust and C/C++ debuginfo should be compressed.", + }, ]; From 10c2c9decb27dcfb846da377269c3e5d991d9866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 21 Jun 2026 13:44:49 +0200 Subject: [PATCH 31/38] Enable debuginfo compression by default in dist builds --- src/bootstrap/defaults/bootstrap.dist.toml | 2 ++ src/ci/run.sh | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/bootstrap/defaults/bootstrap.dist.toml b/src/bootstrap/defaults/bootstrap.dist.toml index cce3f068aabcd..03e012079b079 100644 --- a/src/bootstrap/defaults/bootstrap.dist.toml +++ b/src/bootstrap/defaults/bootstrap.dist.toml @@ -26,6 +26,8 @@ download-rustc = false llvm-bitcode-linker = true # Required to make builds reproducible. remap-debuginfo = true +# Compress debuginfo in generated artifacts to reduce their size +compress-debuginfo = "zlib" [dist] # Use better compression when preparing tarballs. diff --git a/src/ci/run.sh b/src/ci/run.sh index 18b5bf2e2bbb3..883cc20ba0014 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -145,6 +145,8 @@ if [ "$DEPLOY$DEPLOY_ALT" = "1" ]; then fi else RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.remap-debuginfo=false" + # No need to compress debuginfo for tests, and we do not want to depend on zlib + RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.compress-debuginfo=off" # We almost always want debug assertions enabled, but sometimes this takes too # long for too little benefit, so we just turn them off. From cca8cd1736adb36920f6721e540bd9df4298c80f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Mon, 29 Jun 2026 12:32:03 +0200 Subject: [PATCH 32/38] Use doctest attribute `ignore-wasm` instead of manual `cfg_attr` --- library/core/src/keyword_docs.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/library/core/src/keyword_docs.rs b/library/core/src/keyword_docs.rs index 3b0e3f21a0ac4..596765be5e2dd 100644 --- a/library/core/src/keyword_docs.rs +++ b/library/core/src/keyword_docs.rs @@ -1294,11 +1294,8 @@ mod return_keyword {} /// manner to computed goto). /// /// Example of using `become` to implement functional-style `fold`: -#[cfg_attr( - target_family = "wasm", - doc = "```ignore (tail-call target feature not enabled by default on wasm)" -)] -#[cfg_attr(not(target_family = "wasm"), doc = "```")] +/// +/// ```ignore-wasm (tail-call target feature not enabled by default on wasm) /// #![feature(explicit_tail_calls)] /// #![expect(incomplete_features)] /// @@ -1352,11 +1349,8 @@ mod return_keyword {} /// (unless it's coerced to a function pointer) /// /// It is possible to tail-call a function pointer: -#[cfg_attr( - target_family = "wasm", - doc = "```ignore (tail-call target feature not enabled by default on wasm)" -)] -#[cfg_attr(not(target_family = "wasm"), doc = "```")] +/// +/// ```ignore-wasm (tail-call target feature not enabled by default on wasm) /// #![feature(explicit_tail_calls)] /// #![expect(incomplete_features)] /// From 96f3774b0da81331a2c2b129f01dc10428fa2619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 29 Jun 2026 14:35:33 +0200 Subject: [PATCH 33/38] Record step creation locations in bootstrap tracing DOT graph --- src/bootstrap/src/utils/step_graph.rs | 59 +++++++++++++++++++-------- 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/src/bootstrap/src/utils/step_graph.rs b/src/bootstrap/src/utils/step_graph.rs index cffe0dbb3e3a5..d6a2d89482ee4 100644 --- a/src/bootstrap/src/utils/step_graph.rs +++ b/src/bootstrap/src/utils/step_graph.rs @@ -1,10 +1,12 @@ -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::fmt::Debug; use std::io::BufWriter; +use std::panic::Location; use std::path::Path; use crate::core::builder::{AnyDebug, Step, pretty_step_name}; use crate::t; +use crate::utils::tracing::format_location; /// Records the executed steps and their dependencies in a directed graph, /// which can then be rendered into a DOT file for visualization. @@ -20,6 +22,7 @@ pub struct StepGraph { } impl StepGraph { + #[track_caller] pub fn register_step_execution( &mut self, step: &S, @@ -57,6 +60,7 @@ impl StepGraph { } } + #[track_caller] pub fn register_cached_step( &mut self, step: &S, @@ -97,12 +101,18 @@ struct Node { struct NodeHandle(usize); /// Represents a dependency between two bootstrap steps. -#[derive(PartialEq, Eq, Hash, PartialOrd, Ord)] -struct Edge { - src: NodeHandle, - dst: NodeHandle, +#[derive(Default)] +struct EdgeData { // Was the corresponding execution of a step cached, or was the step actually executed? cached: bool, + // Locations from where the step was called + locations: Vec>, +} + +#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Clone)] +struct EdgeKey { + src: NodeHandle, + dst: NodeHandle, } // We could use a library for this, but they either: @@ -114,8 +124,8 @@ struct Edge { #[derive(Default)] struct DotGraph { nodes: Vec, + edges: HashMap, /// The `NodeHandle` represents an index within `self.nodes` - edges: HashSet, key_to_index: HashMap, } @@ -127,16 +137,19 @@ impl DotGraph { handle } + #[track_caller] fn add_edge(&mut self, src: NodeHandle, dst: NodeHandle) { - self.edges.insert(Edge { src, dst, cached: false }); + let key = EdgeKey { src, dst }; + let edge = self.edges.entry(key).or_default(); + edge.locations.push(*Location::caller()); } + #[track_caller] fn add_cached_edge(&mut self, src: NodeHandle, dst: NodeHandle) { - // There's no point in rendering both cached and uncached edge - let uncached = Edge { src, dst, cached: false }; - if !self.edges.contains(&uncached) { - self.edges.insert(Edge { src, dst, cached: true }); - } + let key = EdgeKey { src, dst }; + let edge = self.edges.entry(key).or_default(); + edge.cached = true; + edge.locations.push(*Location::caller()); } fn get_handle_by_key(&self, key: &str) -> Option { @@ -157,11 +170,23 @@ impl DotGraph { )?; } - let mut edges: Vec<&Edge> = self.edges.iter().collect(); - edges.sort(); - for edge in edges { - let style = if edge.cached { "dashed" } else { "solid" }; - writeln!(file, r#"{} -> {} [style="{style}"]"#, edge.src.0, edge.dst.0)?; + let mut edges: Vec<(&EdgeKey, &EdgeData)> = self.edges.iter().collect(); + edges.sort_by_key(|(key, _)| **key); + for (key, data) in edges { + let style = if data.cached { "dashed" } else { "solid" }; + let mut locations = data + .locations + .iter() + .map(|location| format_location(*location)) + .collect::>(); + locations.sort(); + locations.dedup(); + let locations = locations.join(", "); + writeln!( + file, + r#"{} -> {} [style="{style}", tooltip="{locations}"]"#, + key.src.0, key.dst.0, + )?; } writeln!(file, "}}") From e6dc840c594f73aa4ec8e2f0917e997e0628830f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 29 Jun 2026 14:36:25 +0200 Subject: [PATCH 34/38] Add a few `#[track_caller]` annotations to make tracing locations more accurate --- src/bootstrap/src/core/builder/mod.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 048fcc5c80145..2b842121e61fd 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1150,6 +1150,7 @@ impl<'a> Builder<'a> { /// compiler will run on, *not* the target it will build code for). Explicitly does not take /// `Compiler` since all `Compiler` instances are meant to be obtained through this function, /// since it ensures that they are valid (i.e., built and assembled). + #[track_caller] #[cfg_attr( feature = "tracing", instrument( @@ -1183,6 +1184,7 @@ impl<'a> Builder<'a> { /// /// However, without this optimization, we would also build stage 2 rustc for **target1**, /// which is completely wasteful. + #[track_caller] pub fn compiler_for_std(&self, stage: u32) -> Compiler { if compile::Std::should_be_uplifted_from_stage_1(self, stage) { self.compiler(1, self.host_target) @@ -1202,6 +1204,7 @@ impl<'a> Builder<'a> { /// sysroot. /// /// See `force_use_stage1` and `force_use_stage2` for documentation on what each argument is. + #[track_caller] #[cfg_attr( feature = "tracing", instrument( @@ -1249,6 +1252,7 @@ impl<'a> Builder<'a> { /// Prefer using this method rather than manually invoking `Std::new`. /// /// Returns an optional build stamp, if libstd was indeed built. + #[track_caller] #[cfg_attr( feature = "tracing", instrument( @@ -1297,17 +1301,20 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler } } + #[track_caller] pub fn sysroot(&self, compiler: Compiler) -> PathBuf { self.ensure(compile::Sysroot::new(compiler)) } /// Returns the bindir for a compiler's sysroot. + #[track_caller] pub fn sysroot_target_bindir(&self, compiler: Compiler, target: TargetSelection) -> PathBuf { self.ensure(Libdir { compiler, target }).join(target).join("bin") } /// Returns the libdir where the standard library and other artifacts are /// found for a compiler's sysroot. + #[track_caller] pub fn sysroot_target_libdir(&self, compiler: Compiler, target: TargetSelection) -> PathBuf { self.ensure(Libdir { compiler, target }).join(target).join("lib") } @@ -1416,6 +1423,7 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler /// Returns a path to `Rustdoc` that "belongs" to the `target_compiler`. /// It can be either a stage0 rustdoc or a locally built rustdoc that *links* to /// `target_compiler`. + #[track_caller] pub fn rustdoc_for_compiler(&self, target_compiler: Compiler) -> PathBuf { self.ensure(tool::Rustdoc { target_compiler }) } @@ -1532,6 +1540,7 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler /// Ensure that a given step is built, returning its output. This will /// cache the step, so it is safe (and good!) to call this as often as /// needed to ensure that all dependencies are built. + #[track_caller] pub fn ensure(&'a self, step: S) -> S::Output { { let mut stack = self.stack.borrow_mut(); From ee4ea972e086dc1d463235d0b957c97f86ad964d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 29 Jun 2026 14:39:13 +0200 Subject: [PATCH 35/38] Better record locations of executed steps Before it would just show the `ensure` function --- src/bootstrap/src/core/builder/mod.rs | 3 ++- src/bootstrap/src/utils/tracing.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 2b842121e61fd..66e14dae9b9ec 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1598,7 +1598,8 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler // in the step_name field. "step", step_name = pretty_step_name::(), - args = step_debug_args(&step) + args = step_debug_args(&step), + location = crate::utils::tracing::format_location(*std::panic::Location::caller()) ); span.entered() }; diff --git a/src/bootstrap/src/utils/tracing.rs b/src/bootstrap/src/utils/tracing.rs index b1226ed7de7be..4e75347f4cbae 100644 --- a/src/bootstrap/src/utils/tracing.rs +++ b/src/bootstrap/src/utils/tracing.rs @@ -351,7 +351,7 @@ mod inner { let field = &values.fields[0]; write!(writer, " {{{}}}", field.1)?; } - write_location(writer, span.metadata())?; + write_with_location(writer)?; } // Executed command COMMAND_SPAN_TARGET => { From 69f0187c13d890481ecf80e325441cb332265921 Mon Sep 17 00:00:00 2001 From: Yukang Date: Sat, 27 Jun 2026 21:03:04 +0800 Subject: [PATCH 36/38] Avoid building rustdoc for tests without doctests --- src/bootstrap/src/core/builder/cargo.rs | 5 ++++- src/bootstrap/src/core/builder/tests.rs | 13 +++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index e7fe8bd1f3858..b1366ed3aa4d8 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -918,7 +918,10 @@ impl Builder<'_> { } let rustdoc_path = match cmd_kind { - Kind::Doc | Kind::Test | Kind::MiriTest => self.rustdoc_for_compiler(compiler), + Kind::Doc => self.rustdoc_for_compiler(compiler), + Kind::Test | Kind::MiriTest if self.test_target.runs_doctests() => { + self.rustdoc_for_compiler(compiler) + } _ => PathBuf::from("/path/to/nowhere/rustdoc/not/required"), }; diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 1a59e54a95cbf..176a61b15c988 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -2423,6 +2423,19 @@ mod snapshot { } } + #[test] + fn test_library_tests_only_does_not_build_rustdoc() { + let ctx = TestCtx::new(); + let host = TargetSelection::from_user(&host_target()); + insta::assert_snapshot!( + ctx.config("test").args(&["--tests", "library/core"]).render_steps(), + @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + "); + } + #[test] fn test_cargo_stage_1() { let ctx = TestCtx::new(); From c340e8a2ba6ec43522044af10fc03258d9abc462 Mon Sep 17 00:00:00 2001 From: Paul Murphy Date: Wed, 3 Jun 2026 16:40:12 -0500 Subject: [PATCH 37/38] Allow section override when using patchable-function-entries Sometimes it is necessary to group patchable function entrypoint records in distinct linker sections. This is the case for some bpf functions within the linux kernel which shouldn't be visible to ftrace. Extend `-Zpatchable-function-entry` to accept an argument of the form `prefix_nops,total_nops,record_section`, which places all entry record into a user specified section. Likewise, extend the `patchable_function_entry` attribute to accept an optional `section="name"` option to place a function into a specific section. This is made possible by llvm attribute `patchable-function-entry-section` added in llvm 21. --- .../src/attributes/codegen_attrs.rs | 62 +++++++++++-------- .../src/session_diagnostics.rs | 14 +++++ compiler/rustc_codegen_llvm/src/attributes.rs | 32 ++++++++-- compiler/rustc_codegen_llvm/src/context.rs | 8 +-- .../rustc_codegen_ssa/src/codegen_attrs.rs | 6 +- .../rustc_hir/src/attrs/data_structures.rs | 5 +- compiler/rustc_interface/src/tests.rs | 2 +- .../src/middle/codegen_fn_attrs.rs | 25 +++++--- compiler/rustc_session/src/config.rs | 15 ++++- compiler/rustc_session/src/options.rs | 18 +++--- compiler/rustc_span/src/symbol.rs | 1 + .../patchable-function-entry.md | 9 ++- .../patchable-function-entry-both-flags.rs | 32 ++++++++++ .../patchable-function-entry-section.rs | 20 ++++++ tests/ui/attributes/malformed-attrs.stderr | 4 +- .../patchable-function-entry-attribute.rs | 16 +++++ .../patchable-function-entry-attribute.stderr | 60 +++++++++++++++--- .../patchable-function-entry-flags.stderr | 2 +- 18 files changed, 257 insertions(+), 74 deletions(-) create mode 100644 tests/codegen-llvm/patchable-function-entry/patchable-function-entry-section.rs diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index e3865345cae84..f48fbe6324834 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -8,9 +8,9 @@ use rustc_span::edition::Edition::Edition2024; use super::prelude::*; use crate::attributes::AttributeSafety; use crate::session_diagnostics::{ - EmptyExportName, NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass, - NullOnObjcSelector, ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral, - SanitizeInvalidStatic, TargetFeatureOnLangItem, + EmptyExportName, EmptySection, NakedFunctionIncompatibleAttribute, NullOnExport, + NullOnObjcClass, NullOnObjcSelector, NullOnSection, ObjcClassExpectedStringLiteral, + ObjcSelectorExpectedStringLiteral, SanitizeInvalidStatic, TargetFeatureOnLangItem, }; use crate::target_checking::Policy::AllowSilent; @@ -795,7 +795,8 @@ pub(crate) struct PatchableFunctionEntryParser; impl SingleAttributeParser for PatchableFunctionEntryParser { const PATH: &[Symbol] = &[sym::patchable_function_entry]; const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn)]); - const TEMPLATE: AttributeTemplate = template!(List: &["prefix_nops = m, entry_nops = n"]); + const TEMPLATE: AttributeTemplate = + template!(List: &["prefix_nops = m, entry_nops = n, section = \"section\""]); const STABILITY: AttributeStability = unstable!(patchable_function_entry); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { @@ -803,74 +804,85 @@ impl SingleAttributeParser for PatchableFunctionEntryParser { let mut prefix = None; let mut entry = None; + let mut section = None; if meta_item_list.len() == 0 { cx.adcx().expected_at_least_one_argument(meta_item_list.span); return None; } - let mut errored = false; - for item in meta_item_list.mixed() { let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else { - continue; + return None; }; let attrib_to_write = match ident.name { sym::prefix_nops => { // Duplicate prefixes are not allowed if prefix.is_some() { - errored = true; cx.adcx().duplicate_key(ident.span, sym::prefix_nops); - continue; + return None; } &mut prefix } sym::entry_nops => { // Duplicate entries are not allowed if entry.is_some() { - errored = true; cx.adcx().duplicate_key(ident.span, sym::entry_nops); - continue; + return None; } &mut entry } + sym::section => { + // Duplicate entries are not allowed + if section.is_some() { + cx.adcx().duplicate_key(ident.span, sym::section); + return None; + } + // Only a string type value is allowed. + let Some(value_str) = value.value_as_str() else { + cx.adcx().expect_string_literal(value); + return None; + }; + // The section name does not allow null characters. + if value_str.as_str().contains('\0') { + cx.emit_err(NullOnSection { span: value.value_span }); + } + // The section name is not allowed to be empty, LLVM does + // not allow them. + if value_str.is_empty() { + cx.emit_err(EmptySection { span: value.value_span }); + } + section = Some(value_str); + // Integer parsing is not needed, process next item. + continue; + } _ => { - errored = true; cx.adcx().expected_specific_argument( ident.span, &[sym::prefix_nops, sym::entry_nops], ); - continue; + return None; } }; let rustc_ast::LitKind::Int(val, _) = value.value_as_lit().kind else { - errored = true; cx.adcx().expected_integer_literal(value.value_span); - continue; + return None; }; let Ok(val) = val.get().try_into() else { - errored = true; cx.adcx().expected_integer_literal_in_range( value.value_span, u8::MIN as isize, u8::MAX as isize, ); - continue; + return None; }; *attrib_to_write = Some(val); } - if errored { - None - } else { - Some(AttributeKind::PatchableFunctionEntry { - prefix: prefix.unwrap_or(0), - entry: entry.unwrap_or(0), - }) - } + Some(AttributeKind::PatchableFunctionEntry { prefix, entry, section }) } } diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 63e33e0ae4882..9e5d7bb2cf375 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -292,6 +292,13 @@ pub(crate) struct EmptyExportName { pub span: Span, } +#[derive(Diagnostic)] +#[diag("`section` may not be empty")] +pub(crate) struct EmptySection { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag("`export_name` may not contain null characters", code = E0648)] pub(crate) struct NullOnExport { @@ -327,6 +334,13 @@ pub(crate) struct NullOnObjcSelector { pub span: Span, } +#[derive(Diagnostic)] +#[diag("`section` may not contain null characters", code = E0648)] +pub(crate) struct NullOnSection { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag("`objc::class!` expected a string literal")] pub(crate) struct ObjcClassExpectedStringLiteral { diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index fe36a9865485d..20e712174e0e9 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -86,11 +86,26 @@ fn patchable_function_entry_attrs<'ll>( attr: Option, ) -> SmallVec<[&'ll Attribute; 2]> { let mut attrs = SmallVec::new(); - let patchable_spec = attr.unwrap_or_else(|| { - PatchableFunctionEntry::from_config(sess.opts.unstable_opts.patchable_function_entry) - }); - let entry = patchable_spec.entry(); - let prefix = patchable_spec.prefix(); + + let mut entry = sess.opts.unstable_opts.patchable_function_entry.entry(); + let mut prefix = sess.opts.unstable_opts.patchable_function_entry.prefix(); + let mut section = sess.opts.unstable_opts.patchable_function_entry.section(); + let section_sym; + + // Apply attribute specified overrides, if any. + if let Some(patchable_spec) = attr { + if let Some(sym) = patchable_spec.section() { + section_sym = sym; + section = Some(section_sym.as_str()); + } + // Override the nop counts if either is present. If only one is present, the + // other count is implied to be 0. + if patchable_spec.entry().is_some() || patchable_spec.prefix().is_some() { + entry = patchable_spec.entry().unwrap_or(0); + prefix = patchable_spec.prefix().unwrap_or(0); + } + } + if entry > 0 { attrs.push(llvm::CreateAttrStringValue( cx.llcx, @@ -105,6 +120,13 @@ fn patchable_function_entry_attrs<'ll>( &format!("{}", prefix), )); } + if let Some(section) = section { + attrs.push(llvm::CreateAttrStringValue( + cx.llcx, + "patchable-function-entry-section", + section, + )); + } attrs } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 6198a98e5f7ae..80f13a7a35255 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -14,7 +14,6 @@ use rustc_data_structures::base_n::{ALPHANUMERIC_ONLY, ToBaseN}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::small_c_str::SmallCStr; use rustc_hir::def_id::DefId; -use rustc_middle::middle::codegen_fn_attrs::PatchableFunctionEntry; use rustc_middle::mono::CodegenUnit; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTypingEnv, LayoutError, LayoutOfHelpers, @@ -343,14 +342,13 @@ pub(crate) unsafe fn create_module<'ll>( // Add "kcfi-offset" module flag with -Z patchable-function-entry (See // https://reviews.llvm.org/D141172). - let pfe = - PatchableFunctionEntry::from_config(sess.opts.unstable_opts.patchable_function_entry); - if pfe.prefix() > 0 { + let patchable_prefix_nops = sess.opts.unstable_opts.patchable_function_entry.prefix(); + if patchable_prefix_nops > 0 { llvm::add_module_flag_u32( llmod, llvm::ModuleFlagMergeBehavior::Override, "kcfi-offset", - pfe.prefix().into(), + patchable_prefix_nops.into(), ); } diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 4d271447746c1..46bb1182c298c 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -290,9 +290,11 @@ fn process_builtin_attrs( AttributeKind::RustcOffloadKernel => { codegen_fn_attrs.flags |= CodegenFnAttrFlags::OFFLOAD_KERNEL } - AttributeKind::PatchableFunctionEntry { prefix, entry } => { + AttributeKind::PatchableFunctionEntry { prefix, entry, section } => { codegen_fn_attrs.patchable_function_entry = - Some(PatchableFunctionEntry::from_prefix_and_entry(*prefix, *entry)); + Some(PatchableFunctionEntry::from_prefix_entry_and_section( + *prefix, *entry, *section, + )); } AttributeKind::InstrumentFn(instrument_fn) => { codegen_fn_attrs.instrument_fn = match instrument_fn { diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index acae158acb2b4..17d00863d99d5 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1271,8 +1271,9 @@ pub enum AttributeKind { /// Represents `#[patchable_function_entry]` PatchableFunctionEntry { - prefix: u8, - entry: u8, + prefix: Option, + entry: Option, + section: Option, }, /// Represents `#[path]` diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 5933d0fd4b356..a3264d3cc3311 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -866,7 +866,7 @@ fn test_unstable_options_tracking_hash() { tracked!(panic_in_drop, PanicStrategy::Abort); tracked!( patchable_function_entry, - PatchableFunctionEntry::from_total_and_prefix_nops(10, 5) + PatchableFunctionEntry::from_parts(10, 5, None) .expect("total must be greater than or equal to prefix") ); tracked!(plt, Some(true)); diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 713c6597c1966..b6ae4a98a34e3 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -114,7 +114,7 @@ pub struct CodegenFnAttrs { // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity pub alignment: Option, /// The `#[patchable_function_entry(...)]` attribute. Indicates how many nops should be around - /// the function entry. + /// the function entry, or override default section to record entry location. pub patchable_function_entry: Option, /// The `#[rustc_objc_class = "..."]` attribute. pub objc_class: Option, @@ -162,24 +162,33 @@ pub struct TargetFeature { #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, StableHash)] pub struct PatchableFunctionEntry { /// Nops to prepend to the function - prefix: u8, + prefix: Option, /// Nops after entry, but before body - entry: u8, + entry: Option, + /// Optional, specific section to record entry location in + section: Option, } impl PatchableFunctionEntry { - pub fn from_config(config: rustc_session::config::PatchableFunctionEntry) -> Self { - Self { prefix: config.prefix(), entry: config.entry() } + pub fn from_prefix_entry_and_section( + prefix: Option, + entry: Option, + section: Option, + ) -> Self { + Self { prefix, entry, section } } pub fn from_prefix_and_entry(prefix: u8, entry: u8) -> Self { - Self { prefix, entry } + Self { prefix: Some(prefix), entry: Some(entry), section: None } } - pub fn prefix(&self) -> u8 { + pub fn prefix(&self) -> Option { self.prefix } - pub fn entry(&self) -> u8 { + pub fn entry(&self) -> Option { self.entry } + pub fn section(&self) -> Option { + self.section + } } #[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, StableHash)] diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index c7da2d9a1fc38..4492cdf2d2f8a 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -3342,23 +3342,29 @@ impl DumpMonoStatsFormat { /// `-Z patchable-function-entry` representation - how many nops to put before and after function /// entry. -#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)] +#[derive(Clone, PartialEq, Hash, Debug, Default)] pub struct PatchableFunctionEntry { /// Nops before the entry prefix: u8, /// Nops after the entry entry: u8, + /// An optional section name to record the entry location + section: Option, } impl PatchableFunctionEntry { - pub fn from_total_and_prefix_nops( + pub fn from_parts( total_nops: u8, prefix_nops: u8, + section: Option, ) -> Option { if total_nops < prefix_nops { None + // Section name cannot contain null characters. + } else if section.as_ref().map(|x| x.contains('\0') || x.is_empty()).unwrap_or(false) { + None } else { - Some(Self { prefix: prefix_nops, entry: total_nops - prefix_nops }) + Some(Self { prefix: prefix_nops, entry: total_nops - prefix_nops, section }) } } pub fn prefix(&self) -> u8 { @@ -3367,6 +3373,9 @@ impl PatchableFunctionEntry { pub fn entry(&self) -> u8 { self.entry } + pub fn section(&self) -> Option<&str> { + self.section.as_ref().map(|x| x.as_str()) + } } /// `-Zpolonius` values, enabling the borrow checker polonius analysis, and which version: legacy, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index efc8a70f4feb2..b7668bd47d8ec 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -784,7 +784,7 @@ mod desc { pub(crate) const parse_passes: &str = "a space-separated list of passes, or `all`"; pub(crate) const parse_panic_strategy: &str = "either `unwind`, `abort`, or `immediate-abort`"; pub(crate) const parse_on_broken_pipe: &str = "either `kill`, `error`, or `inherit`"; - pub(crate) const parse_patchable_function_entry: &str = "either two comma separated integers (total_nops,prefix_nops), with prefix_nops <= total_nops, or one integer (total_nops)"; + pub(crate) const parse_patchable_function_entry: &str = "a comma separated list of (prefix_nops,total_nops,section_name), (prefix_nops,total_nops), or (total_nops). Where prefix_nops <= total_nops where 0 < total_nops <= 255 and prefix_nops <= total_nops"; pub(crate) const parse_opt_panic_strategy: &str = parse_panic_strategy; pub(crate) const parse_relro_level: &str = "one of: `full`, `partial`, or `off`"; pub(crate) const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `dataflow`, `hwaddress`, `kcfi`, `kernel-address`, `kernel-hwaddress`, `leak`, `memory`, `memtag`, `safestack`, `shadow-call-stack`, `thread`, or 'realtime'"; @@ -1206,20 +1206,24 @@ pub mod parse { ) -> bool { let mut total_nops = 0; let mut prefix_nops = 0; + let mut section = None; if !parse_number(&mut total_nops, v) { - let parts = v.and_then(|v| v.split_once(',')).unzip(); - if !parse_number(&mut total_nops, parts.0) { + let parts: Vec<_> = v.unwrap_or("").split(',').collect(); + if parts.len() < 2 || parts.len() > 3 { return false; } - if !parse_number(&mut prefix_nops, parts.1) { + + if !parse_number(&mut total_nops, Some(parts[0])) { + return false; + } + if !parse_number(&mut prefix_nops, Some(parts[1])) { return false; } + section = parts.get(2).map(|x| x.to_string()); } - if let Some(pfe) = - PatchableFunctionEntry::from_total_and_prefix_nops(total_nops, prefix_nops) - { + if let Some(pfe) = PatchableFunctionEntry::from_parts(total_nops, prefix_nops, section) { *slot = pfe; return true; } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index c75da250efd5f..4bada9712d2a9 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1874,6 +1874,7 @@ symbols! { saturating_sub, sdylib, search_unbox, + section, select_unpredictable, self_in_typedefs, self_struct_ctor, diff --git a/src/doc/unstable-book/src/compiler-flags/patchable-function-entry.md b/src/doc/unstable-book/src/compiler-flags/patchable-function-entry.md index 4a9bf47a29011..80f0e54907872 100644 --- a/src/doc/unstable-book/src/compiler-flags/patchable-function-entry.md +++ b/src/doc/unstable-book/src/compiler-flags/patchable-function-entry.md @@ -2,10 +2,13 @@ -------------------- -The `-Z patchable-function-entry=total_nops,prefix_nops` or `-Z patchable-function-entry=total_nops` +The `-Z patchable-function-entry=total_nops,prefix_nops,record_section`, + `-Z patchable-function-entry=total_nops,prefix_nops`, or + `-Z patchable-function-entry=total_nops` compiler flag enables nop padding of function entries with 'total_nops' nops, with -an offset for the entry of the function at 'prefix_nops' nops. In the second form, -'prefix_nops' defaults to 0. +an offset for the entry of the function at 'prefix_nops' nops. In the third form, +'prefix_nops' defaults to 0. record\_section can specify a specific linker section +to place entry record in, the default is `__patchable_function_entries`. As an illustrative example, `-Z patchable-function-entry=3,2` would produce: diff --git a/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-both-flags.rs b/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-both-flags.rs index 72204c78a4906..45aa46d24fe94 100644 --- a/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-both-flags.rs +++ b/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-both-flags.rs @@ -39,6 +39,26 @@ pub fn fun5() {} #[patchable_function_entry(prefix_nops = 4)] pub fn fun6() {} +// The attribute should override patchable-function-prefix to 4 +// and patchable-function-entry to the default of 0, clearing it entirely, +// while setting patchable-function-entry-section. +#[no_mangle] +#[patchable_function_entry(prefix_nops = 4, section = "foo")] +pub fn fun7() {} + +// The attribute should override patchable-function-entry-section, +// while passing through the commandline options. +#[no_mangle] +#[patchable_function_entry(section = "bar")] +pub fn fun8() {} + +// The attribute should override patchable-function-entry to 5 +// and patchable-function-prefix to the default of 0, clearing it entirely, +// while setting patchable-function-entry-section. +#[no_mangle] +#[patchable_function_entry(entry_nops = 5, section = "baz")] +pub fn fun9() {} + // CHECK: @fun0() unnamed_addr #0 // CHECK: @fun1() unnamed_addr #1 // CHECK: @fun2() unnamed_addr #2 @@ -46,6 +66,9 @@ pub fn fun6() {} // CHECK: @fun4() unnamed_addr #4 // CHECK: @fun5() unnamed_addr #5 // CHECK: @fun6() unnamed_addr #6 +// CHECK: @fun7() unnamed_addr #7 +// CHECK: @fun8() unnamed_addr #8 +// CHECK: @fun9() unnamed_addr #9 // CHECK: attributes #0 = { {{.*}}"patchable-function-entry"="5"{{.*}}"patchable-function-prefix"="10" {{.*}} } // CHECK: attributes #1 = { {{.*}}"patchable-function-entry"="2"{{.*}}"patchable-function-prefix"="1" {{.*}} } @@ -62,3 +85,12 @@ pub fn fun6() {} // CHECK: attributes #6 = { {{.*}}"patchable-function-prefix"="4"{{.*}} } // CHECK-NOT: attributes #6 = { {{.*}}patchable-function-entry{{.*}} } +// +// CHECK: attributes #7 = { {{.*}}"patchable-function-entry-section"="foo"{{.*}}"patchable-function-prefix"="4" {{.*}} } +// CHECK-NOT: attributes #7 = { {{.*}}"patchable-function-entry"{{.*}} } +// +// CHECK: attributes #8 = { {{.*}}"patchable-function-entry-section"="bar"{{.*}} } +// CHECK-NOT: attributes #8 = { {{.*}}"patchable-function-entry"{{.*}} } +// +// CHECK: attributes #9 = { {{.*}}"patchable-function-entry"="5"{{.*}}"patchable-function-entry-section"="baz" {{.*}} } +// CHECK-NOT: attributes #9 = { {{.*}}"patchable-function-prefix{{.*}} } diff --git a/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-section.rs b/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-section.rs new file mode 100644 index 0000000000000..9ffff5ca7537c --- /dev/null +++ b/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-section.rs @@ -0,0 +1,20 @@ +//@ compile-flags: -Z patchable-function-entry=15,10,default_foo_section +// + +#![feature(patchable_function_entry)] +#![crate_type = "lib"] + +// This should have the default, as set by the compile flags +#[no_mangle] +pub fn fun0() {} + +// This should override the default section name +#[no_mangle] +#[patchable_function_entry(section = "bar_section")] +pub fn fun1() {} + +// CHECK: @fun0() unnamed_addr #0 +// CHECK: @fun1() unnamed_addr #1 + +// CHECK: attributes #0 = { {{.*}}"patchable-function-entry"="5"{{.*}}"patchable-function-entry-section"="default_foo_section"{{.*}}"patchable-function-prefix"="10" {{.*}} } +// CHECK: attributes #1 = { {{.*}}"patchable-function-entry"="5"{{.*}}"patchable-function-entry-section"="bar_section"{{.*}}"patchable-function-prefix"="10" {{.*}} } diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 7d8bd3700d4bb..96012d8df936b 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -550,8 +550,8 @@ LL | #[patchable_function_entry] | help: must be of the form | -LL | #[patchable_function_entry(prefix_nops = m, entry_nops = n)] - | +++++++++++++++++++++++++++++++++ +LL | #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] + | ++++++++++++++++++++++++++++++++++++++++++++++++++++++ error[E0565]: malformed `coroutine` attribute input --> $DIR/malformed-attrs.rs:118:5 diff --git a/tests/ui/patchable-function-entry/patchable-function-entry-attribute.rs b/tests/ui/patchable-function-entry/patchable-function-entry-attribute.rs index 5cbeccf1b0e4f..1223c0c5b05f9 100644 --- a/tests/ui/patchable-function-entry/patchable-function-entry-attribute.rs +++ b/tests/ui/patchable-function-entry/patchable-function-entry-attribute.rs @@ -24,3 +24,19 @@ pub fn no_parameters_given() {} #[patchable_function_entry(prefix_nops = 255, prefix_nops = 255)] //~^ ERROR malformed pub fn duplicate_parameter() {} + +#[patchable_function_entry(section = 255)] +//~^ ERROR malformed +pub fn invalid_section_parameter() {} + +#[patchable_function_entry(section = "foo", section = "bar")] +//~^ ERROR malformed +pub fn duplicate_section_parameter() {} + +#[patchable_function_entry(section = "fo\0o")] +//~^ ERROR null characters +pub fn nul_in_section_parameter() {} + +#[patchable_function_entry(section = "")] +//~^ ERROR empty +pub fn empty_section_parameter() {} diff --git a/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr b/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr index 6c32a76834c69..c3f09ff12384a 100644 --- a/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr +++ b/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr @@ -9,7 +9,7 @@ LL | #[patchable_function_entry(prefix_nops = 256, entry_nops = 0)] help: must be of the form | LL - #[patchable_function_entry(prefix_nops = 256, entry_nops = 0)] -LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n)] +LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] | error[E0539]: malformed `patchable_function_entry` attribute input @@ -23,7 +23,7 @@ LL | #[patchable_function_entry(prefix_nops = "stringvalue", entry_nops = 0)] help: must be of the form | LL - #[patchable_function_entry(prefix_nops = "stringvalue", entry_nops = 0)] -LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n)] +LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] | error[E0539]: malformed `patchable_function_entry` attribute input @@ -34,8 +34,8 @@ LL | #[patchable_function_entry] | help: must be of the form | -LL | #[patchable_function_entry(prefix_nops = m, entry_nops = n)] - | +++++++++++++++++++++++++++++++++ +LL | #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] + | ++++++++++++++++++++++++++++++++++++++++++++++++++++++ error[E0539]: malformed `patchable_function_entry` attribute input --> $DIR/patchable-function-entry-attribute.rs:16:1 @@ -48,7 +48,7 @@ LL | #[patchable_function_entry(prefix_nops = 10, something = 0)] help: must be of the form | LL - #[patchable_function_entry(prefix_nops = 10, something = 0)] -LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n)] +LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] | error[E0539]: malformed `patchable_function_entry` attribute input @@ -61,8 +61,8 @@ LL | #[patchable_function_entry()] | help: must be of the form | -LL | #[patchable_function_entry(prefix_nops = m, entry_nops = n)] - | +++++++++++++++++++++++++++++++ +LL | #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] + | ++++++++++++++++++++++++++++++++++++++++++++++++++++ error[E0538]: malformed `patchable_function_entry` attribute input --> $DIR/patchable-function-entry-attribute.rs:24:1 @@ -75,10 +75,50 @@ LL | #[patchable_function_entry(prefix_nops = 255, prefix_nops = 255)] help: must be of the form | LL - #[patchable_function_entry(prefix_nops = 255, prefix_nops = 255)] -LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n)] +LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] | -error: aborting due to 6 previous errors +error[E0539]: malformed `patchable_function_entry` attribute input + --> $DIR/patchable-function-entry-attribute.rs:28:1 + | +LL | #[patchable_function_entry(section = 255)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---^^ + | | + | expected a string literal here + | +help: must be of the form + | +LL - #[patchable_function_entry(section = 255)] +LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] + | + +error[E0538]: malformed `patchable_function_entry` attribute input + --> $DIR/patchable-function-entry-attribute.rs:32:1 + | +LL | #[patchable_function_entry(section = "foo", section = "bar")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-------^^^^^^^^^^ + | | + | found `section` used as a key more than once + | +help: must be of the form + | +LL - #[patchable_function_entry(section = "foo", section = "bar")] +LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] + | + +error[E0648]: `section` may not contain null characters + --> $DIR/patchable-function-entry-attribute.rs:36:38 + | +LL | #[patchable_function_entry(section = "fo\0o")] + | ^^^^^^^ + +error: `section` may not be empty + --> $DIR/patchable-function-entry-attribute.rs:40:38 + | +LL | #[patchable_function_entry(section = "")] + | ^^ + +error: aborting due to 10 previous errors -Some errors have detailed explanations: E0538, E0539. +Some errors have detailed explanations: E0538, E0539, E0648. For more information about an error, try `rustc --explain E0538`. diff --git a/tests/ui/patchable-function-entry/patchable-function-entry-flags.stderr b/tests/ui/patchable-function-entry/patchable-function-entry-flags.stderr index b09af94a61541..bde7f4aa9dff6 100644 --- a/tests/ui/patchable-function-entry/patchable-function-entry-flags.stderr +++ b/tests/ui/patchable-function-entry/patchable-function-entry-flags.stderr @@ -1,2 +1,2 @@ -error: incorrect value `1,2` for unstable option `patchable-function-entry` - either two comma separated integers (total_nops,prefix_nops), with prefix_nops <= total_nops, or one integer (total_nops) was expected +error: incorrect value `1,2` for unstable option `patchable-function-entry` - a comma separated list of (prefix_nops,total_nops,section_name), (prefix_nops,total_nops), or (total_nops). Where prefix_nops <= total_nops where 0 < total_nops <= 255 and prefix_nops <= total_nops was expected From 91a762bd474be0c3605f9a8b5969f376c1400652 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Mon, 29 Jun 2026 16:53:58 +0300 Subject: [PATCH 38/38] Inline information of whether this segment is delegation's child segment into `PathSegment` --- compiler/rustc_ast_lowering/src/delegation.rs | 7 ++++++- .../rustc_ast_lowering/src/delegation/generics.rs | 12 ++++++++++-- compiler/rustc_ast_lowering/src/lib.rs | 1 + compiler/rustc_ast_lowering/src/path.rs | 1 + compiler/rustc_hir/src/hir.rs | 14 +++++++++++++- compiler/rustc_hir/src/intravisit.rs | 3 ++- .../src/hir_ty_lowering/bounds.rs | 1 + .../src/hir_ty_lowering/errors.rs | 1 + .../src/hir_ty_lowering/generics.rs | 2 +- .../rustc_hir_analysis/src/hir_ty_lowering/mod.rs | 2 +- compiler/rustc_middle/src/hir/map.rs | 7 ------- 11 files changed, 37 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index 078ebd6987ae3..7021ac8cfedaf 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -55,7 +55,9 @@ use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::symbol::kw; use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; -use crate::delegation::generics::{GenericsGenerationResult, GenericsGenerationResults}; +use crate::delegation::generics::{ + GenericsGenerationResult, GenericsGenerationResults, GenericsPosition, +}; use crate::diagnostics::{ CycleInDelegationSignatureResolution, DelegationAttemptedBlockWithDefsDeletion, DelegationBlockSpecifiedWhenNoParams, UnresolvedDelegationCallee, @@ -505,6 +507,7 @@ impl<'hir> LoweringContext<'_, 'hir> { res: Res::Local(param_id), args: None, infer_args: false, + delegation_child_segment: false, })); let path = self.arena.alloc(hir::Path { span, res: Res::Local(param_id), segments }); @@ -714,6 +717,8 @@ impl<'hir> LoweringContext<'_, 'hir> { result.args_segment_id = segment.hir_id; result.use_for_sig_inheritance = !result.generics.is_trait_impl(); + segment.delegation_child_segment = result.generics.pos() == GenericsPosition::Child; + segment } diff --git a/compiler/rustc_ast_lowering/src/delegation/generics.rs b/compiler/rustc_ast_lowering/src/delegation/generics.rs index 79a3961c22a53..2a751ffae3eb7 100644 --- a/compiler/rustc_ast_lowering/src/delegation/generics.rs +++ b/compiler/rustc_ast_lowering/src/delegation/generics.rs @@ -12,7 +12,7 @@ use rustc_span::{Ident, Span, sym}; use crate::LoweringContext; use crate::diagnostics::DelegationInfersMismatch; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub(super) enum GenericsPosition { Parent, Child, @@ -155,7 +155,7 @@ impl<'hir> DelegationGenericArgsIterator<'hir> { impl<'hir> HirOrTyGenerics<'hir> { pub(super) fn into_hir_generics(&mut self, ctx: &mut LoweringContext<'_, 'hir>, span: Span) { if let HirOrTyGenerics::Ty(ty) = self { - let rename_self = matches!(ty.pos, GenericsPosition::Child); + let rename_self = ty.pos == GenericsPosition::Child; let params = ctx.uplift_delegation_generic_params(span, &ty.data, rename_self); *self = HirOrTyGenerics::Hir(DelegationGenerics { @@ -218,6 +218,13 @@ impl<'hir> HirOrTyGenerics<'hir> { .expect("`Self` generic param is not found while expected"), } } + + pub(crate) fn pos(&self) -> GenericsPosition { + match self { + HirOrTyGenerics::Ty(ty) => ty.pos, + HirOrTyGenerics::Hir(hir) => hir.pos, + } + } } impl<'hir> GenericsGenerationResult<'hir> { @@ -590,6 +597,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ident: p.name.ident(), infer_args: false, res, + delegation_child_segment: false, }]), res, span: p.span, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 06e83a7486100..1bf798c357bd2 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1014,6 +1014,7 @@ impl<'hir> LoweringContext<'_, 'hir> { res, args, infer_args: args.is_none(), + delegation_child_segment: false, }]), }) } diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 42c4af5add12d..18e037527836a 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -412,6 +412,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } else { Some(generic_args.into_generic_args(self)) }, + delegation_child_segment: false, } } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 6d2e68d20bf30..254d5fdd5f80c 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -387,12 +387,24 @@ pub struct PathSegment<'hir> { /// out of those only the segments with no type parameters /// to begin with, e.g., `Vec::new` is `>::new::<..>`. pub infer_args: bool, + + /// Whether this segment is a delegation's child segment: + /// `reuse Trait::foo`, in this case `foo` is a delegation's child segment. + /// Used for faster check during generic args lowering. + pub delegation_child_segment: bool, } impl<'hir> PathSegment<'hir> { /// Converts an identifier to the corresponding segment. pub fn new(ident: Ident, hir_id: HirId, res: Res) -> PathSegment<'hir> { - PathSegment { ident, hir_id, res, infer_args: true, args: None } + PathSegment { + ident, + hir_id, + res, + infer_args: true, + args: None, + delegation_child_segment: false, + } } pub fn invalid() -> Self { diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 9e0eaef596420..f1f542e9ce1e9 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1468,7 +1468,8 @@ pub fn walk_path_segment<'v, V: Visitor<'v>>( visitor: &mut V, segment: &'v PathSegment<'v>, ) -> V::Result { - let PathSegment { ident, hir_id, res: _, args, infer_args: _ } = segment; + let PathSegment { ident, hir_id, res: _, args, infer_args: _, delegation_child_segment: _ } = + segment; try_visit!(visitor.visit_ident(*ident)); try_visit!(visitor.visit_id(*hir_id)); visit_opt!(visitor, visit_generic_args, *args); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 810e14a7d6d0b..d730683b132d4 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -467,6 +467,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { res: Res::Err, args: Some(constraint.gen_args), infer_args: false, + delegation_child_segment: false, }; let alias_args = self.lower_generic_args_of_assoc_item( diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 32678e8a5a12f..9d22d3060e1ff 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -471,6 +471,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { res: Res::Err, args: Some(constraint.gen_args), infer_args: false, + delegation_child_segment: false, }; let alias_args = self.lower_generic_args_of_assoc_item( diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index a36bb7bce9bab..45c2ed205c74d 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -434,7 +434,7 @@ pub(crate) fn check_generic_arg_count( // Suppress this warning for delegations as it is compiler generated and lifetimes are // propagated while late-bound lifetimes may be present. - let explicit_late_bound = match tcx.hir_is_delegation_child_segment(seg) { + let explicit_late_bound = match seg.delegation_child_segment { true => ExplicitLateBound::No, false => prohibit_explicit_late_bound_lifetimes(cx, gen_params, gen_args, gen_pos), }; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index e8a7156b41fe4..7edcb40be616e 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -870,7 +870,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span, generic_args: segment.args(), infer_args: segment.infer_args, - create_synth_args: tcx.hir_is_delegation_child_segment(segment), + create_synth_args: segment.delegation_child_segment, incorrect_args: &arg_count.correct, }; diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index 3cab936c45c1f..444433dcb6c54 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -879,13 +879,6 @@ impl<'tcx> TyCtxt<'tcx> { self.hir_opt_delegation_info(delegation_id).expect("processing delegation") } - pub fn hir_is_delegation_child_segment(self, segment: &PathSegment<'_>) -> bool { - let parent_def = self.hir_get_parent_item(segment.hir_id).def_id; - - self.hir_opt_delegation_info(parent_def) - .is_some_and(|info| info.child_seg_id == segment.hir_id) - } - #[inline] fn hir_opt_ident(self, id: HirId) -> Option { match self.hir_node(id) {