diff options
author | Ahmed Bougacha <ahmed@bougacha.org> | 2024-05-28 16:39:09 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-28 16:39:09 -0700 |
commit | 0edc97f119f3ac3ff96b11183fe5c001a48a9a8d (patch) | |
tree | 381f97ed82e28f48672f05e138ba9bb17104beff /llvm/lib/IR/Constants.cpp | |
parent | 60bce6eab4d734b86f49b7638856eb8899bc89e8 (diff) | |
download | llvm-0edc97f119f3ac3ff96b11183fe5c001a48a9a8d.zip llvm-0edc97f119f3ac3ff96b11183fe5c001a48a9a8d.tar.gz llvm-0edc97f119f3ac3ff96b11183fe5c001a48a9a8d.tar.bz2 |
[IR][AArch64][PAC] Add "ptrauth(...)" Constant to represent signed pointers. (#85738)
This defines a new kind of IR Constant that represents a ptrauth signed
pointer, as used in AArch64 PAuth.
It allows representing most kinds of signed pointer constants used thus
far in the llvm ptrauth implementations, notably those used in the
Darwin and ELF ABIs being implemented for c/c++. These signed pointer
constants are then lowered to ELF/MachO relocations.
These can be simply thought of as a constant `llvm.ptrauth.sign`, with
the interesting addition of discriminator computation: the `ptrauth`
constant can also represent a combined blend, when both address and
integer discriminator operands are used. Both operands are otherwise
optional, with default values 0/null.
Diffstat (limited to 'llvm/lib/IR/Constants.cpp')
-rw-r--r-- | llvm/lib/IR/Constants.cpp | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/llvm/lib/IR/Constants.cpp b/llvm/lib/IR/Constants.cpp index cfb89d5..119fcb4 100644 --- a/llvm/lib/IR/Constants.cpp +++ b/llvm/lib/IR/Constants.cpp @@ -550,6 +550,9 @@ void llvm::deleteConstant(Constant *C) { case Constant::NoCFIValueVal: delete static_cast<NoCFIValue *>(C); break; + case Constant::ConstantPtrAuthVal: + delete static_cast<ConstantPtrAuth *>(C); + break; case Constant::UndefValueVal: delete static_cast<UndefValue *>(C); break; @@ -2015,6 +2018,124 @@ Value *NoCFIValue::handleOperandChangeImpl(Value *From, Value *To) { return nullptr; } +//---- ConstantPtrAuth::get() implementations. +// + +ConstantPtrAuth *ConstantPtrAuth::get(Constant *Ptr, ConstantInt *Key, + ConstantInt *Disc, Constant *AddrDisc) { + Constant *ArgVec[] = {Ptr, Key, Disc, AddrDisc}; + ConstantPtrAuthKeyType MapKey(ArgVec); + LLVMContextImpl *pImpl = Ptr->getContext().pImpl; + return pImpl->ConstantPtrAuths.getOrCreate(Ptr->getType(), MapKey); +} + +ConstantPtrAuth *ConstantPtrAuth::getWithSameSchema(Constant *Pointer) const { + return get(Pointer, getKey(), getDiscriminator(), getAddrDiscriminator()); +} + +ConstantPtrAuth::ConstantPtrAuth(Constant *Ptr, ConstantInt *Key, + ConstantInt *Disc, Constant *AddrDisc) + : Constant(Ptr->getType(), Value::ConstantPtrAuthVal, &Op<0>(), 4) { + assert(Ptr->getType()->isPointerTy()); + assert(Key->getBitWidth() == 32); + assert(Disc->getBitWidth() == 64); + assert(AddrDisc->getType()->isPointerTy()); + setOperand(0, Ptr); + setOperand(1, Key); + setOperand(2, Disc); + setOperand(3, AddrDisc); +} + +/// Remove the constant from the constant table. +void ConstantPtrAuth::destroyConstantImpl() { + getType()->getContext().pImpl->ConstantPtrAuths.remove(this); +} + +Value *ConstantPtrAuth::handleOperandChangeImpl(Value *From, Value *ToV) { + assert(isa<Constant>(ToV) && "Cannot make Constant refer to non-constant!"); + Constant *To = cast<Constant>(ToV); + + SmallVector<Constant *, 4> Values; + Values.reserve(getNumOperands()); + + unsigned NumUpdated = 0; + + Use *OperandList = getOperandList(); + unsigned OperandNo = 0; + for (Use *O = OperandList, *E = OperandList + getNumOperands(); O != E; ++O) { + Constant *Val = cast<Constant>(O->get()); + if (Val == From) { + OperandNo = (O - OperandList); + Val = To; + ++NumUpdated; + } + Values.push_back(Val); + } + + return getContext().pImpl->ConstantPtrAuths.replaceOperandsInPlace( + Values, this, From, To, NumUpdated, OperandNo); +} + +bool ConstantPtrAuth::isKnownCompatibleWith(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 (getKey() != Key) + return false; + + // We can have 3 kinds of discriminators: + // - simple, integer-only: `i64 x, ptr null` vs. `i64 x` + // - address-only: `i64 0, ptr p` vs. `ptr p` + // - blended address/integer: `i64 x, ptr p` vs. `@llvm.ptrauth.blend(p, x)` + + // If this constant has a simple discriminator (integer, no address), easy: + // it's compatible iff the provided full discriminator is also a simple + // discriminator, identical to our integer discriminator. + if (!hasAddressDiscriminator()) + return getDiscriminator() == Discriminator; + + // Otherwise, we can isolate address and integer discriminator components. + const Value *AddrDiscriminator = nullptr; + + // This constant may or may not have an integer discriminator (instead of 0). + if (!getDiscriminator()->isNullValue()) { + // If it does, there's an implicit blend. We need to have a matching blend + // intrinsic in the provided full discriminator. + if (!match(Discriminator, + m_Intrinsic<Intrinsic::ptrauth_blend>( + m_Value(AddrDiscriminator), m_Specific(getDiscriminator())))) + return false; + } else { + // Otherwise, interpret the provided full discriminator as address-only. + AddrDiscriminator = Discriminator; + } + + // Either way, we can now focus on comparing the address discriminators. + + // Discriminators are i64, so the provided addr disc may be a ptrtoint. + if (auto *Cast = dyn_cast<PtrToIntOperator>(AddrDiscriminator)) + AddrDiscriminator = Cast->getPointerOperand(); + + // Beyond that, we're only interested in compatible pointers. + if (getAddrDiscriminator()->getType() != AddrDiscriminator->getType()) + return false; + + // These are often the same constant GEP, making them trivially equivalent. + if (getAddrDiscriminator() == AddrDiscriminator) + return true; + + // Finally, they may be equivalent base+offset expressions. + APInt Off1(DL.getIndexTypeSizeInBits(getAddrDiscriminator()->getType()), 0); + auto *Base1 = getAddrDiscriminator()->stripAndAccumulateConstantOffsets( + DL, Off1, /*AllowNonInbounds=*/true); + + APInt Off2(DL.getIndexTypeSizeInBits(AddrDiscriminator->getType()), 0); + auto *Base2 = AddrDiscriminator->stripAndAccumulateConstantOffsets( + DL, Off2, /*AllowNonInbounds=*/true); + + return Base1 == Base2 && Off1 == Off2; +} + //---- ConstantExpr::get() implementations. // |