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..35e16ce78a8a8 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, }]), }) } @@ -2791,7 +2792,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_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_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_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index b9b6e51d3b82c..7450f11f3ce80 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}; @@ -241,7 +242,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) => { @@ -290,7 +297,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/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index fe36a9865485d..ee6035359d649 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -12,9 +12,12 @@ 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, FramePointer, LlvmAbi, 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 +646,12 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( } } + if sess.target.llvm_abiname == LlvmAbi::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 a7dec13422f46..bb4ae6e64560a 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::{LlvmAbi, 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}; @@ -123,7 +124,18 @@ 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. + // + // 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.llvm_abiname == LlvmAbi::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); } @@ -140,6 +152,30 @@ pub(crate) fn compile_codegen_unit( cx.add_objc_module_flags(); } + 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 + // binary compatibility. + // + // We intentionally do not emit these flags now, since only a subset of features + // 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 + // 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/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 7535c41cd1ed3..58a62145d576e 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, HasTargetSpec, LlvmAbi, 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.llvm_abiname != LlvmAbi::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/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 5c5e9ed7e082c..1c26738b7cf9c 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::LlvmAbi; 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.llvm_abiname != LlvmAbi::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..a4d52c18c890b 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, LlvmAbi}; 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 @@ -38,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); @@ -109,14 +120,31 @@ 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.llvm_abiname == LlvmAbi::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; } @@ -141,7 +169,21 @@ 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); + // 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| { + 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) { @@ -164,6 +206,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)) { @@ -176,8 +219,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.llvm_abiname == LlvmAbi::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()) @@ -207,7 +255,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) @@ -776,7 +841,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 6198a98e5f7ae..96fc781b15f9d 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 @@ -871,8 +889,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 +948,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_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 7ac35af5820ea..7bd604bdbbd76 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, LlvmAbi}; 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; @@ -1711,6 +1712,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.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; + crate::attributes::apply_to_llfn(rust_try_fn, AttributePlace::Function, &attrs); + } + cx.rust_try_fn.set(Some(rust_try)); rust_try } 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_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 00cfe1c845d7a..0173b84a4d9a1 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), } @@ -1410,7 +1410,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, 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/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/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/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..b29e469348f81 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -483,7 +483,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 { @@ -512,15 +512,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, }; @@ -870,7 +866,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_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/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 859e3463cb79f..bb822f8d0f68d 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}; @@ -1186,11 +1186,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_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_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) { 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_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..c9e4ce60e1a01 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -197,6 +197,31 @@ 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 { + 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/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..6a97327504721 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)`. @@ -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) { @@ -1115,6 +1108,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 5819470765e69..ada3298a6ca6e 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -244,7 +244,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:?}"), @@ -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_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_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_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 2ffac2a0b3fbb..1b12f9c4a6f7e 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -187,7 +187,6 @@ struct InvocationParent { parent_def: LocalDefId, impl_trait_context: ImplTraitContext, in_attr: bool, - const_arg_context: ConstArgContext, owner: NodeId, } @@ -196,7 +195,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, }; } @@ -208,13 +206,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_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/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/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 580c9f2a34b17..986dababf770a 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, 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; @@ -1229,6 +1229,13 @@ fn validate_commandline_args_with_session_available(sess: &Session) { sess.dcx().emit_err(errors::CannotEnableCrtStaticLinux); } + // 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); + } + // 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..f00b6e079c76e 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), @@ -2072,6 +2073,7 @@ crate::target_spec_enum! { Ilp32e = "ilp32e", Llvm = "llvm", MacAbi = "macabi", + Pauthtest = "pauthtest", Sim = "sim", SoftFloat = "softfloat", Spe = "spe", @@ -2112,6 +2114,8 @@ crate::target_spec_enum! { // PowerPC ElfV1 = "elfv1", ElfV2 = "elfv2", + // Pointer authentication: Pauthtest + Pauthtest = "pauthtest", Unspecified = "", } @@ -3398,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. @@ -3413,6 +3418,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 new file mode 100644 index 0000000000000..739694ea1509d --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_pauthtest.rs @@ -0,0 +1,38 @@ +use crate::spec::{ + Arch, CfgAbi, Env, FramePointer, LinkSelfContainedDefault, LlvmAbi, 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::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), + 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/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 4e3a31f0e84b5..3fdb1d6defdf6 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -2556,6 +2556,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) @@ -2571,10 +2572,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/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/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( 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 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..365d6eff28ccf 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.contains("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 3c2348c9bf71c..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,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 the target yet. ]; /// Minimum version threshold for libstdc++ required when using prebuilt LLVM @@ -412,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/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..558bc409e08d2 --- /dev/null +++ b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md @@ -0,0 +1,440 @@ +# 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 \ + "$@" +``` + +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 +`` 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_abi = "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 + * pauth-basic.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/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 +`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/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 \ + 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/run-make/pauth-static-link-warning \ + 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/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 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/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs index b7130c0870800..7c4469afda282 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -106,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", @@ -247,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/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/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/assembly-llvm/asm/aarch64-outline-atomics.rs b/tests/assembly-llvm/asm/aarch64-outline-atomics.rs index 1177c1e68ed56..226f177dfb093 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-pauthtest #![crate_type = "rlib"] diff --git a/tests/assembly-llvm/pauth-basic.rs b/tests/assembly-llvm/pauth-basic.rs new file mode 100644 index 0000000000000..e240e1317f3a3 --- /dev/null +++ b/tests/assembly-llvm/pauth-basic.rs @@ -0,0 +1,44 @@ +//@ add-minicore +//@ assembly-output: emit-asm +//@ 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 + +#![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/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index c949d8861975b..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`: @@ -299,6 +302,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 +380,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/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..63ed4b211a9a2 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-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..e3dfbc819f9af 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-pauthtest #![crate_type = "rlib"] diff --git a/tests/codegen-llvm/issues/issue-73258.rs b/tests/codegen-llvm/issues/issue-73258.rs index c9eceb0dccf73..4962c53853aac 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 emitted call may carry attributes (e.g. +// `ptrauth-calls`), which would otherwise make a plain `call` pattern too permissive. #[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/codegen-llvm/pauth/pauth-attr-special-funcs.rs b/tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs new file mode 100644 index 0000000000000..882cc4c6db971 --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs @@ -0,0 +1,31 @@ +//@ 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 +// 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..643b428339b73 --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-extern-c-direct-indirect-call.rs @@ -0,0 +1,98 @@ +//@ add-minicore +// ignore-tidy-linelength +//@ only-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..ac12729f80699 --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-extern-c.rs @@ -0,0 +1,75 @@ +// ignore-tidy-linelength +//@ only-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} 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..a83298dd5725c --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-extern-weak-global.rs @@ -0,0 +1,38 @@ +// ignore-tidy-linelength +//@ only-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..db327644d96cf --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-init-fini.rs @@ -0,0 +1,34 @@ +//@ add-minicore +// ignore-tidy-linelength +//@ only-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) {} 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..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,6 +6,8 @@ //@ needs-target-std //@ ignore-android: FIXME(#142855) //@ ignore-sgx: (x86 machine code cannot be directly executed) +//@ 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/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..3cf978a2f6da5 --- /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-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..e77081d70039c --- /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-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/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..8d4fb18fe33f9 --- /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-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/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/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(), + ); +} 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 = (); +} diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index 47ed9891a1a6c..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` 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(); } 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 + diff --git a/tests/ui/process/nofile-limit.rs b/tests/ui/process/nofile-limit.rs index f5246856b80f2..96b298cc78105 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-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 new file mode 100644 index 0000000000000..bc5badc600d88 --- /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-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 + 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`.