diff --git a/llvm/docs/PointerAuth.md b/llvm/docs/PointerAuth.md --- a/llvm/docs/PointerAuth.md +++ b/llvm/docs/PointerAuth.md @@ -14,6 +14,8 @@ * a [set of intrinsics](#intrinsics) (to sign/authenticate pointers) * a [call operand bundle](#operand-bundle) (to authenticate called pointers) +* a [special section and relocation](#authenticated-global-relocation) + (to sign globals) The current implementation leverages the [Armv8.3-A PAuth/Pointer Authentication Code](#armv8-3-a-pauth-pointer-authentication-code) @@ -262,6 +264,41 @@ are not stored to (and reloaded from) memory. +### Authenticated Global Relocation + +[Intrinsics](#intrinsics) can be used to produce signed pointers dynamically, +in code, but not for signed pointers referenced by constants, in, e.g., global +initializers. + +The latter are represented using a special kind of global describing an +authenticated relocation (producing a signed pointer). + +These special global must live in section '``llvm.ptrauth``', and have a +specific type. + +```llvm +@fp.ptrauth = constant { ptr, i32, i64, i64 } + { ptr , + i32 , + i64
, + i64 + }, section "llvm.ptrauth" +``` + +is equivalent to ``@fp.ptrauth`` being initialized with: + +```llvm + %disc = call i64 @llvm.ptrauth.blend.i64(i64
, i64 ) + %fp_i = ptrtoint ptr to i64 + %signed_fp = call i64 @llvm.ptrauth.sign.i64(i64 %fp_i, i32 , i64 %disc) + store i64 %signed_fp, i8* @fp.ptrauth +``` + +Note that this is a temporary representation, chosen to minimize the required +changes. Ideally, this would simply be a new kind of ConstantExpr. + + + ## AArch64 Support AArch64 is currently the only architecture with full support of the pointer diff --git a/llvm/include/llvm/IR/GlobalPtrAuthInfo.h b/llvm/include/llvm/IR/GlobalPtrAuthInfo.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/IR/GlobalPtrAuthInfo.h @@ -0,0 +1,113 @@ +//===- GlobalPtrAuthInfo.h - Analysis tools for ptrauth globals -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// This file contains a set of utilities to analyze llvm.ptrauth globals, and +/// to decompose them into key, discriminator, and base pointer. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_IR_GLOBALPTRAUTHINFO_H +#define LLVM_IR_GLOBALPTRAUTHINFO_H + +#include "llvm/IR/Constants.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/Support/Error.h" + +#include + +namespace llvm { + +/// Helper class to access the information regarding an "llvm.ptrauth" global. +/// These globals are of the form: +/// @sg = constant { ptr, i32, i64, i64 } +/// { ptr @g, ; base pointer +/// i32 2, ; key ID +/// i64 ptrtoint (ptr @pg to i64), ; address discriminator +/// i64 42 ; discriminator +/// }, section "llvm.ptrauth" +/// +class GlobalPtrAuthInfo { + const GlobalVariable *GV; + + const ConstantStruct *getInitializer() const { + return cast(GV->getInitializer()); + } + + GlobalPtrAuthInfo(const GlobalVariable *GV) : GV(GV) {} + +public: + /// Try to analyze \p V as an authenticated global reference, and return its + /// information if successful. + static std::optional analyze(const Value *V); + + /// Try to analyze \p V as an authenticated global reference, and return its + /// information if successful, or an error explaining the failure if not. + static Expected tryAnalyze(const Value *V); + + /// Access the information contained in the "llvm.ptrauth" globals. + /// @{ + /// The "llvm.ptrauth" global itself. + const GlobalVariable *getGV() const { return GV; } + + /// The pointer that is authenticated in this authenticated global reference. + Constant *getPointer() const { + return cast(getInitializer()->getOperand(0)); + } + + /// The Key ID, an i32 constant. + ConstantInt *getKey() const { + return cast(getInitializer()->getOperand(1)); + } + + /// The address discriminator if any, or the null constant. + /// If present, this must be a value equivalent to the storage location of + /// the only user of the authenticated ptrauth global. + Constant *getAddrDiscriminator() const { + return cast(getInitializer()->getOperand(2)); + } + + /// Whether there is any non-null address discriminator. + bool hasAddressDiversity() const { + return !getAddrDiscriminator()->isNullValue(); + } + + /// Whether the address uses a special address discriminator. + /// These discriminators can't be used in real pointer-auth values; they + /// can only be used in "prototype" values that indicate how some real + /// schema is supposed to be produced. + bool hasSpecialAddressDiscriminator(uint64_t Value) const { + auto *CI = dyn_cast(getAddrDiscriminator()); + return CI && CI->getValue() == Value; + } + + /// The discriminator. + ConstantInt *getDiscriminator() const { + return cast(getInitializer()->getOperand(3)); + } + /// @} + + /// Check whether an authentication operation with key \p Key and (possibly + /// blended) discriminator \p Discriminator is compatible with this + /// authenticated global reference. + bool isCompatibleWith(const Value *Key, const Value *Discriminator, + const DataLayout &DL) const; + + /// Produce a "llvm.ptrauth" global that signs a value using the given + /// schema. + static Constant *create(Module &M, Constant *Pointer, ConstantInt *Key, + Constant *AddrDiscriminator, + ConstantInt *Discriminator); + + /// Produce a new "llvm.ptrauth" global for signing the given value using + /// the same schema as is stored in this info. + Constant *createWithSameSchema(Module &M, Constant *Pointer) const; +}; + +} // end namespace llvm + +#endif diff --git a/llvm/include/llvm/IR/GlobalValue.h b/llvm/include/llvm/IR/GlobalValue.h --- a/llvm/include/llvm/IR/GlobalValue.h +++ b/llvm/include/llvm/IR/GlobalValue.h @@ -521,9 +521,7 @@ bool hasInternalLinkage() const { return isInternalLinkage(getLinkage()); } bool hasPrivateLinkage() const { return isPrivateLinkage(getLinkage()); } bool hasLocalLinkage() const { return isLocalLinkage(getLinkage()); } - bool hasExternalWeakLinkage() const { - return isExternalWeakLinkage(getLinkage()); - } + bool hasExternalWeakLinkage() const; bool hasCommonLinkage() const { return isCommonLinkage(getLinkage()); } bool hasValidDeclarationLinkage() const { return isValidDeclarationLinkage(getLinkage()); diff --git a/llvm/lib/IR/CMakeLists.txt b/llvm/lib/IR/CMakeLists.txt --- a/llvm/lib/IR/CMakeLists.txt +++ b/llvm/lib/IR/CMakeLists.txt @@ -25,6 +25,7 @@ Function.cpp GCStrategy.cpp GVMaterializer.cpp + GlobalPtrAuthInfo.cpp Globals.cpp IRBuilder.cpp IRPrintingPasses.cpp diff --git a/llvm/lib/IR/GlobalPtrAuthInfo.cpp b/llvm/lib/IR/GlobalPtrAuthInfo.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/IR/GlobalPtrAuthInfo.cpp @@ -0,0 +1,142 @@ +//===- GlobalPtrAuthInfo.cpp - Analysis tools for ptrauth globals ---------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/GlobalPtrAuthInfo.h" + +#include "llvm/ADT/APInt.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Operator.h" + +using namespace llvm; + +Expected GlobalPtrAuthInfo::tryAnalyze(const Value *V) { + auto Invalid = [](const Twine &Reason) { + return make_error(Reason, inconvertibleErrorCode()); + }; + + auto &Ctx = V->getContext(); + + V = V->stripPointerCasts(); + + auto *GV = dyn_cast(V); + if (!GV) + return Invalid("value isn't a global"); + + if (GV->getSection() != "llvm.ptrauth") + return Invalid("global isn't in section \"llvm.ptrauth\""); + + if (!GV->hasInitializer()) + return Invalid("global doesn't have an initializer"); + + auto *Init = GV->getInitializer(); + + auto *Ty = dyn_cast(GV->getInitializer()->getType()); + if (!Ty) + return Invalid("global isn't a struct"); + + auto *I64Ty = Type::getInt64Ty(Ctx); + auto *I32Ty = Type::getInt32Ty(Ctx); + auto *Ptr0Ty = PointerType::getUnqual(Ctx); + // Check that the struct matches its expected shape: + // { ptr, i32, i64, i64 } + if (!Ty->isLayoutIdentical( + StructType::get(Ctx, {Ptr0Ty, I32Ty, I64Ty, I64Ty}))) + return Invalid("global doesn't have type '{ ptr, i32, i64, i64 }'"); + + auto *Key = dyn_cast(Init->getOperand(1)); + if (!Key) + return Invalid("key isn't a constant integer"); + + auto *AddrDiscriminator = Init->getOperand(2); + if (!isa(AddrDiscriminator) && + !isa(AddrDiscriminator)) + return Invalid("address discriminator isn't a constant integer or expr"); + + auto *Discriminator = dyn_cast(Init->getOperand(3)); + if (!Discriminator) + return Invalid("discriminator isn't a constant integer"); + + return GlobalPtrAuthInfo(GV); +} + +std::optional GlobalPtrAuthInfo::analyze(const Value *V) { + return expectedToOptional(tryAnalyze(V)); +} + +static bool areEquivalentAddrDiscriminators(const Value *V1, const Value *V2, + const DataLayout &DL) { + APInt V1Off(DL.getPointerSizeInBits(), 0); + APInt V2Off(DL.getPointerSizeInBits(), 0); + + if (auto *V1Cast = dyn_cast(V1)) + V1 = V1Cast->getPointerOperand(); + if (auto *V2Cast = dyn_cast(V2)) + V2 = V2Cast->getPointerOperand(); + auto *V1Base = V1->stripAndAccumulateConstantOffsets( + DL, V1Off, /*AllowNonInbounds=*/true); + auto *V2Base = V2->stripAndAccumulateConstantOffsets( + DL, V2Off, /*AllowNonInbounds=*/true); + return V1Base == V2Base && V1Off == V2Off; +} + +bool GlobalPtrAuthInfo::isCompatibleWith(const Value *Key, + const Value *Discriminator, + const DataLayout &DL) const { + // If the keys are different, there's no chance for this to be compatible. + if (Key != getKey()) + return false; + + // If Discriminator is dynamically blended, compare its parts. + if (auto *DiscBlend = dyn_cast(Discriminator)) { + return DiscBlend->getIntrinsicID() == Intrinsic::ptrauth_blend && + DiscBlend->getOperand(1) == getDiscriminator() && + areEquivalentAddrDiscriminators(DiscBlend->getOperand(0), + getAddrDiscriminator(), DL); + } + + // Otherwise, check for either integer-only or address-only Discriminator. + + if (getAddrDiscriminator()->isNullValue() && + Discriminator == getDiscriminator()) + return true; + + if (getDiscriminator()->isNullValue() && + areEquivalentAddrDiscriminators(getAddrDiscriminator(), Discriminator, + DL)) + return true; + + return false; +} + +Constant *GlobalPtrAuthInfo::createWithSameSchema(Module &M, + Constant *Pointer) const { + return create(M, Pointer, getKey(), getAddrDiscriminator(), + getDiscriminator()); +} + +Constant *GlobalPtrAuthInfo::create(Module &M, Constant *Pointer, + ConstantInt *Key, + Constant *AddrDiscriminator, + ConstantInt *Discriminator) { + auto *Init = ConstantStruct::getAnon( + {Pointer, Key, AddrDiscriminator, Discriminator}, /*packed*/ false); + + // TODO: look for an existing global with the right setup? + auto *GV = new GlobalVariable(M, Init->getType(), /*constant*/ true, + GlobalVariable::PrivateLinkage, Init); + GV->setSection("llvm.ptrauth"); + + assert(analyze(GV) && "invalid ptrauth constant"); + + return GV; +} diff --git a/llvm/lib/IR/Globals.cpp b/llvm/lib/IR/Globals.cpp --- a/llvm/lib/IR/Globals.cpp +++ b/llvm/lib/IR/Globals.cpp @@ -141,6 +141,32 @@ setSection(Src->getSection()); } +bool GlobalValue::hasExternalWeakLinkage() const { + // llvm.ptrauth wrapper globals are tricky: they're always constant defs + // with private linkage, but the extern_weak linkage check is almost always + // used to check whether the global reference can be null. + // llvm.ptrauth references can be, if the underlying global is itself null. + // That's only possible if it's extern_weak: look through the initializer. + // llvm.ptrauth globals should be thought of as special ConstantExprs. + if (auto *GVB = dyn_cast(this)) { + if (getSection() == "llvm.ptrauth") { + // While llvm.ptrauth globals must have an initializer, this code might + // be called early enough that they don't (in particular in LTO, + // via ConstantFold, before the initializer has been mapped). + if (!GVB->hasInitializer()) + return true; + auto *Base = GVB->getInitializer()->getOperand(0); + APInt Offset(64, 0); + Base = Base->stripAndAccumulateConstantOffsets( + getParent()->getDataLayout(), Offset, /*AllowNonInbounds=*/true); + if (auto *BaseGV = dyn_cast(Base)) + return BaseGV->hasExternalWeakLinkage(); + return true; + } + } + return isExternalWeakLinkage(getLinkage()); +} + std::string GlobalValue::getGlobalIdentifier(StringRef Name, GlobalValue::LinkageTypes Linkage, StringRef FileName) { diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -76,6 +76,7 @@ #include "llvm/IR/Function.h" #include "llvm/IR/GCStrategy.h" #include "llvm/IR/GlobalAlias.h" +#include "llvm/IR/GlobalPtrAuthInfo.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/InlineAsm.h" @@ -810,6 +811,14 @@ } } + if (GV.getSection() == "llvm.ptrauth") { + if (auto Err = GlobalPtrAuthInfo::tryAnalyze(&GV).takeError()) { + CheckFailed("invalid llvm.ptrauth global: " + toString(std::move(Err)), + &GV); + return; + } + } + // Visit any debug info attachments. SmallVector MDs; GV.getMetadata(LLVMContext::MD_dbg, MDs); diff --git a/llvm/lib/Linker/IRMover.cpp b/llvm/lib/Linker/IRMover.cpp --- a/llvm/lib/Linker/IRMover.cpp +++ b/llvm/lib/Linker/IRMover.cpp @@ -1000,6 +1000,11 @@ if (ValuesToLink.count(&SGV) || SGV.hasLocalLinkage()) return true; + // ptrauth wrapper globals can't be prototypes. + if (auto *GV = dyn_cast(&SGV)) + if (GV->getSection() == "llvm.ptrauth") + return true; + if (DGV && !DGV->isDeclarationForLinker()) return false; diff --git a/llvm/lib/Transforms/IPO/ExtractGV.cpp b/llvm/lib/Transforms/IPO/ExtractGV.cpp --- a/llvm/lib/Transforms/IPO/ExtractGV.cpp +++ b/llvm/lib/Transforms/IPO/ExtractGV.cpp @@ -79,6 +79,9 @@ continue; } + if (GV.getSection() == "llvm.ptrauth") + continue; + makeVisible(GV, Delete); if (Delete) { diff --git a/llvm/lib/Transforms/IPO/FunctionImport.cpp b/llvm/lib/Transforms/IPO/FunctionImport.cpp --- a/llvm/lib/Transforms/IPO/FunctionImport.cpp +++ b/llvm/lib/Transforms/IPO/FunctionImport.cpp @@ -1087,6 +1087,8 @@ F->clearMetadata(); F->setComdat(nullptr); } else if (GlobalVariable *V = dyn_cast(&GV)) { + if (V->getSection() == "llvm.ptrauth") + report_fatal_error("can't convert llvm.ptrauth to declaration!"); V->setInitializer(nullptr); V->setLinkage(GlobalValue::ExternalLinkage); V->clearMetadata(); diff --git a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp --- a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -1901,6 +1901,9 @@ // Globals from llvm.metadata aren't emitted, do not instrument them. if (Section == "llvm.metadata") return false; + // Same for globals in llvm.ptrauth. + if (Section == "llvm.ptrauth") + return false; // Do not instrument globals from special LLVM sections. if (Section.contains("__llvm") || Section.contains("__LLVM")) return false; diff --git a/llvm/lib/Transforms/Utils/Evaluator.cpp b/llvm/lib/Transforms/Utils/Evaluator.cpp --- a/llvm/lib/Transforms/Utils/Evaluator.cpp +++ b/llvm/lib/Transforms/Utils/Evaluator.cpp @@ -23,6 +23,7 @@ #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalAlias.h" +#include "llvm/IR/GlobalPtrAuthInfo.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/InstrTypes.h" @@ -46,6 +47,10 @@ SmallPtrSetImpl &SimpleConstants, const DataLayout &DL); +static bool isPtrauthWrapperGlobal(Constant *C) { + return GlobalPtrAuthInfo::analyze(C).has_value(); +} + /// Return true if the specified constant can be handled by the code generator. /// We don't want to generate something like: /// void *X = &X/42; @@ -60,8 +65,10 @@ const DataLayout &DL) { // Simple global addresses are supported, do not allow dllimport or // thread-local globals. + // Conservatively disallow any nested llvm.ptrauth global as well. if (auto *GV = dyn_cast(C)) - return !GV->hasDLLImportStorageClass() && !GV->isThreadLocal(); + return !GV->hasDLLImportStorageClass() && !GV->isThreadLocal() && + !isPtrauthWrapperGlobal(GV); // Simple integer, undef, constant aggregate zero, etc are all supported. if (C->getNumOperands() == 0 || isa(C)) @@ -326,8 +333,14 @@ // If this might be too difficult for the backend to handle (e.g. the addr // of one global variable divided by another) then we can't commit it. + // If it's a simple-enough top-level llvm.ptrauth global, allow it; + // we conservatively disallow any nested llvm.ptrauth global usage when + // checking the constants. Make sure to only check the constant if + // it's not a ptrauth global, to avoid incorrectly caching it as + // simple. Constant *Val = getVal(SI->getOperand(0)); - if (!isSimpleEnoughValueToCommit(Val, SimpleConstants, DL)) { + if (!isPtrauthWrapperGlobal(Val) && + !isSimpleEnoughValueToCommit(Val, SimpleConstants, DL)) { LLVM_DEBUG(dbgs() << "Store value is too complex to evaluate store. " << *Val << "\n"); return false; diff --git a/llvm/lib/Transforms/Utils/FunctionImportUtils.cpp b/llvm/lib/Transforms/Utils/FunctionImportUtils.cpp --- a/llvm/lib/Transforms/Utils/FunctionImportUtils.cpp +++ b/llvm/lib/Transforms/Utils/FunctionImportUtils.cpp @@ -253,6 +253,13 @@ // If global value dead stripping is not enabled in summary then // propagateConstants hasn't been run. We can't internalize GV // in such case. + // + // Ignore llvm.ptrauth globals: + // - we don't need to internalize them (because they're artificial and aren't + // emitted as symbols) + // - we can't set their initializer to 'zeroinitializer' (because the + // initializer struct has to be present, because it's interpreted as a list + // of ConstantExpr operands). if (!GV.isDeclaration() && VI && ImportIndex.withAttributePropagation()) { if (GlobalVariable *V = dyn_cast(&GV)) { // We can have more than one local with the same GUID, in the case of @@ -266,7 +273,7 @@ // matches one in this module (e.g. weak or appending linkage). auto *GVS = dyn_cast_or_null( ImportIndex.findSummaryInModule(VI, M.getModuleIdentifier())); - if (GVS && + if (GVS && V->getSection() != "llvm.ptrauth" && (ImportIndex.isReadOnly(GVS) || ImportIndex.isWriteOnly(GVS))) { V->addAttribute("thinlto-internalize"); // Objects referenced by writeonly GV initializer should not be diff --git a/llvm/test/Instrumentation/AddressSanitizer/do-not-instrument-ptrauth-globals.ll b/llvm/test/Instrumentation/AddressSanitizer/do-not-instrument-ptrauth-globals.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Instrumentation/AddressSanitizer/do-not-instrument-ptrauth-globals.ll @@ -0,0 +1,11 @@ +; This test checks that we are not instrumenting llvm.ptrauth globals. +; RUN: opt < %s -passes=asan -S | FileCheck %s + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-darwin" + +declare void @f() + +@f.ptrauth.ia.42 = private constant { ptr, i32, i64, i64 } { ptr @f, i32 0, i64 0, i64 42 }, section "llvm.ptrauth" + +; CHECK: @f.ptrauth.ia.42 = private constant { ptr, i32, i64, i64 } { ptr @f, i32 0, i64 0, i64 42 }, section "llvm.ptrauth" diff --git a/llvm/test/Transforms/GlobalOpt/evaluate-ptrauth-global.ll b/llvm/test/Transforms/GlobalOpt/evaluate-ptrauth-global.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/GlobalOpt/evaluate-ptrauth-global.ll @@ -0,0 +1,47 @@ +; RUN: opt -passes=globalopt %s -S -o - | FileCheck %s + +target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" +target triple = "arm64e-apple-ios13" + +; Check that simple references to llvm.ptrauth globals get optimized into +; constant initializers, but more complex ones don't. +; CHECK: @llvm.global_ctors = appending global [2 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @test_init_ptrauth_offset, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @test_init_ptrauth_nested, ptr null }] +@llvm.global_ctors = appending global [3 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @test_init_ptrauth, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @test_init_ptrauth_offset, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @test_init_ptrauth_nested, ptr null }] + +@f.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @f, i32 0, i64 0, i64 0 }, section "llvm.ptrauth", align 8 + +; CHECK: @fp = local_unnamed_addr global ptr @f.ptrauth, align 8 +@fp = global ptr null, align 8 + +; CHECK-NOT: @test_init_ptrauth +define internal void @test_init_ptrauth() { + store ptr @f.ptrauth, ptr @fp, align 8 + ret void +} + +; CHECK: @fp_offset = local_unnamed_addr global ptr null, align 8 +@fp_offset = global ptr null, align 8 + +; CHECK: @fp_nested = local_unnamed_addr global ptr null, align 8 +@fp_nested = global ptr null, align 8 + +; CHECK: @fp_nested_offset = local_unnamed_addr global ptr null, align 8 +@fp_nested_offset = global ptr null, align 8 + +; CHECK: define internal void @test_init_ptrauth_offset() +; CHECK: store ptr getelementptr inbounds (i8, ptr @f.ptrauth, i64 1), ptr @fp_offset, align 8 +define internal void @test_init_ptrauth_offset() { + store ptr getelementptr inbounds (i8, ptr @f.ptrauth, i64 1), ptr @fp_offset, align 8 + ret void +} + +; CHECK: define internal void @test_init_ptrauth_nested() +; CHECK: store ptr @f.ptrauth, ptr @fp_nested, align 8 +; CHECK: store ptr getelementptr inbounds (i8, ptr @f.ptrauth, i64 1), ptr @fp_nested_offset, align 8 +define internal void @test_init_ptrauth_nested() { + store ptr @f.ptrauth, ptr @fp_nested, align 8 + store ptr getelementptr inbounds (i8, ptr @f.ptrauth, i64 1), ptr @fp_nested_offset, align 8 + ret void +} + +declare void @f() diff --git a/llvm/test/Transforms/InstSimplify/ptrauth-global-compare.ll b/llvm/test/Transforms/InstSimplify/ptrauth-global-compare.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/InstSimplify/ptrauth-global-compare.ll @@ -0,0 +1,54 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=instsimplify -S | FileCheck %s + +; Basic global variable + +@g = external global i32 +@g.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @g, i32 2, i64 0, i64 0 }, section "llvm.ptrauth" + +define i1 @test_external_global() { +; CHECK-LABEL: @test_external_global( +; CHECK-NEXT: ret i1 false +; + %tmp0 = icmp eq ptr @g.ptrauth, null + ret i1 %tmp0 +} + +; Address discrimination + +@g.ptrauth.addr = private constant { ptr, i32, i64, i64 } { ptr getelementptr (i64, ptr @g, i64 16), i32 2, i64 ptrtoint (ptr @g_addr_ref to i64), i64 42 }, section "llvm.ptrauth" + +@g_addr_ref = constant ptr @g.ptrauth.addr + +define i1 @test_external_global_addrdisc() { +; CHECK-LABEL: @test_external_global_addrdisc( +; CHECK-NEXT: ret i1 false +; + %tmp0 = icmp eq ptr @g.ptrauth.addr, null + ret i1 %tmp0 +} + +; extern_weak global variable + +@g_weak = extern_weak global i32 +@g_weak.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @g_weak, i32 2, i64 0, i64 0 }, section "llvm.ptrauth" + +define i1 @test_extern_weak_global() { +; CHECK-LABEL: @test_extern_weak_global( +; CHECK-NEXT: ret i1 icmp eq (ptr @g_weak.ptrauth, ptr null) +; + %tmp0 = icmp eq ptr @g_weak.ptrauth, null + ret i1 %tmp0 +} + +; Integer constant + +@two.ptrauth = private constant { ptr, i32, i64, i64 } { ptr inttoptr (i64 0 to ptr), i32 0, i64 0, i64 42 }, section "llvm.ptrauth" + +define i1 @test_constantint() { +; CHECK-LABEL: @test_constantint( +; CHECK-NEXT: ret i1 icmp eq (ptr @two.ptrauth, ptr null) +; + %tmp0 = icmp eq ptr @two.ptrauth, null + ret i1 %tmp0 +} diff --git a/llvm/test/Verifier/ptrauth-global.ll b/llvm/test/Verifier/ptrauth-global.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Verifier/ptrauth-global.ll @@ -0,0 +1,21 @@ +; RUN: not opt -S -passes=verify < %s 2>&1 | FileCheck %s + +; CHECK: invalid llvm.ptrauth global: global doesn't have an initializer +@no_init = external global { ptr, i32, i64, i64 }, section "llvm.ptrauth" + +; CHECK: invalid llvm.ptrauth global: global isn't a struct +@not_struct = constant ptr null, section "llvm.ptrauth" + +; CHECK: invalid llvm.ptrauth global: global doesn't have type '{ ptr, i32, i64, i64 }' +@bad_type = constant { ptr, i32, i32, i32 } zeroinitializer, section "llvm.ptrauth" + +; CHECK: invalid llvm.ptrauth global: key isn't a constant integer +@bad_key = constant { ptr, i32, i64, i64 } { ptr null, i32 ptrtoint (ptr @g to i32), i64 0, i64 0}, section "llvm.ptrauth" + +; CHECK: invalid llvm.ptrauth global: discriminator isn't a constant integer +@bad_disc = constant { ptr, i32, i64, i64 } { ptr null, i32 0, i64 0, i64 ptrtoint (ptr @g to i64)}, section "llvm.ptrauth" + +; CHECK-NOT: invalid +@valid = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds (i8, ptr @g, i64 2), i32 3, i64 0, i64 0 }, section "llvm.ptrauth" + +@g = external global i32 diff --git a/llvm/test/tools/llvm-extract/extract-ptrauth.ll b/llvm/test/tools/llvm-extract/extract-ptrauth.ll new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-extract/extract-ptrauth.ll @@ -0,0 +1,11 @@ +; RUN: llvm-extract -S -func test_fn %s | FileCheck %s + +@test_gv = external global i32 +@test_gv.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @test_gv, i32 2, i64 0, i64 0 }, section "llvm.ptrauth" + +; CHECK: @test_gv = external global i32 +; CHECK: @test_gv.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @test_gv, i32 2, i64 0, i64 0 }, section "llvm.ptrauth" + +define ptr @test_fn() { + ret ptr @test_gv.ptrauth +} diff --git a/llvm/tools/bugpoint/ExtractFunction.cpp b/llvm/tools/bugpoint/ExtractFunction.cpp --- a/llvm/tools/bugpoint/ExtractFunction.cpp +++ b/llvm/tools/bugpoint/ExtractFunction.cpp @@ -204,6 +204,8 @@ // making it external. // void llvm::DeleteGlobalInitializer(GlobalVariable *GV) { + if (GV->getSection() == "llvm.ptrauth") + return; eliminateAliases(GV); GV->setInitializer(nullptr); GV->setComdat(nullptr);