From dbc4eb8f42ad2d2a9e7170d52e00512a6e12ce69 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 3 Apr 2026 14:55:36 +0000 Subject: [PATCH 01/24] 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 02/24] 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 03/24] 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 04/24] 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 05/24] 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 06/24] 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 07/24] 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 08/24] 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 09/24] 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 10/24] 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 11/24] 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 12/24] 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 13/24] 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 14/24] 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 15/24] 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 16/24] 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 bd938c5bfb48fe83ef8f2f54f562c2394c17d89f Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Sat, 27 Jun 2026 01:13:26 -0400 Subject: [PATCH 17/24] 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 18/24] 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 5128fcb1991d35c0e23d2622504d76d33b480825 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Mon, 29 Jun 2026 14:58:44 +1000 Subject: [PATCH 19/24] Note where and why documentation hard-links are used Also link back to a relevant issue to track possible changes in functionality. --- library/alloc/src/io/error.rs | 3 +++ library/core/src/io/cursor.rs | 2 ++ library/core/src/io/error.rs | 6 ++++++ 3 files changed, 11 insertions(+) diff --git a/library/alloc/src/io/error.rs b/library/alloc/src/io/error.rs index 8053e73917c7f..eb02664ee7e46 100644 --- a/library/alloc/src/io/error.rs +++ b/library/alloc/src/io/error.rs @@ -55,6 +55,7 @@ impl Error { /// originate from the OS itself. It is a shortcut for [`Error::new`][new] /// with [`ErrorKind::Other`]. /// + // FIXME(#74481): Hard-links required to link from `alloc` to `std` for incoherent method /// [new]: struct.Error.html#method.new /// /// # Examples @@ -84,6 +85,7 @@ impl Error { /// then this function will return [`Some`], /// otherwise it will return [`None`]. /// + // FIXME(#74481): Hard-links required to link from `alloc` to `std` for incoherent method /// [new]: struct.Error.html#method.new /// [other]: struct.Error.html#method.other /// @@ -141,6 +143,7 @@ impl Error { /// `Box::downcast` on the custom boxed error, returned by /// [`Error::into_inner`][into_inner]. /// + // FIXME(#74481): Hard-links required to link from `alloc` to `std` for incoherent method /// [into_inner]: struct.Error.html#method.into_inner /// /// # Examples diff --git a/library/core/src/io/cursor.rs b/library/core/src/io/cursor.rs index ebbec6c5e8210..24d3bfc67b988 100644 --- a/library/core/src/io/cursor.rs +++ b/library/core/src/io/cursor.rs @@ -16,6 +16,7 @@ /// code, but use an in-memory buffer in our tests. We can do this with /// `Cursor`: /// +// FIXME(#74481): Hard-links required to link from `core` to `std` /// [bytes]: crate::slice "slice" /// [`File`]: ../../std/fs/struct.File.html /// [`Read`]: ../../std/io/trait.Read.html @@ -80,6 +81,7 @@ impl Cursor { /// is not empty. So writing to cursor starts with overwriting [`Vec`] /// content, not with appending to it. /// + // FIXME(#74481): Hard-links required to link from `core` to `alloc` /// [`Vec`]: ../../alloc/vec/struct.Vec.html /// /// # Examples diff --git a/library/core/src/io/error.rs b/library/core/src/io/error.rs index f9e45313be53d..2d15307daf153 100644 --- a/library/core/src/io/error.rs +++ b/library/core/src/io/error.rs @@ -45,6 +45,7 @@ use crate::{error, fmt, result}; /// will generally use `io::Result` instead of shadowing the [prelude]'s import /// of [`core::result::Result`][`Result`]. /// +// FIXME(#74481): Hard-links required to link from `core` to `std` /// [`std::io`]: ../../std/io/index.html /// [`io::Error`]: Error /// [`Result`]: crate::result::Result @@ -76,6 +77,7 @@ pub type Result = result::Result; /// `Error` can be created with crafted error messages and a particular value of /// [`ErrorKind`]. /// +// FIXME(#74481): Hard-links required to link from `core` to `std` /// [Read]: ../../std/io/trait.Read.html /// [Write]: ../../std/io/trait.Write.html /// [Seek]: ../../std/io/trait.Seek.html @@ -171,6 +173,7 @@ pub struct SimpleMessage { /// Contrary to [`Error::new`][new], this macro does not allocate and can be used in /// `const` contexts. /// +// FIXME(#74481): Hard-links required to link from `core` to `alloc` for incoherent method /// [new]: ../../alloc/io/struct.Error.html#method.new /// /// # Example @@ -286,6 +289,7 @@ impl Error { /// [`from_raw_os_error`][from_raw_os_error], then this function will return [`Some`], otherwise /// it will return [`None`]. /// + // FIXME(#74481): Hard-links required to link from `core` to `std` for incoherent method /// [last_os_error]: ../../std/io/struct.Error.html#method.last_os_error /// [from_raw_os_error]: ../../std/io/struct.Error.html#method.from_raw_os_error /// @@ -366,6 +370,7 @@ impl Error { /// If this [`Error`] was constructed via [`new`][new] then this function will /// return [`Some`], otherwise it will return [`None`]. /// + // FIXME(#74481): Hard-links required to link from `core` to `std` /// [new]: ../../alloc/io/struct.Error.html#method.new /// /// # Examples @@ -441,6 +446,7 @@ impl Error { /// it will be a value inferred from the system's error encoding. /// See [`last_os_error`][last_os_error] for more details. /// + // FIXME(#74481): Hard-links required to link from `core` to `std` /// [last_os_error]: ../../std/io/struct.Error.html#method.last_os_error /// /// # Examples From 91a762bd474be0c3605f9a8b5969f376c1400652 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Mon, 29 Jun 2026 16:53:58 +0300 Subject: [PATCH 20/24] 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) { From cd55f64904d8dfe73145a71b82b86d34dd222ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Mon, 29 Jun 2026 15:55:27 +0200 Subject: [PATCH 21/24] fix typo in `-Z min-recursion-limit` unstable doc --- .../{min-recursive-limit.md => min-recursion-limit.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/doc/unstable-book/src/compiler-flags/{min-recursive-limit.md => min-recursion-limit.md} (100%) diff --git a/src/doc/unstable-book/src/compiler-flags/min-recursive-limit.md b/src/doc/unstable-book/src/compiler-flags/min-recursion-limit.md similarity index 100% rename from src/doc/unstable-book/src/compiler-flags/min-recursive-limit.md rename to src/doc/unstable-book/src/compiler-flags/min-recursion-limit.md From a60be48dff6b696a9b55c2878d0731fea26230a2 Mon Sep 17 00:00:00 2001 From: Liza Burakova Date: Mon, 29 Jun 2026 15:09:50 +0000 Subject: [PATCH 22/24] llvm-wrapper: use accessors for private fields in LLVM 23+ In LLVM, FeatureKV/SubtargetKV pointers are now private: https://github.com/llvm/llvm-project/pull/206237 This change fixes compiler errors when building rustc with ToT LLVM by using the key() and desc() accessors. --- compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 5dcaa5f6f84b8..45229e5399dd5 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -288,7 +288,11 @@ extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM, // Just print a bare list of target CPU names, and let Rust-side code handle // the full formatting of `--print=target-cpus`. for (auto &CPU : CPUTable) { +#if LLVM_VERSION_GE(23, 0) + OS << CPU.key() << "\n"; +#else OS << CPU.Key << "\n"; +#endif } } @@ -315,9 +319,15 @@ extern "C" void LLVMRustGetTargetFeature(LLVMTargetMachineRef TM, size_t Index, #endif const ArrayRef FeatTable = MCInfo.getAllProcessorFeatures(); +#if LLVM_VERSION_GE(23, 0) + const SubtargetFeatureKV &Feat = FeatTable[Index]; + *Feature = Feat.key(); + *Desc = Feat.desc(); +#else const SubtargetFeatureKV Feat = FeatTable[Index]; *Feature = Feat.Key; *Desc = Feat.Desc; +#endif } extern "C" const char *LLVMRustGetHostCPUName(size_t *OutLen) { From 7bfdb60f23f254d2f38699295c46feafe3b87ef8 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Mon, 29 Jun 2026 12:28:25 -0700 Subject: [PATCH 23/24] Comment on needed RAM in huge-stacks.rs --- tests/ui/codegen/huge-stacks.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/ui/codegen/huge-stacks.rs b/tests/ui/codegen/huge-stacks.rs index e0f2cd2cb05d6..9aee3937c7534 100644 --- a/tests/ui/codegen/huge-stacks.rs +++ b/tests/ui/codegen/huge-stacks.rs @@ -5,6 +5,7 @@ //@ min-llvm-version: 22 // Regression test for https://github.com/rust-lang/rust/issues/83060 +// Verifies a program is not miscompiled if it includes a 4GB array on the stack fn func() { const CAP: usize = std::u32::MAX as usize; @@ -17,7 +18,7 @@ fn main() { std::thread::Builder::new() .stack_size(5 * 1024 * 1024 * 1024) .spawn(func) - .unwrap() + .expect("huge-stacks.rs requires 5GB RAM to run") .join() .unwrap(); } From abbfd2ce05dc13e28db659b2b9e8e6403ae827e1 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 8 May 2026 00:52:44 +0300 Subject: [PATCH 24/24] Pass the whole `GenericArgs` to `Interner::for_each_relevant_impl()` And not just the self type. rustc does not make use of this, but rust-analyzer needs it to support impls in the same block as args, see https://rust-lang.zulipchat.com/#narrow/channel/144729-t-types/topic/non.20local.20impls.20for.20generic.20args/with/593629693. I'm not entirely sure this covers all cases (e.g. an unnormalized alias), and wants feedback from a types team member. --- .../src/ty/context/impl_interner.rs | 5 ++- .../src/solve/assembly/mod.rs | 38 +++++++++---------- .../src/solve/trait_goals.rs | 10 ++--- compiler/rustc_type_ir/src/interner.rs | 5 +-- 4 files changed, 25 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 5819470765e69..cf6385522bdad 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -541,8 +541,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { // only want to consider types that *actually* unify with float/int vars. fn for_each_relevant_impl( self, - trait_def_id: DefId, - self_ty: Ty<'tcx>, + trait_ref: ty::TraitRef<'tcx>, mut f: impl FnMut(DefId) -> R, ) -> R { macro_rules! ret { @@ -554,6 +553,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> { }; } + let trait_def_id = trait_ref.def_id; + let self_ty = trait_ref.self_ty(); let tcx = self; let trait_impls = tcx.trait_impls_of(trait_def_id); let mut consider_impls_for_simplified_type = |simp| { diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 0421bd7aaedc0..db1a8228c43cb 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -539,28 +539,24 @@ where candidates: &mut Vec>, ) -> Result<(), RerunNonErased> { let cx = self.cx(); - cx.for_each_relevant_impl( - goal.predicate.trait_def_id(cx), - goal.predicate.self_ty(), - |impl_def_id| -> Result<_, _> { - // For every `default impl`, there's always a non-default `impl` - // that will *also* apply. There's no reason to register a candidate - // for this impl, since it is *not* proof that the trait goal holds. - if cx.impl_is_default(impl_def_id) { - return Ok(()); - } - match G::consider_impl_candidate(self, goal, impl_def_id, |ecx, certainty| { - ecx.evaluate_added_goals_and_make_canonical_response(certainty) - }) - .map_err_to_rerun()? - { - Ok(candidate) => candidates.push(candidate), - Err(NoSolution) => {} - } + cx.for_each_relevant_impl(goal.predicate.trait_ref(cx), |impl_def_id| -> Result<_, _> { + // For every `default impl`, there's always a non-default `impl` + // that will *also* apply. There's no reason to register a candidate + // for this impl, since it is *not* proof that the trait goal holds. + if cx.impl_is_default(impl_def_id) { + return Ok(()); + } + match G::consider_impl_candidate(self, goal, impl_def_id, |ecx, certainty| { + ecx.evaluate_added_goals_and_make_canonical_response(certainty) + }) + .map_err_to_rerun()? + { + Ok(candidate) => candidates.push(candidate), + Err(NoSolution) => {} + } - Ok(()) - }, - ) + Ok(()) + }) } #[instrument(level = "trace", skip_all)] diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 5c002d09a75cc..09d96e3b3a143 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -1246,13 +1246,9 @@ where let self_ty = goal.predicate.self_ty(); let check_impls = || { let mut disqualifying_impl = None; - self.cx().for_each_relevant_impl( - goal.predicate.def_id(), - goal.predicate.self_ty(), - |impl_def_id| { - disqualifying_impl = Some(impl_def_id); - }, - ); + self.cx().for_each_relevant_impl(goal.predicate.trait_ref, |impl_def_id| { + disqualifying_impl = Some(impl_def_id); + }); if let Some(def_id) = disqualifying_impl { trace!(?def_id, ?goal, "disqualified auto-trait implementation"); // No need to actually consider the candidate here, diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 6fd19f9362a9b..815890c989d93 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -16,7 +16,7 @@ use crate::solve::{ AccessedOpaques, CanonicalInput, Certainty, ExternalConstraintsData, QueryResult, inspect, }; use crate::visit::{Flags, TypeVisitable}; -use crate::{self as ty, CanonicalParamEnvCacheEntry, search_graph}; +use crate::{self as ty, CanonicalParamEnvCacheEntry, TraitRef, search_graph}; #[cfg_attr(feature = "nightly", rustc_diagnostic_item = "type_ir_interner")] pub trait Interner: @@ -398,8 +398,7 @@ pub trait Interner: fn for_each_relevant_impl( self, - trait_def_id: Self::TraitId, - self_ty: Self::Ty, + trait_ref: TraitRef, f: impl FnMut(Self::ImplId) -> R, ) -> R; fn for_each_blanket_impl(