https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/193087
>From dadf5bc5a9e61e644d9bf2753ddb108c17d17caa Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko <[email protected]> Date: Fri, 17 Apr 2026 20:55:13 +0300 Subject: [PATCH 1/3] [AArch64][PAC] Handle signing of init/fini pointers in AsmPrinter Move signing of the contents of `@llvm.global_(ctors|dtors)` from Clang frontend to the end of the backend pipeline, to AsmPrinter. Signing of the pointers to init/fini functions in the backend fixes registration of the constructors and destructors performed by the optimizer or the backend. This commit introduces two new module flags, `ptrauth-init-fini` and `ptrauth-init-fini-address-discrimination`, mirroring corresponding Clang options. The flags are semantically boolean, and the module is allowed to have either none of these flags, only the first one, or both. The particular constant discriminator to use is not configurable via module flags and is hardcoded to the value 0xD9D4 in the `llvm/lib/Target/AArch64/AArch64PointerAuth.h` file. --- .../include/clang/Basic/PointerAuthOptions.h | 7 - clang/lib/CodeGen/CodeGenModule.cpp | 27 +--- clang/lib/Frontend/CompilerInvocation.cpp | 6 - clang/test/CodeGen/ptrauth-init-fini.c | 30 ++-- llvm/lib/IR/AutoUpgrade.cpp | 101 ++++++++++++- llvm/lib/IR/Verifier.cpp | 58 +++++-- llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp | 44 ++++-- llvm/lib/Target/AArch64/AArch64PointerAuth.h | 10 +- .../AArch64/ptrauth-init-fini-autoupgrade.ll | 143 ++++++++++++++++++ .../test/CodeGen/AArch64/ptrauth-init-fini.ll | 87 +++++++++-- 10 files changed, 428 insertions(+), 85 deletions(-) create mode 100644 llvm/test/CodeGen/AArch64/ptrauth-init-fini-autoupgrade.ll diff --git a/clang/include/clang/Basic/PointerAuthOptions.h b/clang/include/clang/Basic/PointerAuthOptions.h index 2b920250721fc..2aa8d70088dbc 100644 --- a/clang/include/clang/Basic/PointerAuthOptions.h +++ b/clang/include/clang/Basic/PointerAuthOptions.h @@ -27,10 +27,6 @@ namespace clang { /// is ptrauth_string_discriminator("block_descriptor") constexpr uint16_t BlockDescriptorConstantDiscriminator = 0xC0BB; -/// Constant discriminator to be used with function pointers in .init_array and -/// .fini_array. The value is ptrauth_string_discriminator("init_fini") -constexpr uint16_t InitFiniPointerConstantDiscriminator = 0xD9D4; - /// Constant discriminator to be used with method list pointers. The value is /// ptrauth_string_discriminator("method_list_t") constexpr uint16_t MethodListPointerConstantDiscriminator = 0xC310; @@ -224,9 +220,6 @@ struct PointerAuthOptions { /// The ABI for C++ member function pointers. PointerAuthSchema CXXMemberFunctionPointers; - /// The ABI for function addresses in .init_array and .fini_array - PointerAuthSchema InitFiniPointers; - /// The ABI for block invocation function pointers. PointerAuthSchema BlockInvocationFunctionPointers; diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 41049d85121be..1e6f28692d503 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1483,6 +1483,12 @@ void CodeGenModule::Release() { if (LangOpts.PointerAuthELFGOT) getModule().addModuleFlag(llvm::Module::Error, "ptrauth-elf-got", 1); + if (LangOpts.PointerAuthCalls && LangOpts.PointerAuthInitFini) { + getModule().addModuleFlag(llvm::Module::Error, "ptrauth-init-fini", 1); + if (LangOpts.PointerAuthInitFiniAddressDiscrimination) + getModule().addModuleFlag( + llvm::Module::Error, "ptrauth-init-fini-address-discrimination", 1); + } if (getTriple().isOSLinux()) { if (LangOpts.PointerAuthCalls) @@ -2548,9 +2554,6 @@ void CodeGenModule::AddGlobalDtor(llvm::Function *Dtor, int Priority, void CodeGenModule::EmitCtorList(CtorList &Fns, const char *GlobalName) { if (Fns.empty()) return; - const PointerAuthSchema &InitFiniAuthSchema = - getCodeGenOpts().PointerAuth.InitFiniPointers; - // Ctor function type is ptr. llvm::PointerType *PtrTy = llvm::PointerType::get( getLLVMContext(), TheModule.getDataLayout().getProgramAddressSpace()); @@ -2564,23 +2567,7 @@ void CodeGenModule::EmitCtorList(CtorList &Fns, const char *GlobalName) { for (const auto &I : Fns) { auto Ctor = Ctors.beginStruct(CtorStructTy); Ctor.addInt(Int32Ty, I.Priority); - if (InitFiniAuthSchema) { - llvm::Constant *StorageAddress = - (InitFiniAuthSchema.isAddressDiscriminated() - ? llvm::ConstantExpr::getIntToPtr( - llvm::ConstantInt::get( - IntPtrTy, - llvm::ConstantPtrAuth::AddrDiscriminator_CtorsDtors), - PtrTy) - : nullptr); - llvm::Constant *SignedCtorPtr = getConstantSignedPointer( - I.Initializer, InitFiniAuthSchema.getKey(), StorageAddress, - llvm::ConstantInt::get( - SizeTy, InitFiniAuthSchema.getConstantDiscrimination())); - Ctor.add(SignedCtorPtr); - } else { - Ctor.add(I.Initializer); - } + Ctor.add(I.Initializer); if (I.AssociatedData) Ctor.add(I.AssociatedData); else diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 47cdcad377d06..352e61e53e166 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1481,12 +1481,6 @@ void CompilerInvocation::setDefaultPointerAuthOptions( Opts.CXXMemberFunctionPointers = PointerAuthSchema(Key::ASIA, false, Discrimination::Type); - if (LangOpts.PointerAuthInitFini) { - Opts.InitFiniPointers = PointerAuthSchema( - Key::ASIA, LangOpts.PointerAuthInitFiniAddressDiscrimination, - Discrimination::Constant, InitFiniPointerConstantDiscriminator); - } - Opts.BlockInvocationFunctionPointers = PointerAuthSchema(Key::ASIA, true, Discrimination::None); Opts.BlockHelperFunctionPointers = diff --git a/clang/test/CodeGen/ptrauth-init-fini.c b/clang/test/CodeGen/ptrauth-init-fini.c index 1e8953961d64e..466d992299226 100644 --- a/clang/test/CodeGen/ptrauth-init-fini.c +++ b/clang/test/CodeGen/ptrauth-init-fini.c @@ -1,28 +1,36 @@ // REQUIRES: aarch64-registered-target // RUN: %clang_cc1 -triple aarch64-elf -target-feature +pauth -fptrauth-calls -fptrauth-init-fini \ -// RUN: -emit-llvm %s -o - | FileCheck --check-prefix=SIGNED %s +// RUN: -emit-llvm %s -o - | FileCheck --check-prefix=COMMON,SIGNED %s // RUN: %clang_cc1 -triple aarch64-elf -target-feature +pauth -fptrauth-calls -fptrauth-init-fini \ -// RUN: -fptrauth-init-fini-address-discrimination -emit-llvm %s -o - | FileCheck --check-prefix=ADDRDISC %s +// RUN: -fptrauth-init-fini-address-discrimination -emit-llvm %s -o - | FileCheck --check-prefix=COMMON,ADDRDISC %s // RUN: %clang_cc1 -triple aarch64-elf -target-feature +pauth -fptrauth-calls \ -// RUN: -emit-llvm %s -o - | FileCheck --check-prefix=UNSIGNED %s +// RUN: -emit-llvm %s -o - | FileCheck --check-prefix=COMMON,UNSIGNED %s // RUN: %clang_cc1 -triple aarch64-elf -target-feature +pauth -fptrauth-calls -fptrauth-init-fini-address-discrimination \ -// RUN: -emit-llvm %s -o - | FileCheck --check-prefix=UNSIGNED %s +// RUN: -emit-llvm %s -o - | FileCheck --check-prefix=COMMON,UNSIGNED %s // RUN: %clang_cc1 -triple aarch64-elf -target-feature +pauth -fptrauth-init-fini \ -// RUN: -emit-llvm %s -o - | FileCheck --check-prefix=UNSIGNED %s +// RUN: -emit-llvm %s -o - | FileCheck --check-prefix=COMMON,UNSIGNED %s -// SIGNED: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr ptrauth (ptr @foo, i32 0, i64 55764), ptr null }] -// SIGNED: @llvm.global_dtors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr ptrauth (ptr @bar, i32 0, i64 55764), ptr null }] +// COMMON: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @foo, ptr null }] +// COMMON: @llvm.global_dtors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @bar, ptr null }] -// ADDRDISC: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr ptrauth (ptr @foo, i32 0, i64 55764, ptr inttoptr (i64 1 to ptr)), ptr null }] -// ADDRDISC: @llvm.global_dtors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr ptrauth (ptr @bar, i32 0, i64 55764, ptr inttoptr (i64 1 to ptr)), ptr null }] +// The below checks assume no other module flags happens to be set. -// UNSIGNED: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @foo, ptr null }] -// UNSIGNED: @llvm.global_dtors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @bar, ptr null }] +// UNSIGNED-NOT: !llvm.module.flags +// UNSIGNED-NOT: !"ptrauth-init-fini" +// UNSIGNED-NOT: !"ptrauth-init-fini-address-discrimination" + +// SIGNED: !llvm.module.flags = !{!0} +// SIGNED: !0 = !{i32 1, !"ptrauth-init-fini", i32 1} +// SIGNED-NOT: !"ptrauth-init-fini-address-discrimination" + +// ADDRDISC: !llvm.module.flags = !{!0, !1} +// ADDRDISC: !0 = !{i32 1, !"ptrauth-init-fini", i32 1} +// ADDRDISC: !1 = !{i32 1, !"ptrauth-init-fini-address-discrimination", i32 1} volatile int x = 0; diff --git a/llvm/lib/IR/AutoUpgrade.cpp b/llvm/lib/IR/AutoUpgrade.cpp index 0770f0f0ff060..3d699cd0bb7ad 100644 --- a/llvm/lib/IR/AutoUpgrade.cpp +++ b/llvm/lib/IR/AutoUpgrade.cpp @@ -6217,12 +6217,109 @@ void llvm::UpgradeARCRuntime(Module &M) { UpgradeToIntrinsic(I.first, I.second); } +// Upgrade from wrapping each pointer stored in the @llvm.global_(ctors|dtors) +// with ptrauth constant expression to storing plain pointers in these arrays +// and requesting the particular signing schema globally via module flags. +// +// Only perform the upgrade if all elements of *both* arrays agree on a common +// signing schema. The processing of the array is *not* stopped on the first +// null function pointer. +static bool upgradePtrauthInitFiniArrays(Module &M) { + // Either "not decided yet" or whether we should request address diversity + // in addition to the basic constant diversity. + // There is no value representing "decided not to sign", as this results + // in immediate return from upgradePtrauthInitFiniArrays. + std::optional<bool> UseAddressDisc; + + // Do not attempt upgrading if the new module flags already exist. + if (const NamedMDNode *ModFlags = M.getModuleFlagsMetadata()) { + for (const MDNode *Flag : ModFlags->operands()) { + if (Flag->getNumOperands() != 3) + continue; + const MDString *ID = dyn_cast_or_null<MDString>(Flag->getOperand(1)); + if (ID && (ID->getString() == "ptrauth-init-fini" || + ID->getString() == "ptrauth-init-fini-address-discriminator")) + return false; + } + } + + auto UpgradeSinglePointer = [&UseAddressDisc](Constant *CV) -> Constant * { + const unsigned ExpectedConstDisc = 0xD9D4; + const unsigned ExpectedAddressMarker = 1; + + auto *CPA = dyn_cast<ConstantPtrAuth>(CV); + if (!CPA || !CPA->getDiscriminator()->equalsInt(ExpectedConstDisc)) + return nullptr; // Nothing to upgrade or unknown pattern found. + + bool HasAddressDisc; + if (!CPA->hasAddressDiscriminator()) + HasAddressDisc = false; + else if (CPA->hasSpecialAddressDiscriminator(ExpectedAddressMarker)) + HasAddressDisc = true; + else + return nullptr; // Unknown pattern. + + if (UseAddressDisc && *UseAddressDisc != HasAddressDisc) + return nullptr; // Disagreement with the decided mode. + + UseAddressDisc = HasAddressDisc; + return CPA->getPointer(); + }; + + SmallVector<std::pair<GlobalVariable *, Constant *>> PendingUpgrades; + for (const char *Name : {"llvm.global_ctors", "llvm.global_dtors"}) { + auto *GV = dyn_cast_if_present<GlobalVariable>(M.getNamedValue(Name)); + if (!GV || !GV->hasInitializer()) + continue; // Skip, but it is okay to upgrade the other variable. + + auto *Init = dyn_cast<ConstantArray>(GV->getInitializer()); + if (!Init) + return false; + + std::vector<Constant *> NewStructors; + NewStructors.reserve(Init->getNumOperands()); + for (Use &U : Init->operands()) { + auto *Structor = dyn_cast<ConstantStruct>(U.get()); + if (!Structor || Structor->getNumOperands() != 3) + return false; + + Constant *Prio = Structor->getOperand(0); + Constant *Func = UpgradeSinglePointer(Structor->getOperand(1)); + Constant *Arg = Structor->getOperand(2); + if (!Func) + return false; + + NewStructors.push_back( + ConstantStruct::get(Structor->getType(), {Prio, Func, Arg})); + } + + Constant *NewInit = ConstantArray::get(Init->getType(), NewStructors); + PendingUpgrades.push_back({GV, NewInit}); + } + + if (PendingUpgrades.empty()) + return false; + assert(UseAddressDisc.has_value()); + + for (auto [GV, NewInit] : PendingUpgrades) + GV->setInitializer(NewInit); + M.addModuleFlag(Module::Error, "ptrauth-init-fini", 1); + if (UseAddressDisc.value()) + M.addModuleFlag(Module::Error, "ptrauth-init-fini-address-discriminator", + 1); + + return true; +} + bool llvm::UpgradeModuleFlags(Module &M) { + bool Changed = false; + Changed |= upgradePtrauthInitFiniArrays(M); + NamedMDNode *ModFlags = M.getModuleFlagsMetadata(); if (!ModFlags) - return false; + return Changed; - bool HasObjCFlag = false, HasClassProperties = false, Changed = false; + bool HasObjCFlag = false, HasClassProperties = false; bool HasSwiftVersionFlag = false; uint8_t SwiftMajorVersion, SwiftMinorVersion; uint32_t SwiftABIVersion; diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 6d900ce0d917f..6422ce8e0405a 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -930,6 +930,19 @@ void Verifier::visitGlobalVariable(const GlobalVariable &GV) { Check(ETy->isPointerTy(), "wrong type for intrinsic global variable", &GV); } + + auto *Init = GV.hasInitializer() + ? dyn_cast<ConstantArray>(GV.getInitializer()) + : nullptr; + if (Init) { + for (const Use &U : Init->operands()) { + auto *Structor = dyn_cast<ConstantStruct>(U); + if (!Structor || Structor->getNumOperands() != 3) + continue; + Check(!isa<ConstantPtrAuth>(Structor->getOperand(1)), + "signing of ctors/dtors should be requested via module flags"); + } + } } if (GV.hasName() && (GV.getName() == "llvm.used" || @@ -1998,26 +2011,49 @@ void Verifier::visitModuleFlags() { // Scan each flag, and track the flags and requirements. DenseMap<const MDString*, const MDNode*> SeenIDs; SmallVector<const MDNode*, 16> Requirements; - uint64_t PAuthABIPlatform = -1; - uint64_t PAuthABIVersion = -1; + std::optional<uint64_t> PAuthABIPlatform; + std::optional<uint64_t> PAuthABIVersion; + std::optional<uint64_t> HasPtrauthInitFini; + std::optional<uint64_t> HasPtrauthInitFiniAddr; + for (const MDNode *MDN : Flags->operands()) { visitModuleFlag(MDN, SeenIDs, Requirements); if (MDN->getNumOperands() != 3) continue; + if (const auto *FlagName = dyn_cast_or_null<MDString>(MDN->getOperand(1))) { - if (FlagName->getString() == "aarch64-elf-pauthabi-platform") { - if (const auto *PAP = + auto GetFlagNamed = [&](StringRef Name) -> std::optional<uint64_t> { + if (FlagName->getString() != Name) + return std::nullopt; + if (const auto *FlagValue = mdconst::dyn_extract_or_null<ConstantInt>(MDN->getOperand(2))) - PAuthABIPlatform = PAP->getZExtValue(); - } else if (FlagName->getString() == "aarch64-elf-pauthabi-version") { - if (const auto *PAV = - mdconst::dyn_extract_or_null<ConstantInt>(MDN->getOperand(2))) - PAuthABIVersion = PAV->getZExtValue(); - } + return FlagValue->getZExtValue(); + + CheckFailed(Name + ": module flag expects integer value"); + return std::nullopt; + }; + + if (auto Value = GetFlagNamed("aarch64-elf-pauthabi-platform")) + PAuthABIPlatform = *Value; + else if (auto Value = GetFlagNamed("aarch64-elf-pauthabi-version")) + PAuthABIVersion = *Value; + else if (auto Value = GetFlagNamed("ptrauth-init-fini")) + HasPtrauthInitFini = *Value; + else if (auto Value = + GetFlagNamed("ptrauth-init-fini-address-discrimination")) + HasPtrauthInitFiniAddr = *Value; } } - if ((PAuthABIPlatform == uint64_t(-1)) != (PAuthABIVersion == uint64_t(-1))) + Check(!HasPtrauthInitFini || HasPtrauthInitFini.value() == 1, + "ptrauth-init-fini must be set to 1 or unset"); + Check(!HasPtrauthInitFiniAddr || HasPtrauthInitFiniAddr.value() == 1, + "ptrauth-init-fini-address-discrimination must be set to 1 or unset"); + if (HasPtrauthInitFiniAddr) + Check(HasPtrauthInitFini, "ptrauth-init-fini-address-discrimination module " + "flag requires ptrauth-init-fini"); + + if (PAuthABIPlatform.has_value() != PAuthABIVersion.has_value()) CheckFailed("either both or no 'aarch64-elf-pauthabi-platform' and " "'aarch64-elf-pauthabi-version' module flags must be present"); diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp index b16c0460adf38..936bb794b608f 100644 --- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -98,6 +98,8 @@ class AArch64AsmPrinter : public AsmPrinter { FaultMaps FM; const AArch64Subtarget *STI; bool ShouldEmitWeakSwiftAsyncExtendedFramePointerFlags = false; + bool PtrauthInitFini = false; + bool PtrauthInitFiniAddressDisc = false; #ifndef NDEBUG unsigned InstsEmitted; #endif @@ -405,6 +407,11 @@ void AArch64AsmPrinter::emitStartOfAsmFile(Module &M) { EnableImportCallOptimization = true; } + if (M.getModuleFlag("ptrauth-init-fini")) + PtrauthInitFini = true; + if (M.getModuleFlag("ptrauth-init-fini-address-discrimination")) + PtrauthInitFiniAddressDisc = true; + if (!TT.isOSBinFormatELF()) return; @@ -1461,18 +1468,31 @@ void AArch64AsmPrinter::emitFunctionEntryLabel() { void AArch64AsmPrinter::emitXXStructor(const DataLayout &DL, const Constant *CV) { - if (const auto *CPA = dyn_cast<ConstantPtrAuth>(CV)) - if (CPA->hasAddressDiscriminator() && - !CPA->hasSpecialAddressDiscriminator( - ConstantPtrAuth::AddrDiscriminator_CtorsDtors)) - report_fatal_error( - "unexpected address discrimination value for ctors/dtors entry, only " - "'ptr inttoptr (i64 1 to ptr)' is allowed"); - // If we have signed pointers in xxstructors list, they'll be lowered to @AUTH - // MCExpr's via AArch64AsmPrinter::lowerConstantPtrAuth. It does not look at - // actual address discrimination value and only checks - // hasAddressDiscriminator(), so it's OK to leave special address - // discrimination value here. + LLVMContext &C = CV->getContext(); + assert(!isa<ConstantPtrAuth>(CV) && + "ctors/dtors are to be signed by asm printer"); + + if (PtrauthInitFini) { + IntegerType *Int32Ty = IntegerType::get(C, 32); + IntegerType *Int64Ty = IntegerType::get(C, 64); + PointerType *PtrTy = PointerType::get(C, 0); + + ConstantInt *Key = ConstantInt::get(Int32Ty, AArch64PAuth::InitFiniKey); + ConstantInt *IntDisc = ConstantInt::get( + Int64Ty, AArch64PAuth::InitFiniPointerConstantDiscriminator); + Constant *Null = ConstantPointerNull::get(PtrTy); + Constant *AddressDisc = Null; + if (PtrauthInitFiniAddressDisc) { + uint64_t Marker = ConstantPtrAuth::AddrDiscriminator_CtorsDtors; + AddressDisc = + ConstantExpr::getIntToPtr(ConstantInt::get(Int64Ty, Marker), PtrTy); + } + + CV = ConstantPtrAuth::get(const_cast<Constant *>(CV), Key, IntDisc, + AddressDisc, /*DeactivationSymbol=*/Null); + } + + // Signed pointers will be lowered by AArch64AsmPrinter::lowerConstantPtrAuth. AsmPrinter::emitXXStructor(DL, CV); } diff --git a/llvm/lib/Target/AArch64/AArch64PointerAuth.h b/llvm/lib/Target/AArch64/AArch64PointerAuth.h index d6947f0d22aac..1e69558e996d1 100644 --- a/llvm/lib/Target/AArch64/AArch64PointerAuth.h +++ b/llvm/lib/Target/AArch64/AArch64PointerAuth.h @@ -9,12 +9,18 @@ #ifndef LLVM_LIB_TARGET_AARCH64_AARCH64POINTERAUTH_H #define LLVM_LIB_TARGET_AARCH64_AARCH64POINTERAUTH_H -#include "llvm/CodeGen/MachineBasicBlock.h" -#include "llvm/CodeGen/Register.h" +#include "Utils/AArch64BaseInfo.h" namespace llvm { namespace AArch64PAuth { +/// PAuth key to be used with function pointers in .init_array and .fini_array. +constexpr AArch64PACKey::ID InitFiniKey = AArch64PACKey::IA; + +/// Constant discriminator to be used with function pointers in .init_array and +/// .fini_array. The value is ptrauth_string_discriminator("init_fini") +constexpr unsigned InitFiniPointerConstantDiscriminator = 0xD9D4; + /// Variants of check performed on an authenticated pointer. /// /// In cases such as authenticating the LR value when performing a tail call diff --git a/llvm/test/CodeGen/AArch64/ptrauth-init-fini-autoupgrade.ll b/llvm/test/CodeGen/AArch64/ptrauth-init-fini-autoupgrade.ll new file mode 100644 index 0000000000000..68efd9c2a8b7e --- /dev/null +++ b/llvm/test/CodeGen/AArch64/ptrauth-init-fini-autoupgrade.ll @@ -0,0 +1,143 @@ +; RUN: rm -rf %t && split-file %s %t && cd %t + +;--- nodisc.ll + +; RUN: opt -S < nodisc.ll | FileCheck %s --check-prefix=NODISC + [email protected]_ctors = appending global [2 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr ptrauth (ptr @foo1, i32 0, i64 55764), ptr null }, { i32, ptr, ptr } { i32 65535, ptr ptrauth (ptr @foo2, i32 0, i64 55764), ptr null }] [email protected]_dtors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr ptrauth (ptr @bar, i32 0, i64 55764), ptr null }] + +define void @foo1() { + ret void +} + +define void @foo2() { + ret void +} + +define void @bar() { + ret void +} + +; NODISC: @llvm.global_ctors = appending global [2 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @foo1, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @foo2, ptr null }] +; NODISC: @llvm.global_dtors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @bar, ptr null }] +; NODISC: !llvm.module.flags = !{!0} +; NODISC: !0 = !{i32 1, !"ptrauth-init-fini", i32 1} + +;--- disc.ll + +; RUN: opt -S < disc.ll | FileCheck %s --check-prefix=DISC + [email protected]_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr ptrauth (ptr @foo, i32 0, i64 55764, ptr inttoptr (i64 1 to ptr)), ptr null }] [email protected]_dtors = appending global [2 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr ptrauth (ptr @bar1, i32 0, i64 55764, ptr inttoptr (i64 1 to ptr)), ptr null }, { i32, ptr, ptr } { i32 65535, ptr ptrauth (ptr @bar2, i32 0, i64 55764, ptr inttoptr (i64 1 to ptr)), ptr null }] + +define void @foo() { + ret void +} + +define void @bar1() { + ret void +} + +define void @bar2() { + ret void +} + +; DISC: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @foo, ptr null }] +; DISC: @llvm.global_dtors = appending global [2 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @bar1, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @bar2, ptr null }] +; DISC: !llvm.module.flags = !{!0, !1} +; DISC: !0 = !{i32 1, !"ptrauth-init-fini", i32 1} +; DISC: !1 = !{i32 1, !"ptrauth-init-fini-address-discriminator", i32 1} + +;--- err1.ll + +; RUN: not opt -S < err1.ll 2>&1 | FileCheck %s --check-prefix=ERR1 + +; ERR1: signing of ctors/dtors should be requested via module flags + [email protected]_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr ptrauth (ptr @foo, i32 0, i64 55764, ptr inttoptr (i64 2 to ptr)), ptr null }] + +define void @foo() { + ret void +} + +;--- err2.ll + +; RUN: not opt -S < err2.ll 2>&1 | FileCheck %s --check-prefix=ERR2 + +; ERR2: signing of ctors/dtors should be requested via module flags + +@g = external global ptr [email protected]_dtors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr ptrauth (ptr @bar, i32 0, i64 55764, ptr @g), ptr null }] + +define void @bar() { + ret void +} + +;--- disagreement1.ll + +; RUN: not opt -S < disagreement1.ll 2>&1 | FileCheck %s --check-prefix=DISAGREEMENT1 + +; DISAGREEMENT1: signing of ctors/dtors should be requested via module flags + [email protected]_ctors = appending global [2 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr ptrauth (ptr @foo, i32 0, i64 55764), ptr null }, { i32, ptr, ptr } { i32 65535, ptr @bar, ptr null }] + +define void @foo() { + ret void +} + +define void @bar() { + ret void +} + +;--- disagreement2.ll + +; RUN: not opt -S < disagreement2.ll 2>&1 | FileCheck %s --check-prefix=DISAGREEMENT2 + +; DISAGREEMENT2: signing of ctors/dtors should be requested via module flags + [email protected]_ctors = appending global [2 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr ptrauth (ptr @foo, i32 0, i64 55764), ptr null }, { i32, ptr, ptr } { i32 65535, ptr ptrauth (ptr @bar, i32 0, i64 55764, ptr inttoptr (i64 1 to ptr)), ptr null }] + +define void @foo() { + ret void +} + +define void @bar() { + ret void +} + +;--- disagreement3.ll + +; RUN: not opt -S < disagreement3.ll 2>&1 | FileCheck %s --check-prefix=DISAGREEMENT3 + +; DISAGREEMENT3: signing of ctors/dtors should be requested via module flags + [email protected]_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr ptrauth (ptr @foo, i32 0, i64 55764), ptr null }] [email protected]_dtors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr ptrauth (ptr @bar, i32 0, i64 55764, ptr inttoptr (i64 1 to ptr)), ptr null }] + +define void @foo() { + ret void +} + +define void @bar() { + ret void +} + +;--- existing-flags.ll + +; RUN: not opt -S < existing-flags.ll 2>&1 | FileCheck %s --check-prefix=EXISTING-FLAGS + +; EXISTING-FLAGS: signing of ctors/dtors should be requested via module flags + [email protected]_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr ptrauth (ptr @foo, i32 0, i64 55764), ptr null }] + +define void @foo() { + ret void +} + +define void @bar() { + ret void +} + +!llvm.module.flags = !{!0} +!0 = !{i32 1, !"ptrauth-init-fini", i32 1} diff --git a/llvm/test/CodeGen/AArch64/ptrauth-init-fini.ll b/llvm/test/CodeGen/AArch64/ptrauth-init-fini.ll index 186a31c63ba10..e3b1f3835f553 100644 --- a/llvm/test/CodeGen/AArch64/ptrauth-init-fini.ll +++ b/llvm/test/CodeGen/AArch64/ptrauth-init-fini.ll @@ -27,8 +27,8 @@ ;; ^^^^ 0xD9D4: constant discriminator = 55764 ;; ^^ 0x80: bits 61..60 key = IA; bit 63 addr disc = false [email protected]_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr ptrauth (ptr @foo, i32 0, i64 55764), ptr null }] [email protected]_dtors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr ptrauth (ptr @bar, i32 0, i64 55764), ptr null }] [email protected]_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @foo, ptr null }] [email protected]_dtors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @bar, ptr null }] define void @foo() { ret void @@ -38,6 +38,9 @@ define void @bar() { ret void } +!llvm.module.flags = !{!0} +!0 = !{i32 1, !"ptrauth-init-fini", i32 1} + ;--- disc.ll ; RUN: llc -mtriple aarch64-elf -mattr=+pauth -filetype=asm -o - disc.ll | \ @@ -65,8 +68,8 @@ define void @bar() { ;; ^^^^ 0xD9D4: constant discriminator = 55764 ;; ^^ 0x80: bits 61..60 key = IA; bit 63 addr disc = true [email protected]_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr ptrauth (ptr @foo, i32 0, i64 55764, ptr inttoptr (i64 1 to ptr)), ptr null }] [email protected]_dtors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr ptrauth (ptr @bar, i32 0, i64 55764, ptr inttoptr (i64 1 to ptr)), ptr null }] [email protected]_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @foo, ptr null }] [email protected]_dtors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @bar, ptr null }] define void @foo() { ret void @@ -76,29 +79,85 @@ define void @bar() { ret void } +!llvm.module.flags = !{!0, !1} +!0 = !{i32 1, !"ptrauth-init-fini", i32 1} +!1 = !{i32 1, !"ptrauth-init-fini-address-discrimination", i32 1} + ;--- err1.ll -; RUN: not --crash llc -mtriple aarch64-elf -mattr=+pauth -filetype=asm -o - err1.ll 2>&1 | \ -; RUN: FileCheck %s --check-prefix=ERR1 +; RUN: not opt -S < err1.ll 2>&1 | FileCheck %s --check-prefix=ERR1 -; ERR1: LLVM ERROR: unexpected address discrimination value for ctors/dtors entry, only 'ptr inttoptr (i64 1 to ptr)' is allowed +; ERR1: ptrauth-init-fini must be set to 1 or unset [email protected]_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr ptrauth (ptr @foo, i32 0, i64 55764, ptr inttoptr (i64 2 to ptr)), ptr null }] [email protected]_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @foo, ptr null }] define void @foo() { ret void } +!llvm.module.flags = !{!0, !1} +!0 = !{i32 1, !"ptrauth-init-fini", i32 0} +!1 = !{i32 1, !"ptrauth-init-fini-address-discrimination", i32 1} + ;--- err2.ll -; RUN: not --crash llc -mtriple aarch64-elf -mattr=+pauth -filetype=asm -o - err2.ll 2>&1 | \ -; RUN: FileCheck %s --check-prefix=ERR2 +; RUN: not opt -S < err2.ll 2>&1 | FileCheck %s --check-prefix=ERR2 -; ERR2: LLVM ERROR: unexpected address discrimination value for ctors/dtors entry, only 'ptr inttoptr (i64 1 to ptr)' is allowed +; ERR2: ptrauth-init-fini-address-discrimination must be set to 1 or unset -@g = external global ptr [email protected]_dtors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr ptrauth (ptr @bar, i32 0, i64 55764, ptr @g), ptr null }] [email protected]_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @foo, ptr null }] -define void @bar() { +define void @foo() { ret void } + +!llvm.module.flags = !{!0, !1} +!0 = !{i32 1, !"ptrauth-init-fini", i32 1} +!1 = !{i32 1, !"ptrauth-init-fini-address-discrimination", i32 0} + +;--- err3.ll + +; RUN: not opt -S < err3.ll 2>&1 | FileCheck %s --check-prefix=ERR3 + +; ERR3: ptrauth-init-fini: module flag expects integer value + [email protected]_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @foo, ptr null }] + +define void @foo() { + ret void +} + +!llvm.module.flags = !{!0, !1} +!0 = !{i32 1, !"ptrauth-init-fini", !"1"} +!1 = !{i32 1, !"ptrauth-init-fini-address-discrimination", i32 1} + +;--- err4.ll + +; RUN: not opt -S < err4.ll 2>&1 | FileCheck %s --check-prefix=ERR4 + +; ERR4: ptrauth-init-fini-address-discrimination: module flag expects integer value + [email protected]_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @foo, ptr null }] + +define void @foo() { + ret void +} + +!llvm.module.flags = !{!0, !1} +!0 = !{i32 1, !"ptrauth-init-fini", i32 1} +!1 = !{i32 1, !"ptrauth-init-fini-address-discrimination", !"1"} + +;--- err5.ll + +; RUN: not opt -S < err5.ll 2>&1 | FileCheck %s --check-prefix=ERR5 + +; ERR5: ptrauth-init-fini-address-discrimination module flag requires ptrauth-init-fini + [email protected]_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @foo, ptr null }] + +define void @foo() { + ret void +} + +!llvm.module.flags = !{!0} +!0 = !{i32 1, !"ptrauth-init-fini-address-discrimination", i32 1} >From 593568efe82a32c196f2e19979b0455ee9d2efc3 Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko <[email protected]> Date: Fri, 12 Jun 2026 17:51:40 +0300 Subject: [PATCH 2/3] Improve readability --- llvm/lib/IR/AutoUpgrade.cpp | 60 +++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/llvm/lib/IR/AutoUpgrade.cpp b/llvm/lib/IR/AutoUpgrade.cpp index 3d699cd0bb7ad..6197c479993f9 100644 --- a/llvm/lib/IR/AutoUpgrade.cpp +++ b/llvm/lib/IR/AutoUpgrade.cpp @@ -5884,6 +5884,14 @@ Constant *llvm::UpgradeBitCastExpr(unsigned Opc, Constant *C, Type *DestTy) { return nullptr; } +static std::optional<StringRef> getModuleFlagNameSafely(const MDNode &Flag) { + if (Flag.getNumOperands() < 3) + return std::nullopt; + if (MDString *Name = dyn_cast_or_null<MDString>(Flag.getOperand(1))) + return Name->getString(); + return std::nullopt; +} + /// Check the debug info version number, if it is out-dated, drop the debug /// info. Return true if module is modified. bool llvm::UpgradeDebugInfo(Module &M) { @@ -5897,10 +5905,8 @@ bool llvm::UpgradeDebugInfo(Module &M) { unsigned Version = 0; if (NamedMDNode *ModFlags = M.getModuleFlagsMetadata()) { auto OpIt = find_if(ModFlags->operands(), [](const MDNode *Flag) { - if (Flag->getNumOperands() < 3) - return false; - if (MDString *K = dyn_cast_or_null<MDString>(Flag->getOperand(1))) - return K->getString() == "Debug Info Version"; + if (auto Name = getModuleFlagNameSafely(*Flag)) + return *Name == "Debug Info Version"; return false; }); if (OpIt != ModFlags->op_end()) { @@ -6217,13 +6223,11 @@ void llvm::UpgradeARCRuntime(Module &M) { UpgradeToIntrinsic(I.first, I.second); } -// Upgrade from wrapping each pointer stored in the @llvm.global_(ctors|dtors) -// with ptrauth constant expression to storing plain pointers in these arrays -// and requesting the particular signing schema globally via module flags. +// Upgrade from storing `ptrauth` constants in `@llvm.global_(ctors|dtors)` +// arrays to configuring signing of pointers to *structors via module flags. // // Only perform the upgrade if all elements of *both* arrays agree on a common -// signing schema. The processing of the array is *not* stopped on the first -// null function pointer. +// signing schema. static bool upgradePtrauthInitFiniArrays(Module &M) { // Either "not decided yet" or whether we should request address diversity // in addition to the basic constant diversity. @@ -6234,11 +6238,9 @@ static bool upgradePtrauthInitFiniArrays(Module &M) { // Do not attempt upgrading if the new module flags already exist. if (const NamedMDNode *ModFlags = M.getModuleFlagsMetadata()) { for (const MDNode *Flag : ModFlags->operands()) { - if (Flag->getNumOperands() != 3) - continue; - const MDString *ID = dyn_cast_or_null<MDString>(Flag->getOperand(1)); - if (ID && (ID->getString() == "ptrauth-init-fini" || - ID->getString() == "ptrauth-init-fini-address-discriminator")) + std::optional<StringRef> Name = getModuleFlagNameSafely(*Flag); + if (Name && (*Name == "ptrauth-init-fini" || + *Name == "ptrauth-init-fini-address-discriminator")) return false; } } @@ -6266,26 +6268,32 @@ static bool upgradePtrauthInitFiniArrays(Module &M) { return CPA->getPointer(); }; - SmallVector<std::pair<GlobalVariable *, Constant *>> PendingUpgrades; + // Do not apply any changes until we know the upgrade is non-ambiguous. + using PendingUpgrade = std::pair<GlobalVariable *, Constant *>; + SmallVector<PendingUpgrade, 2> GlobalArraysToUpgrade; + for (const char *Name : {"llvm.global_ctors", "llvm.global_dtors"}) { auto *GV = dyn_cast_if_present<GlobalVariable>(M.getNamedValue(Name)); if (!GV || !GV->hasInitializer()) continue; // Skip, but it is okay to upgrade the other variable. - auto *Init = dyn_cast<ConstantArray>(GV->getInitializer()); - if (!Init) + auto *OldStructorsArray = dyn_cast<ConstantArray>(GV->getInitializer()); + if (!OldStructorsArray) return false; std::vector<Constant *> NewStructors; - NewStructors.reserve(Init->getNumOperands()); - for (Use &U : Init->operands()) { - auto *Structor = dyn_cast<ConstantStruct>(U.get()); + NewStructors.reserve(OldStructorsArray->getNumOperands()); + + for (Use &U : OldStructorsArray->operands()) { + ConstantStruct *Structor = dyn_cast<ConstantStruct>(U.get()); if (!Structor || Structor->getNumOperands() != 3) return false; Constant *Prio = Structor->getOperand(0); - Constant *Func = UpgradeSinglePointer(Structor->getOperand(1)); + Constant *Func = Structor->getOperand(1); Constant *Arg = Structor->getOperand(2); + + Func = UpgradeSinglePointer(Func); if (!Func) return false; @@ -6293,16 +6301,18 @@ static bool upgradePtrauthInitFiniArrays(Module &M) { ConstantStruct::get(Structor->getType(), {Prio, Func, Arg})); } - Constant *NewInit = ConstantArray::get(Init->getType(), NewStructors); - PendingUpgrades.push_back({GV, NewInit}); + Constant *NewInit = + ConstantArray::get(OldStructorsArray->getType(), NewStructors); + GlobalArraysToUpgrade.push_back({GV, NewInit}); } - if (PendingUpgrades.empty()) + if (GlobalArraysToUpgrade.empty()) return false; assert(UseAddressDisc.has_value()); - for (auto [GV, NewInit] : PendingUpgrades) + for (auto [GV, NewInit] : GlobalArraysToUpgrade) GV->setInitializer(NewInit); + M.addModuleFlag(Module::Error, "ptrauth-init-fini", 1); if (UseAddressDisc.value()) M.addModuleFlag(Module::Error, "ptrauth-init-fini-address-discriminator", >From 044c9b73bffa30800bdb451e01598de313383f55 Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko <[email protected]> Date: Fri, 12 Jun 2026 17:52:12 +0300 Subject: [PATCH 3/3] Fix module flag name spelling in AutoUpgrade.cpp --- llvm/lib/IR/AutoUpgrade.cpp | 4 ++-- llvm/test/CodeGen/AArch64/ptrauth-init-fini-autoupgrade.ll | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/llvm/lib/IR/AutoUpgrade.cpp b/llvm/lib/IR/AutoUpgrade.cpp index 6197c479993f9..3412ef1a2ca59 100644 --- a/llvm/lib/IR/AutoUpgrade.cpp +++ b/llvm/lib/IR/AutoUpgrade.cpp @@ -6240,7 +6240,7 @@ static bool upgradePtrauthInitFiniArrays(Module &M) { for (const MDNode *Flag : ModFlags->operands()) { std::optional<StringRef> Name = getModuleFlagNameSafely(*Flag); if (Name && (*Name == "ptrauth-init-fini" || - *Name == "ptrauth-init-fini-address-discriminator")) + *Name == "ptrauth-init-fini-address-discrimination")) return false; } } @@ -6315,7 +6315,7 @@ static bool upgradePtrauthInitFiniArrays(Module &M) { M.addModuleFlag(Module::Error, "ptrauth-init-fini", 1); if (UseAddressDisc.value()) - M.addModuleFlag(Module::Error, "ptrauth-init-fini-address-discriminator", + M.addModuleFlag(Module::Error, "ptrauth-init-fini-address-discrimination", 1); return true; diff --git a/llvm/test/CodeGen/AArch64/ptrauth-init-fini-autoupgrade.ll b/llvm/test/CodeGen/AArch64/ptrauth-init-fini-autoupgrade.ll index 68efd9c2a8b7e..c07ffd8c7e4c4 100644 --- a/llvm/test/CodeGen/AArch64/ptrauth-init-fini-autoupgrade.ll +++ b/llvm/test/CodeGen/AArch64/ptrauth-init-fini-autoupgrade.ll @@ -47,7 +47,7 @@ define void @bar2() { ; DISC: @llvm.global_dtors = appending global [2 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @bar1, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @bar2, ptr null }] ; DISC: !llvm.module.flags = !{!0, !1} ; DISC: !0 = !{i32 1, !"ptrauth-init-fini", i32 1} -; DISC: !1 = !{i32 1, !"ptrauth-init-fini-address-discriminator", i32 1} +; DISC: !1 = !{i32 1, !"ptrauth-init-fini-address-discrimination", i32 1} ;--- err1.ll _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
