diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 44757b8ebb05..a8a28998f498 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -2304,6 +2304,50 @@ llvm::DINodeArray CGDebugInfo::CollectBTFDeclTagAnnotations(const Decl *D) { return DBuilder.getOrCreateArray(Annotations); } +llvm::DINodeArray +CGDebugInfo::CollectPtrAuthStructAnnotations(const RecordDecl *RD) { + if (!RD->hasAttr()) + return nullptr; + + llvm::LLVMContext &Context = CGM.getLLVMContext(); + + auto [IsKeyValIndependent, Key] = + CGM.getContext().getPointerAuthStructKey(RD); + auto [IsDiscValIndependent, Disc] = + CGM.getContext().getPointerAuthStructDisc(RD); + + // TODO: handle value-dependent keys and discriminators. What does this even + // mean? Using template integer parameters as key/disc or expressions like + // `__builtin_ptrauth_struct_key(...)` result in specific constants, and does + // not make key/disc value dependent. + if (!IsKeyValIndependent || !IsDiscValIndependent) + return nullptr; + + // TODO: 2-bit key and 16-bit disc results in negative values of i2 and i16 + // types in IR for unsigned integers using all the given bit-width (for ex., + // key 3 is i8 -1, disc 65534 is i16 -2). Do we want to use wider types to + // prevent negation? + auto *KeyMD = llvm::ConstantAsMetadata::get( + llvm::ConstantInt::get(Context, llvm::APInt(2, Key, false))); + auto *DiscMD = llvm::ConstantAsMetadata::get( + llvm::ConstantInt::get(Context, llvm::APInt(16, Disc, false))); + + llvm::Metadata *Annotations[2]; + llvm::Metadata *OpsKey[2] = { + llvm::MDString::get(Context, StringRef("ptrauth_struct_key")), KeyMD}; + Annotations[0] = llvm::MDNode::get(CGM.getLLVMContext(), OpsKey); + llvm::Metadata *OpsDisc[2] = { + llvm::MDString::get(Context, StringRef("ptrauth_struct_disc")), DiscMD}; + Annotations[1] = llvm::MDNode::get(CGM.getLLVMContext(), OpsDisc); + + // TODO: is it OK to return DINodeArray instead of MDNodeArray from + // CollectPtrAuthStructAnnotations and CollectBTFDeclTagAnnotations? The + // elements are not actually DINodes, and trying to iterate over the returned + // DINodeArray results in an assertion failure. This does not seem to happen + // though - it looks like that there are no places where such loops are used. + return DBuilder.getOrCreateArray(Annotations); +} + llvm::DIType *CGDebugInfo::getOrCreateVTablePtrType(llvm::DIFile *Unit) { if (VTablePtrType) return VTablePtrType; @@ -2432,6 +2476,47 @@ void CGDebugInfo::CollectVTableInfo(const CXXRecordDecl *RD, llvm::DIFile *Unit, if (!VPtrTy) VPtrTy = getOrCreateVTablePtrType(Unit); + if (auto *VptrAuthAttr = RD->getAttr()) { + unsigned TypedDiscriminator = + CGM.getContext().getPointerAuthVTablePointerDiscriminator(RD); + auto &LangOpts = CGM.getContext().getLangOpts(); + + unsigned Key = VptrAuthAttr->getKey(); + + bool HasAddressDiscrimination = + LangOpts.PointerAuthVTPtrAddressDiscrimination; + if (VptrAuthAttr->getAddressDiscrimination() != + VTablePointerAuthenticationAttr::DefaultAddressDiscrimination) { + HasAddressDiscrimination = + (VptrAuthAttr->getAddressDiscrimination() == + VTablePointerAuthenticationAttr::AddressDiscrimination); + } + + unsigned ExtraDiscriminator = 0; + + switch (VptrAuthAttr->getExtraDiscrimination()) { + case VTablePointerAuthenticationAttr::DefaultExtraDiscrimination: + if (LangOpts.PointerAuthVTPtrTypeDiscrimination) + ExtraDiscriminator = TypedDiscriminator; + break; + case VTablePointerAuthenticationAttr::TypeDiscrimination: + ExtraDiscriminator = TypedDiscriminator; + break; + case VTablePointerAuthenticationAttr::CustomDiscrimination: + ExtraDiscriminator = VptrAuthAttr->getCustomDiscriminationValue(); + break; + case VTablePointerAuthenticationAttr::NoExtraDiscrimination: + break; + } + + // See CodeGenModule::computeVTPointerAuthentication: + // isIsaPointer and authenticatesNullValues are always false. + VPtrTy = DBuilder.createPtrAuthQualifiedType( + VPtrTy, Key, HasAddressDiscrimination, ExtraDiscriminator, + /* isIsaPointer */ false, + /* authenticatesNullValues */ false); + } + unsigned Size = CGM.getContext().getTypeSize(CGM.getContext().VoidPtrTy); llvm::DIType *VPtrMember = DBuilder.createMemberType(Unit, getVTableName(RD), Unit, 0, Size, 0, 0, @@ -3749,7 +3834,21 @@ llvm::DICompositeType *CGDebugInfo::CreateLimitedType(const RecordType *Ty) { dyn_cast(CXXRD->getDeclContext())); } - llvm::DINodeArray Annotations = CollectBTFDeclTagAnnotations(D); + llvm::DINodeArray AnnotationsBTFTag = CollectBTFDeclTagAnnotations(D); + llvm::DINodeArray AnnotationsPAuthStruct = CollectPtrAuthStructAnnotations(D); + + llvm::SmallVector AnnotationsVec; + if (AnnotationsBTFTag.get() != nullptr) + for (const llvm::MDOperand &Op : AnnotationsBTFTag.get()->operands()) + AnnotationsVec.push_back(Op.get()); + if (AnnotationsPAuthStruct.get() != nullptr) + for (const llvm::MDOperand &Op : AnnotationsPAuthStruct.get()->operands()) + AnnotationsVec.push_back(Op.get()); + + llvm::DINodeArray Annotations = + AnnotationsVec.empty() ? nullptr + : DBuilder.getOrCreateArray(AnnotationsVec); + llvm::DICompositeType *RealDecl = DBuilder.createReplaceableCompositeType( getTagForRecord(RD), RDName, RDContext, DefUnit, Line, 0, Size, Align, Flags, Identifier, Annotations); diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h index ae12485850ca..ffcede3a4f90 100644 --- a/clang/lib/CodeGen/CGDebugInfo.h +++ b/clang/lib/CodeGen/CGDebugInfo.h @@ -316,6 +316,9 @@ class CGDebugInfo { /// A helper function to collect debug info for btf_decl_tag annotations. llvm::DINodeArray CollectBTFDeclTagAnnotations(const Decl *D); + /// A helper function to collect debug info for ptrauth_struct attribute. + llvm::DINodeArray CollectPtrAuthStructAnnotations(const RecordDecl *RD); + llvm::DIType *createFieldType(StringRef name, QualType type, SourceLocation loc, AccessSpecifier AS, uint64_t offsetInBits, uint32_t AlignInBits, diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index d8ecf681f6d6..75ccd28057ac 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -51,6 +51,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/BinaryFormat/ELF.h" #include "llvm/Frontend/OpenMP/OMPIRBuilder.h" #include "llvm/IR/AttributeMask.h" #include "llvm/IR/CallingConv.h" @@ -1075,6 +1076,23 @@ void CodeGenModule::Release() { if (!LangOpts.isSignReturnAddressWithAKey()) getModule().addModuleFlag(llvm::Module::Min, "sign-return-address-with-bkey", 1); + + if (getTriple().isOSLinux() && getTriple().isOSBinFormatELF()) { + uint64_t PAuthABIVersion = + (LangOpts.PointerAuthCalls << 0) | + (LangOpts.PointerAuthReturns << 1) | + (LangOpts.PointerAuthVTPtrAddressDiscrimination << 2) | + (LangOpts.PointerAuthVTPtrTypeDiscrimination << 3) | + (LangOpts.PointerAuthInitFini << 4); + if (PAuthABIVersion != 0) { + getModule().addModuleFlag( + llvm::Module::Error, "aarch64-elf-pauthabi-platform", + llvm::ELF::GNU_PROPERTY_AARCH64_FEATURE_PAUTH_PLATFORM_LINUX); + getModule().addModuleFlag(llvm::Module::Error, + "aarch64-elf-pauthabi-version", + PAuthABIVersion); + } + } } if (!CodeGenOpts.MemoryProfileOutput.empty()) { diff --git a/clang/test/CodeGen/aarch64-elf-pauthabi.c b/clang/test/CodeGen/aarch64-elf-pauthabi.c new file mode 100644 index 000000000000..5b65dfed9338 --- /dev/null +++ b/clang/test/CodeGen/aarch64-elf-pauthabi.c @@ -0,0 +1,28 @@ +// RUN: %clang -target aarch64-linux -S -emit-llvm -o - -mbranch-protection=pauthabi %s | FileCheck %s --check-prefix=ALL +// RUN: %clang -target aarch64-linux -S -emit-llvm -o - -fptrauth-returns %s | FileCheck %s --check-prefix=RET +// RUN: %clang -target aarch64-linux -S -emit-llvm -o - -fptrauth-calls %s | FileCheck %s --check-prefix=CALL +// RUN: %clang -target aarch64-linux -S -emit-llvm -o - -fptrauth-calls -fptrauth-vtable-pointer-address-discrimination %s | FileCheck %s --check-prefix=VPTRADDR +// RUN: %clang -target aarch64-linux -S -emit-llvm -o - -fptrauth-calls -fptrauth-vtable-pointer-type-discrimination %s | FileCheck %s --check-prefix=VPTRTYPE +// RUN: %clang -target aarch64-linux -S -emit-llvm -o - -fptrauth-calls -fptrauth-init-fini %s | FileCheck %s --check-prefix=INITFINI + +// REQUIRES: aarch64-registered-target + +// ALL: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 2} +// ALL: !{i32 1, !"aarch64-elf-pauthabi-version", i32 31} + +// RET: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 2} +// RET: !{i32 1, !"aarch64-elf-pauthabi-version", i32 2} + +// CALL: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 2} +// CALL: !{i32 1, !"aarch64-elf-pauthabi-version", i32 1} + +// VPTRADDR: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 2} +// VPTRADDR: !{i32 1, !"aarch64-elf-pauthabi-version", i32 5} + +// VPTRTYPE: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 2} +// VPTRTYPE: !{i32 1, !"aarch64-elf-pauthabi-version", i32 9} + +// INITFINI: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 2} +// INITFINI: !{i32 1, !"aarch64-elf-pauthabi-version", i32 17} + +void foo() {} diff --git a/clang/test/CodeGenCXX/ptrauth-struct-attr-annotations.cpp b/clang/test/CodeGenCXX/ptrauth-struct-attr-annotations.cpp new file mode 100644 index 000000000000..3f75f1ea5729 --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-struct-attr-annotations.cpp @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -triple aarch64 -emit-llvm -debug-info-kind=limited -o - %s | FileCheck %s + +#include + +struct __attribute__((ptrauth_struct(1, 42))) S0 {}; + +template +class [[clang::ptrauth_struct(k, 65534)]] S1 {}; + +union __attribute__((ptrauth_struct(__builtin_ptrauth_struct_key(S0) + 1, __builtin_ptrauth_struct_disc(S0) + 1))) S2 {}; + +S0 s0; +S1<3> s1; +S2 s2; + +// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "S1<3>", +// CHECK-SAME: annotations: ![[A1:.*]]) +// CHECK: ![[A1]] = !{![[A1K:.*]], ![[A1D:.*]]} +// CHECK: ![[A1K]] = !{!"ptrauth_struct_key", i2 -1} +// CHECK: ![[A1D]] = !{!"ptrauth_struct_disc", i16 -2} + +// CHECK: !DICompositeType(tag: DW_TAG_union_type, name: "S2", +// CHECK-SAME: annotations: ![[A2:.*]]) +// CHECK: ![[A2]] = !{![[A2K:.*]], ![[A2D:.*]]} +// CHECK: ![[A2K]] = !{!"ptrauth_struct_key", i2 -2} +// CHECK: ![[A2D]] = !{!"ptrauth_struct_disc", i16 43} + +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "S0", +// CHECK-SAME: annotations: ![[A0:.*]]) +// CHECK: ![[A0]] = !{![[A0K:.*]], ![[A0D:.*]]} +// CHECK: ![[A0K]] = !{!"ptrauth_struct_key", i2 1} +// CHECK: ![[A0D]] = !{!"ptrauth_struct_disc", i16 42} diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index 842c21fa2ac5..598416016da7 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -27,6 +27,7 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/GlobPattern.h" #include "llvm/Support/PrettyStackTrace.h" +#include #include #include #include @@ -492,7 +493,7 @@ struct Ctx { llvm::raw_fd_ostream openAuxiliaryFile(llvm::StringRef, std::error_code &); - SmallVector aarch64PauthAbiTag; + std::optional> aarch64PauthAbiTag; }; LLVM_LIBRARY_VISIBILITY extern Ctx ctx; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 8cf6438f5ae4..4f370e2b7eb8 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -2662,7 +2662,7 @@ static void getAarch64PauthInfo() { auto NonEmptyIt = std::find_if( ctx.objectFiles.begin(), ctx.objectFiles.end(), - [](const ELFFileBase *f) { return !f->aarch64PauthAbiTag.empty(); }); + [](const ELFFileBase *f) { return f->aarch64PauthAbiTag.has_value(); }); if (NonEmptyIt == ctx.objectFiles.end()) return; @@ -2670,9 +2670,9 @@ static void getAarch64PauthInfo() { StringRef F1 = (*NonEmptyIt)->getName(); for (ELFFileBase *F : ArrayRef(ctx.objectFiles)) { StringRef F2 = F->getName(); - const SmallVector &D1 = ctx.aarch64PauthAbiTag; - const SmallVector &D2 = F->aarch64PauthAbiTag; - if (D1.empty() != D2.empty()) { + std::optional> D1 = ctx.aarch64PauthAbiTag; + std::optional> D2 = F->aarch64PauthAbiTag; + if (D1.has_value() != D2.has_value()) { auto Helper = [](StringRef Report, const Twine &Msg) { if (Report == "warning") warn(Msg); @@ -2681,19 +2681,19 @@ static void getAarch64PauthInfo() { }; Helper(config->zPauthReport, - (D1.empty() ? F1.str() : F2.str()) + + (D1.has_value() ? F2.str() : F1.str()) + " has no AArch64 PAuth compatibility info while " + - (D1.empty() ? F2.str() : F1.str()) + + (D1.has_value() ? F1.str() : F2.str()) + " has one; either all or no input files must have it"); } - if (!D1.empty() && !D2.empty() && - !std::equal(D1.begin(), D1.end(), D2.begin(), D2.end())) + if (D1.has_value() && D2.has_value() && + !std::equal(D1->begin(), D1->end(), D2->begin(), D2->end())) errorOrWarn( "incompatible values of AArch64 PAuth compatibility info found" "\n" + - F1 + ": 0x" + toHex(ArrayRef(D1.data(), D1.size())) + "\n" + F2 + - ": 0x" + toHex(ArrayRef(D2.data(), D2.size()))); + F1 + ": 0x" + toHex(ArrayRef(D1->data(), D1->size())) + "\n" + F2 + + ": 0x" + toHex(ArrayRef(D2->data(), D2->size()))); } } diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 873438033a65..821a14003df1 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -31,6 +31,8 @@ #include "llvm/Support/TarWriter.h" #include "llvm/Support/raw_ostream.h" +#include + using namespace llvm; using namespace llvm::ELF; using namespace llvm::object; @@ -878,11 +880,14 @@ void ObjFile::initializeSections(bool ignoreComdats, // of zero or more type-length-value fields. We want to find a field of a // certain type. It seems a bit too much to just store a 32-bit value, perhaps // the ABI is unnecessarily complicated. -template static uint32_t readAndFeatures(const InputSection &sec) { +template +static std::pair>> +readGnuProperty(const InputSection &sec) { using Elf_Nhdr = typename ELFT::Nhdr; using Elf_Note = typename ELFT::Note; uint32_t featuresSet = 0; + std::optional> aarch64PauthAbiTag; ArrayRef data = sec.content(); auto reportFatal = [&](const uint8_t *place, const char *msg) { fatal(toString(sec.file) + ":(" + sec.name + "+0x" + @@ -924,6 +929,19 @@ template static uint32_t readAndFeatures(const InputSection &sec) { if (size < 4) reportFatal(place, "FEATURE_1_AND entry is too short"); featuresSet |= read32(desc.data()); + } else if (config->emachine == EM_AARCH64 && + type == GNU_PROPERTY_AARCH64_FEATURE_PAUTH) { + if (aarch64PauthAbiTag != std::nullopt) + reportFatal(data.data(), + "multiple GNU_PROPERTY_AARCH64_FEATURE_PAUTH properties " + "are not allowed"); + if (size != 16) + reportFatal( + data.data(), + "size of GNU_PROPERTY_AARCH64_FEATURE_PAUTH property must be 16"); + aarch64PauthAbiTag = std::array{}; + memcpy(aarch64PauthAbiTag->data(), desc.data(), + aarch64PauthAbiTag->size()); } // Padding is present in the note descriptor, if necessary. @@ -934,7 +952,7 @@ template static uint32_t readAndFeatures(const InputSection &sec) { data = data.slice(nhdr->getSize(sec.addralign)); } - return featuresSet; + return {featuresSet, aarch64PauthAbiTag}; } // Extract compatibility info for aarch64 pointer authentication from the @@ -966,13 +984,15 @@ static void readAArch64PauthAbiTag(const InputSection &sec, ObjFile &f) { " (ARM expected)"); ArrayRef desc = note.getDesc(sec.addralign); - if (desc.size() < 16) { - reportError("too short AArch64 PAuth compatibility info " - "(at least 16 bytes expected)"); + if (desc.size() != 16) { + reportError("invalid AArch64 PAuth compatibility info length " + "(exactly 16 bytes expected)"); return; } - f.aarch64PauthAbiTag = SmallVector(iterator_range(desc)); + f.aarch64PauthAbiTag = std::array{}; + memcpy(f.aarch64PauthAbiTag->data(), desc.data(), + f.aarch64PauthAbiTag->size()); } template @@ -1029,15 +1049,24 @@ InputSectionBase *ObjFile::createInputSection(uint32_t idx, // .note.gnu.property containing a single AND'ed bitmap, we discard an input // file's .note.gnu.property section. if (name == ".note.gnu.property") { - this->andFeatures = readAndFeatures(InputSection(*this, sec, name)); + std::tie(this->andFeatures, this->aarch64PauthAbiTag) = + readGnuProperty(InputSection(*this, sec, name)); return &InputSection::discarded; } - if (config->emachine == EM_AARCH64 && - name == ".note.AARCH64-PAUTH-ABI-tag") { - readAArch64PauthAbiTag(InputSection(*this, sec, name), *this); - return &InputSection::discarded; - } + // TODO: alternative PAuth ELF marking way + // To be implemented after the following PRs are merged: + // - https://github.com/ARM-software/abi-aa/pull/240 + // - https://github.com/llvm/llvm-project/pull/72714 + // + // if (config->emachine == EM_AARCH64 && + // name == ".note.AARCH64-PAUTH-ABI-tag") { + // if (this->aarch64PauthAbiTag != std::nullopt) + // error("cannot mix two PAuth ABI tag ELF marking ways in one object + // file"); + // readAArch64PauthAbiTag(InputSection(*this, sec, name), *this); + // return &InputSection::discarded; + // } // Split stacks is a feature to support a discontiguous stack, // commonly used in the programming language Go. For the details, diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h index 7b6eb0720068..63a7a5da03fd 100644 --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -218,7 +218,7 @@ class ELFFileBase : public InputFile { public: uint32_t andFeatures = 0; bool hasCommonSyms = false; - SmallVector aarch64PauthAbiTag; + std::optional> aarch64PauthAbiTag; }; // .o file. diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index 51a017d8fb6c..24737733a814 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -314,33 +314,56 @@ GnuPropertySection::GnuPropertySection() config->wordsize, ".note.gnu.property") {} void GnuPropertySection::writeTo(uint8_t *buf) { + write32(buf, 4); // Name size + write32(buf + 4, getSize() - 16); // Content size + write32(buf + 8, NT_GNU_PROPERTY_TYPE_0); // Type + memcpy(buf + 12, "GNU", 4); // Name string + uint32_t featureAndType = config->emachine == EM_AARCH64 ? GNU_PROPERTY_AARCH64_FEATURE_1_AND : GNU_PROPERTY_X86_FEATURE_1_AND; - write32(buf, 4); // Name size - write32(buf + 4, config->is64 ? 16 : 12); // Content size - write32(buf + 8, NT_GNU_PROPERTY_TYPE_0); // Type - memcpy(buf + 12, "GNU", 4); // Name string - write32(buf + 16, featureAndType); // Feature type - write32(buf + 20, 4); // Feature size - write32(buf + 24, config->andFeatures); // Feature flags - if (config->is64) - write32(buf + 28, 0); // Padding + unsigned offset = 16; + + if (config->andFeatures != 0) { + write32(buf + offset + 0, featureAndType); // Feature type + write32(buf + offset + 4, 4); // Feature size + write32(buf + offset + 8, config->andFeatures); // Feature flags + if (config->is64) + write32(buf + offset + 12, 0); // Padding + offset += 16; + } + + if (ctx.aarch64PauthAbiTag != std::nullopt) { + write32(buf + offset + 0, GNU_PROPERTY_AARCH64_FEATURE_PAUTH); + write32(buf + offset + 4, 8 * 2); + memcpy(buf + offset + 8, ctx.aarch64PauthAbiTag->data(), + ctx.aarch64PauthAbiTag->size()); + } } -size_t GnuPropertySection::getSize() const { return config->is64 ? 32 : 28; } +size_t GnuPropertySection::getSize() const { + uint32_t contentSize = 0; + if (config->andFeatures != 0) + contentSize += config->is64 ? 16 : 12; + if (ctx.aarch64PauthAbiTag != std::nullopt) { + assert(config->emachine == EM_AARCH64); + contentSize += 4 + 4 + 8 * 2; + } + assert(contentSize != 0); + return contentSize + 16; +} AArch64PauthAbiTag::AArch64PauthAbiTag() : SyntheticSection(llvm::ELF::SHF_ALLOC, llvm::ELF::SHT_NOTE, config->wordsize, ".note.AARCH64-PAUTH-ABI-tag") {} bool AArch64PauthAbiTag::isNeeded() const { - return !ctx.aarch64PauthAbiTag.empty(); + return ctx.aarch64PauthAbiTag != std::nullopt; } void AArch64PauthAbiTag::writeTo(uint8_t *buf) { - const SmallVector &data = ctx.aarch64PauthAbiTag; + std::array data = ctx.aarch64PauthAbiTag.value(); write32(buf, 4); // Name size write32(buf + 4, data.size()); // Content size write32(buf + 8, NT_ARM_TYPE_PAUTH_ABI_TAG); // Type @@ -349,11 +372,6 @@ void AArch64PauthAbiTag::writeTo(uint8_t *buf) { memset(buf + 16 + data.size(), 0, getSize() - 16 - data.size()); // Padding } -size_t AArch64PauthAbiTag::getSize() const { - return alignToPowerOf2(16 + ctx.aarch64PauthAbiTag.size(), - config->is64 ? 8 : 4); -} - BuildIdSection::BuildIdSection() : SyntheticSection(SHF_ALLOC, SHT_NOTE, 4, ".note.gnu.build-id"), hashSize(getHashSize()) {} diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h index d183a547c682..5b81d2005228 100644 --- a/lld/ELF/SyntheticSections.h +++ b/lld/ELF/SyntheticSections.h @@ -150,7 +150,7 @@ class AArch64PauthAbiTag final : public SyntheticSection { public: AArch64PauthAbiTag(); void writeTo(uint8_t *buf) override; - size_t getSize() const override; + size_t getSize() const override { return 32; }; bool isNeeded() const override; }; diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index ed68da5d442e..a1ff1d2a7969 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -521,13 +521,18 @@ template void elf::createSyntheticSections() { in.iplt = std::make_unique(); add(*in.iplt); - if (config->andFeatures) + if (config->andFeatures || ctx.aarch64PauthAbiTag.has_value()) add(*make()); - if (!ctx.aarch64PauthAbiTag.empty()) { - in.aarch64PauthAbiTag = std::make_unique(); - add(*in.aarch64PauthAbiTag); - } + // TODO: alternative PAuth ELF marking way + // To be implemented after the following PRs are merged: + // - https://github.com/ARM-software/abi-aa/pull/240 + // - https://github.com/llvm/llvm-project/pull/72714 + // + // if (!ctx.aarch64PauthAbiTag.empty()) { + // in.aarch64PauthAbiTag = std::make_unique(); + // add(*in.aarch64PauthAbiTag); + // } // .note.GNU-stack is always added when we are creating a re-linkable // object file. Other linkers are using the presence of this marker diff --git a/lld/test/ELF/aarch64-feature-pauth-gnu-prop.s b/lld/test/ELF/aarch64-feature-pauth-gnu-prop.s new file mode 100644 index 000000000000..976f9c1955c1 --- /dev/null +++ b/lld/test/ELF/aarch64-feature-pauth-gnu-prop.s @@ -0,0 +1,96 @@ +# REQUIRES: aarch64 + +# RUN: rm -rf %t && split-file %s %t && cd %t + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu abi-tag1.s -o tag11.o +# RUN: cp tag11.o tag12.o +# RUN: ld.lld -shared tag11.o tag12.o -o tagok.so +# RUN: llvm-readelf -n tagok.so | FileCheck --check-prefix OK %s + +# OK: Properties: AArch64 PAuth ABI tag: platform 0x2a, version 0x1 + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu abi-tag2.s -o tag2.o +# RUN: not ld.lld tag11.o tag12.o tag2.o -o /dev/null 2>&1 | FileCheck --check-prefix ERR1 %s + +# ERR1: error: incompatible values of AArch64 PAuth compatibility info found +# ERR1: {{.*}}: 0x2A000000000000000{{1|2}}00000000000000 +# ERR1: {{.*}}: 0x2A000000000000000{{1|2}}00000000000000 + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu abi-tag-short.s -o short.o +# RUN: not ld.lld short.o -o /dev/null 2>&1 | FileCheck --check-prefix ERR2 %s + +# ERR2: error: short.o:(.note.gnu.property+0x0): size of GNU_PROPERTY_AARCH64_FEATURE_PAUTH property must be 16 + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu abi-tag-multiple.s -o multiple.o +# RUN: not ld.lld multiple.o -o /dev/null 2>&1 | FileCheck --check-prefix ERR3 %s +# ERR3: error: multiple.o:(.note.gnu.property+0x0): multiple GNU_PROPERTY_AARCH64_FEATURE_PAUTH properties are not allowed + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu no-info.s -o noinfo1.o +# RUN: cp noinfo1.o noinfo2.o +# RUN: not ld.lld -z pauth-report=error tag11.o noinfo1.o noinfo2.o -o /dev/null 2>&1 | FileCheck --check-prefix ERR4 %s +# RUN: ld.lld -z pauth-report=warning tag11.o noinfo1.o noinfo2.o -o /dev/null 2>&1 | FileCheck --check-prefix WARN %s +# RUN: ld.lld -z pauth-report=none tag11.o noinfo1.o noinfo2.o -o /dev/null 2>&1 | FileCheck --check-prefix NONE %s + +# ERR4: error: {{.*}}noinfo1.o has no AArch64 PAuth compatibility info while {{.*}}tag11.o has one; either all or no input files must have it +# ERR4-NEXT: error: {{.*}}noinfo2.o has no AArch64 PAuth compatibility info while {{.*}}tag11.o has one; either all or no input files must have it +# WARN: warning: {{.*}}noinfo1.o has no AArch64 PAuth compatibility info while {{.*}}tag11.o has one; either all or no input files must have it +# WARN-NEXT: warning: {{.*}}noinfo2.o has no AArch64 PAuth compatibility info while {{.*}}tag11.o has one; either all or no input files must have it +# NONE-NOT: {{.*}} has no AArch64 PAuth compatibility info while {{.*}} has one; either all or no input files must have it + +#--- abi-tag-short.s + +# Version is 4 bytes instead of 8 bytes, must emit an error + +.section ".note.gnu.property", "a" +.long 4 +.long 20 +.long 5 +.asciz "GNU" +.long 0xc0000001 +.long 12 +.quad 2 +.long 31 + +#--- abi-tag-multiple.s + +.section ".note.gnu.property", "a" +.long 4 +.long 48 +.long 5 +.asciz "GNU" +.long 0xc0000001 +.long 16 +.quad 42 // platform +.quad 1 // version +.long 0xc0000001 +.long 16 +.quad 42 // platform +.quad 1 // version + +#--- abi-tag1.s + +.section ".note.gnu.property", "a" +.long 4 +.long 24 +.long 5 +.asciz "GNU" +.long 0xc0000001 +.long 16 +.quad 42 // platform +.quad 1 // version + +#--- abi-tag2.s + +.section ".note.gnu.property", "a" +.long 4 +.long 24 +.long 5 +.asciz "GNU" +.long 0xc0000001 +.long 16 +.quad 42 // platform +.quad 2 // version + +#--- no-info.s + +.section ".test", "a" diff --git a/lld/test/ELF/aarch64-feature-pauth.s b/lld/test/ELF/aarch64-feature-pauth.s index 0520b2f28631..c7fe27b330ac 100644 --- a/lld/test/ELF/aarch64-feature-pauth.s +++ b/lld/test/ELF/aarch64-feature-pauth.s @@ -1,5 +1,11 @@ # REQUIRES: aarch64 +# XFAIL: * +# TODO: alternative PAuth ELF marking way +# To be implemented after the following PRs are merged: +# - https://github.com/ARM-software/abi-aa/pull/240 +# - https://github.com/llvm/llvm-project/pull/72714 + # RUN: rm -rf %t && split-file %s %t && cd %t # RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu abi-tag1.s -o tag11.o diff --git a/lldb/include/lldb/Symbol/CompilerType.h b/lldb/include/lldb/Symbol/CompilerType.h index 2d7092d2c93f..4e5b4b76d459 100644 --- a/lldb/include/lldb/Symbol/CompilerType.h +++ b/lldb/include/lldb/Symbol/CompilerType.h @@ -215,6 +215,12 @@ class CompilerType { size_t GetPointerByteSize() const; /// \} + unsigned GetPtrAuthKey() const; + + unsigned GetPtrAuthDiscriminator() const; + + bool GetPtrAuthAddressDiversity() const; + /// Accessors. /// \{ @@ -322,6 +328,13 @@ class CompilerType { /// Create related types using the current type's AST CompilerType GetBasicTypeFromAST(lldb::BasicType basic_type) const; + + /// Return a new CompilerType adds a ptrauth modifier with given parameters to + /// this type if this type is valid and the type system supports ptrauth + /// modifiers, else return an invalid type. Note that this does not check if + /// this type is a pointer. + CompilerType AddPtrAuthModifier(unsigned key, bool isAddressDiscriminated, + unsigned extraDiscriminator) const; /// \} /// Exploring the type. diff --git a/lldb/include/lldb/Symbol/ObjectFile.h b/lldb/include/lldb/Symbol/ObjectFile.h index 6348d8103f85..2027cb3251de 100644 --- a/lldb/include/lldb/Symbol/ObjectFile.h +++ b/lldb/include/lldb/Symbol/ObjectFile.h @@ -23,6 +23,7 @@ #include "llvm/Support/Threading.h" #include "llvm/Support/VersionTuple.h" #include +#include namespace lldb_private { @@ -331,6 +332,15 @@ class ObjectFile : public std::enable_shared_from_this, /// virtual void RelocateSection(lldb_private::Section *section); + /// Parse ELF's AArch64 PAuth ABI from GNU property section. If it's missing + /// or the object file is not an ELF, return std::nullopt. Define this as a + /// virtual function in a base class since direct usage of ObjectFileELF in + /// ClangExpressionParser requires linking against the ObjectFileELF plugin. + virtual std::optional> + ParseGNUPropertyAArch64PAuthABI() { + return std::nullopt; + } + /// Appends a Symbol for the specified so_addr to the symbol table. /// /// If verify_unique is false, the symbol table is not searched to determine diff --git a/lldb/include/lldb/Symbol/Type.h b/lldb/include/lldb/Symbol/Type.h index 046501931d21..5dcd98387c90 100644 --- a/lldb/include/lldb/Symbol/Type.h +++ b/lldb/include/lldb/Symbol/Type.h @@ -92,7 +92,9 @@ class Type : public std::enable_shared_from_this, public UserID { /// This type is the type whose UID is m_encoding_uid as an atomic type. eEncodingIsAtomicUID, /// This type is the synthetic type whose UID is m_encoding_uid. - eEncodingIsSyntheticUID + eEncodingIsSyntheticUID, + /// This type is a signed pointer. + eEncodingIsLLVMPtrAuthUID }; enum class ResolveState : unsigned char { diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h index eb6e453e1aec..52313c86a558 100644 --- a/lldb/include/lldb/Symbol/TypeSystem.h +++ b/lldb/include/lldb/Symbol/TypeSystem.h @@ -215,6 +215,16 @@ class TypeSystem : public PluginInterface, virtual uint32_t GetPointerByteSize() = 0; + // TODO: are we allowed to insert virtual functions in the middle of the class + // interface and break ABI? + virtual unsigned GetPtrAuthKey(lldb::opaque_compiler_type_t type) = 0; + + virtual unsigned + GetPtrAuthDiscriminator(lldb::opaque_compiler_type_t type) = 0; + + virtual bool + GetPtrAuthAddressDiversity(lldb::opaque_compiler_type_t type) = 0; + // Accessors virtual ConstString GetTypeName(lldb::opaque_compiler_type_t type, @@ -279,6 +289,13 @@ class TypeSystem : public PluginInterface, virtual CompilerType AddRestrictModifier(lldb::opaque_compiler_type_t type); + // TODO: are we allowed to insert virtual functions in the middle of the class + // interface and break ABI? + virtual CompilerType AddPtrAuthModifier(lldb::opaque_compiler_type_t type, + unsigned key, + bool isAddressDiscriminated, + unsigned extraDiscriminator); + /// \param opaque_payload The m_payload field of Type, which may /// carry TypeSystem-specific extra information. virtual CompilerType CreateTypedef(lldb::opaque_compiler_type_t type, diff --git a/lldb/include/lldb/Target/LanguageRuntime.h b/lldb/include/lldb/Target/LanguageRuntime.h index eff79a0bf0d0..1c875d8c8745 100644 --- a/lldb/include/lldb/Target/LanguageRuntime.h +++ b/lldb/include/lldb/Target/LanguageRuntime.h @@ -226,6 +226,8 @@ class LanguageRuntime : public Runtime, public PluginInterface { } LanguageRuntime(Process *process); + + std::pair m_aarch64_pauth_abi_tag; }; } // namespace lldb_private diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp index f65192a3225e..7cb1655ab7fb 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp @@ -36,6 +36,7 @@ #include "clang/Sema/SemaConsumer.h" #include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/ELF.h" #include "llvm/ExecutionEngine/ExecutionEngine.h" #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/Debug.h" @@ -445,6 +446,11 @@ ClangExpressionParser::ClangExpressionParser( // Supported subsets of x86 if (target_machine == llvm::Triple::x86 || target_machine == llvm::Triple::x86_64) { + // FIXME: shouldn't this be placed after + // `auto target_info = TargetInfo::CreateTargetInfo(...)` + // (see `if (target_machine == llvm::Triple::aarch64)`)? + // It computes `Features` from `FeatureMap` and `FeaturesAsWritten` and + // erases initial `Features` vector. m_compiler->getTargetOpts().Features.push_back("+sse"); m_compiler->getTargetOpts().Features.push_back("+sse2"); } @@ -467,6 +473,22 @@ ClangExpressionParser::ClangExpressionParser( auto target_info = TargetInfo::CreateTargetInfo( m_compiler->getDiagnostics(), m_compiler->getInvocation().TargetOpts); + + std::optional> elf_pauth_abi_tag; + if (target_machine == llvm::Triple::aarch64) { + do { + Module *module_sp = target_sp->GetExecutableModulePointer(); + if (module_sp == nullptr) + break; + ObjectFile *obj_file = module_sp->GetObjectFile(); + if (obj_file == nullptr) + break; + elf_pauth_abi_tag = obj_file->ParseGNUPropertyAArch64PAuthABI(); + if (elf_pauth_abi_tag != std::nullopt) + target_info->getTargetOpts().Features.push_back("+pauth"); + } while (false); + } + if (log) { LLDB_LOGF(log, "Target datalayout string: '%s'", target_info->getDataLayoutString()); @@ -482,6 +504,23 @@ ClangExpressionParser::ClangExpressionParser( lldb::LanguageType language = expr.Language(); LangOptions &lang_opts = m_compiler->getLangOpts(); + if (elf_pauth_abi_tag != std::nullopt) { + uint64_t elf_pauth_abi_platform = elf_pauth_abi_tag->first; + // TODO: store this magic constant corresponding to Linux platform in some + // header + if (elf_pauth_abi_platform == + llvm::ELF::GNU_PROPERTY_AARCH64_FEATURE_PAUTH_PLATFORM_LINUX) { + uint64_t elf_pauth_abi_version = elf_pauth_abi_tag->second; + lang_opts.PointerAuthCalls = elf_pauth_abi_version & (1 << 0); + lang_opts.PointerAuthReturns = elf_pauth_abi_version & (1 << 1); + lang_opts.PointerAuthVTPtrAddressDiscrimination = + elf_pauth_abi_version & (1 << 2); + lang_opts.PointerAuthVTPtrTypeDiscrimination = + elf_pauth_abi_version & (1 << 3); + lang_opts.PointerAuthInitFini = elf_pauth_abi_version & (1 << 4); + } + } + switch (language) { case lldb::eLanguageTypeC: case lldb::eLanguageTypeC89: @@ -622,6 +661,10 @@ ClangExpressionParser::ClangExpressionParser( else m_compiler->getCodeGenOpts().setDebugInfo(codegenoptions::NoDebugInfo); + CompilerInvocation::setDefaultPointerAuthOptions( + m_compiler->getCodeGenOpts().PointerAuth, lang_opts, + target_arch.GetTriple()); + // Disable some warnings. SetupDefaultClangDiagnostics(*m_compiler); diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp index 2da971dff895..bff0940a6027 100644 --- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -1284,6 +1284,68 @@ ObjectFileELF::RefineModuleDetailsFromNote(lldb_private::DataExtractor &data, return error; } +std::optional> +ObjectFileELF::ParseGNUPropertyAArch64PAuthABI() { + // TODO: store parsing results in some kind of cache to avoid recurrent + // parsing on multiple calls + assert(m_arch_spec.GetMachine() == llvm::Triple::aarch64); + + SectionList *SL = GetSectionList(); + if (SL == nullptr) + return std::nullopt; + + lldb::SectionSP GNUPropSecSP = + SL->FindSectionByName(ConstString(".note.gnu.property")); + if (GNUPropSecSP == nullptr) + return std::nullopt; + + DataExtractor Data; + lldb::offset_t Length = GNUPropSecSP->GetSectionData(Data); + if (Length < 16) + return std::nullopt; + + lldb::offset_t Offset = 0; + uint32_t NameSz = Data.GetU32(&Offset); + if (NameSz != 4) + return std::nullopt; + + uint32_t ContentSz = Data.GetU32(&Offset); + if (ContentSz + 16 != Length) // the section is ill-formed + return std::nullopt; + uint32_t SectionType = Data.GetU32(&Offset); + if (SectionType != NT_GNU_PROPERTY_TYPE_0) + return std::nullopt; + + llvm::StringRef Name = Data.GetCStr(&Offset, NameSz); + if (Name != "GNU") + return std::nullopt; + + while (Offset < Length) { + lldb::offset_t OldOffset = Offset; + + uint32_t FeatureType = Data.GetU32(&Offset); + uint32_t Size = Data.GetU32(&Offset); + if (OldOffset + 8 != Offset) // there were not enough bytes + return std::nullopt; + if (FeatureType != GNU_PROPERTY_AARCH64_FEATURE_PAUTH) { + Offset += Size; + Offset = + llvm::alignTo(Offset, (uint64_t(1) << GNUPropSecSP->GetLog2Align())); + continue; + } + if (Size != 16) + return std::nullopt; + + uint64_t PAuthABIPlatform = Data.GetU64(&Offset); + uint64_t PAuthABIVersion = Data.GetU64(&Offset); + if (OldOffset + 24 != Offset) // there were not enough bytes + return std::nullopt; + return std::make_pair(PAuthABIPlatform, PAuthABIVersion); + } + + return std::nullopt; +} + void ObjectFileELF::ParseARMAttributes(DataExtractor &data, uint64_t length, ArchSpec &arch_spec) { lldb::offset_t Offset = 0; diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h index bc8e34981a9d..d258977d96ea 100644 --- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h @@ -12,6 +12,7 @@ #include #include +#include #include #include "lldb/Symbol/ObjectFile.h" @@ -155,6 +156,9 @@ class ObjectFileELF : public lldb_private::ObjectFile { void RelocateSection(lldb_private::Section *section) override; + std::optional> + ParseGNUPropertyAArch64PAuthABI() override; + protected: std::vector diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index 37fb16d4e035..5bee3618372f 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -504,6 +504,10 @@ TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc, type_sp = ParsePointerToMemberType(die, attrs); break; } + case DW_TAG_LLVM_ptrauth_type: { + type_sp = ParsePtrAuthQualifiedType(die, attrs); + break; + } default: dwarf->GetObjectFile()->GetModule()->ReportError( "[{0:x16}]: unhandled type tag {1:x4} ({2}), " @@ -1375,6 +1379,33 @@ TypeSP DWARFASTParserClang::ParsePointerToMemberType( return nullptr; } +TypeSP DWARFASTParserClang::ParsePtrAuthQualifiedType( + const DWARFDIE &die, const ParsedDWARFTypeAttributes &attrs) { + SymbolFileDWARF *dwarf = die.GetDWARF(); + Type *pointer_type = dwarf->ResolveTypeUID(attrs.type.Reference(), true); + + if (pointer_type == nullptr) + return nullptr; + + CompilerType pointer_clang_type = pointer_type->GetForwardCompilerType(); + + unsigned key = die.GetAttributeValueAsUnsigned(DW_AT_LLVM_ptrauth_key, 0); + bool has_addr_discr = die.GetAttributeValueAsUnsigned( + DW_AT_LLVM_ptrauth_address_discriminated, false); + unsigned extra_discr = die.GetAttributeValueAsUnsigned( + DW_AT_LLVM_ptrauth_extra_discriminator, 0); + CompilerType clang_type = m_ast.AddPtrAuthModifier( + pointer_clang_type.GetOpaqueQualType(), key, has_addr_discr, extra_discr); + + TypeSP type_sp = dwarf->MakeType( + die.GetID(), attrs.name, pointer_type->GetByteSize(nullptr), nullptr, + attrs.type.Reference().GetID(), Type::eEncodingIsLLVMPtrAuthUID, + &attrs.decl, clang_type, Type::ResolveState::Forward); + + dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); + return type_sp; +} + void DWARFASTParserClang::ParseInheritance( const DWARFDIE &die, const DWARFDIE &parent_die, const CompilerType class_clang_type, const AccessType default_accessibility, @@ -1811,6 +1842,81 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc, attrs.name.GetCString(), tag_decl_kind, attrs.class_language, &metadata, attrs.exports_symbols); } + + if (metadata.GetIsDynamicCXXType()) { + clang::RecordDecl *record_decl = m_ast.GetAsRecordDecl(clang_type); + DWARFDIE vptr_type_die = + die.GetFirstChild().GetAttributeValueAsReferenceDIE(DW_AT_type); + if (vptr_type_die.Tag() == DW_TAG_LLVM_ptrauth_type) { + unsigned key = vptr_type_die.GetAttributeValueAsUnsigned( + DW_AT_LLVM_ptrauth_key, -1); + unsigned discriminator = vptr_type_die.GetAttributeValueAsUnsigned( + DW_AT_LLVM_ptrauth_extra_discriminator, -1); + unsigned has_addr_discr = vptr_type_die.GetAttributeValueAsUnsigned( + DW_AT_LLVM_ptrauth_address_discriminated, -1); + + auto error_missing = [&vptr_type_die](const dw_attr_t attr) { + vptr_type_die.GetDWARF()->GetObjectFile()->GetModule()->ReportError( + "[{0:x16}]: missing attribute {1:x4} ({2}) required for signed " + "vtable pointer", + vptr_type_die.GetOffset(), attr, DW_AT_value_to_name(attr)); + }; + + if (key == unsigned(-1)) + error_missing(DW_AT_LLVM_ptrauth_key); + if (discriminator == unsigned(-1)) + error_missing(DW_AT_LLVM_ptrauth_extra_discriminator); + if (has_addr_discr == unsigned(-1)) + error_missing(DW_AT_LLVM_ptrauth_extra_discriminator); + + record_decl->addAttr( + clang::VTablePointerAuthenticationAttr::CreateImplicit( + m_ast.getASTContext(), + key == 2 + ? clang::VTablePointerAuthenticationAttr::ProcessDependent + : clang::VTablePointerAuthenticationAttr:: + ProcessIndependent, + has_addr_discr ? clang::VTablePointerAuthenticationAttr:: + AddressDiscrimination + : clang::VTablePointerAuthenticationAttr:: + NoAddressDiscrimination, + clang::VTablePointerAuthenticationAttr::CustomDiscrimination, + discriminator)); + } + } + + // TODO: check that each annotation does not appear more than once + std::optional pauth_key, pauth_disc; + for (DWARFDIE die_child = die.GetFirstChild(); die_child; + die_child = die_child.GetSibling()) { + if (die_child.Tag() != DW_TAG_LLVM_annotation) + continue; + if (die_child.GetAttributeValueAsString(DW_AT_name, nullptr) == + llvm::StringRef("ptrauth_struct_key")) + pauth_key = + die_child.GetAttributeValueAsOptionalUnsigned(DW_AT_const_value); + else if (die_child.GetAttributeValueAsString(DW_AT_name, nullptr) == + llvm::StringRef("ptrauth_struct_disc")) + pauth_disc = + die_child.GetAttributeValueAsOptionalUnsigned(DW_AT_const_value); + } + + // TODO: do we need some handling for one std::nullopt out of two? + if (pauth_key != std::nullopt && pauth_disc != std::nullopt) { + clang::ASTContext &ctx = m_ast.getASTContext(); + llvm::APInt key_int = llvm::APInt(ctx.getTypeSize(ctx.UnsignedIntTy), + pauth_key.value(), false); + auto *key = clang::IntegerLiteral::Create(ctx, key_int, ctx.UnsignedIntTy, + clang::SourceLocation()); + llvm::APInt disc_int = llvm::APInt(ctx.getTypeSize(ctx.UnsignedIntTy), + pauth_disc.value(), false); + auto *disc = clang::IntegerLiteral::Create( + ctx, disc_int, ctx.UnsignedIntTy, clang::SourceLocation()); + + clang::RecordDecl *record_decl = m_ast.GetAsRecordDecl(clang_type); + record_decl->addAttr( + clang::PointerAuthStructAttr::CreateImplicit(ctx, key, disc)); + } } // Store a forward declaration to this class type in case any diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h index 88bfc490e890..15f8d44c2434 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h @@ -296,6 +296,10 @@ class DWARFASTParserClang : public DWARFASTParser { lldb::TypeSP ParsePointerToMemberType(const DWARFDIE &die, const ParsedDWARFTypeAttributes &attrs); + lldb::TypeSP + ParsePtrAuthQualifiedType(const DWARFDIE &die, + const ParsedDWARFTypeAttributes &attrs); + /// Parses a DW_TAG_inheritance DIE into a base/super class. /// /// \param die The DW_TAG_inheritance DIE to parse. diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 69cff0f35ae4..df863c9920a8 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -3048,6 +3048,35 @@ bool TypeSystemClang::IsCStringType(lldb::opaque_compiler_type_t type, return false; } +unsigned TypeSystemClang::GetPtrAuthKey(lldb::opaque_compiler_type_t type) { + if (type) { + clang::QualType qual_type(GetCanonicalQualType(type)); + if (auto pointer_auth = qual_type.getPointerAuth()) + return pointer_auth.getKey(); + } + return 0; +} + +unsigned +TypeSystemClang::GetPtrAuthDiscriminator(lldb::opaque_compiler_type_t type) { + if (type) { + clang::QualType qual_type(GetCanonicalQualType(type)); + if (auto pointer_auth = qual_type.getPointerAuth()) + return pointer_auth.getExtraDiscriminator(); + } + return 0; +} + +bool TypeSystemClang::GetPtrAuthAddressDiversity( + lldb::opaque_compiler_type_t type) { + if (type) { + clang::QualType qual_type(GetCanonicalQualType(type)); + if (auto pointer_auth = qual_type.getPointerAuth()) + return pointer_auth.isAddressDiscriminated(); + } + return false; +} + bool TypeSystemClang::IsFunctionType(lldb::opaque_compiler_type_t type) { auto isFunctionType = [&](clang::QualType qual_type) { return qual_type->isFunctionType(); @@ -4622,6 +4651,24 @@ TypeSystemClang::AddConstModifier(lldb::opaque_compiler_type_t type) { return CompilerType(); } +CompilerType +TypeSystemClang::AddPtrAuthModifier(lldb::opaque_compiler_type_t type, + unsigned key, bool isAddressDiscriminated, + unsigned extraDiscriminator) { + if (type) { + clang::ASTContext &clang_ast = getASTContext(); + auto pauth = PointerAuthQualifier::Create( + key, isAddressDiscriminated, extraDiscriminator, + PointerAuthenticationMode::SignAndAuth, + /* isIsaPointer */ false, + /* authenticatesNullValues */ false); + clang::QualType result = + clang_ast.getPointerAuthType(GetQualType(type), pauth); + return GetType(result); + } + return CompilerType(); +} + CompilerType TypeSystemClang::AddVolatileModifier(lldb::opaque_compiler_type_t type) { if (type) { diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index 0544de3cd33b..e9f41ff880bc 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -642,6 +642,10 @@ class TypeSystemClang : public TypeSystem { bool IsFloatingPointType(lldb::opaque_compiler_type_t type, uint32_t &count, bool &is_complex) override; + unsigned GetPtrAuthKey(lldb::opaque_compiler_type_t type) override; + unsigned GetPtrAuthDiscriminator(lldb::opaque_compiler_type_t type) override; + bool GetPtrAuthAddressDiversity(lldb::opaque_compiler_type_t type) override; + bool IsFunctionType(lldb::opaque_compiler_type_t type) override; uint32_t IsHomogeneousAggregate(lldb::opaque_compiler_type_t type, @@ -782,6 +786,10 @@ class TypeSystemClang : public TypeSystem { CompilerType AddConstModifier(lldb::opaque_compiler_type_t type) override; + CompilerType AddPtrAuthModifier(lldb::opaque_compiler_type_t type, + unsigned key, bool isAddressDiscriminated, + unsigned extraDiscriminator) override; + CompilerType AddVolatileModifier(lldb::opaque_compiler_type_t type) override; CompilerType AddRestrictModifier(lldb::opaque_compiler_type_t type) override; diff --git a/lldb/source/Symbol/CompilerType.cpp b/lldb/source/Symbol/CompilerType.cpp index 33f7e63d9be4..5cd33a9ed4c1 100644 --- a/lldb/source/Symbol/CompilerType.cpp +++ b/lldb/source/Symbol/CompilerType.cpp @@ -108,6 +108,27 @@ bool CompilerType::IsConst() const { return false; } +unsigned CompilerType::GetPtrAuthKey() const { + if (IsValid()) + if (auto type_system_sp = GetTypeSystem()) + return type_system_sp->GetPtrAuthKey(m_type); + return 0; +} + +unsigned CompilerType::GetPtrAuthDiscriminator() const { + if (IsValid()) + if (auto type_system_sp = GetTypeSystem()) + return type_system_sp->GetPtrAuthDiscriminator(m_type); + return 0; +} + +bool CompilerType::GetPtrAuthAddressDiversity() const { + if (IsValid()) + if (auto type_system_sp = GetTypeSystem()) + return type_system_sp->GetPtrAuthAddressDiversity(m_type); + return false; +} + bool CompilerType::IsCStringType(uint32_t &length) const { if (IsValid()) if (auto type_system_sp = GetTypeSystem()) @@ -485,6 +506,17 @@ CompilerType CompilerType::GetPointerType() const { return CompilerType(); } +CompilerType +CompilerType::AddPtrAuthModifier(unsigned key, bool isAddressDiscriminated, + unsigned extraDiscriminator) const { + if (IsValid()) { + if (auto type_system_sp = GetTypeSystem()) + return type_system_sp->AddPtrAuthModifier( + m_type, key, isAddressDiscriminated, extraDiscriminator); + } + return CompilerType(); +} + CompilerType CompilerType::GetLValueReferenceType() const { if (IsValid()) if (auto type_system_sp = GetTypeSystem()) diff --git a/lldb/source/Symbol/Type.cpp b/lldb/source/Symbol/Type.cpp index 66284eb73cad..e27642a84157 100644 --- a/lldb/source/Symbol/Type.cpp +++ b/lldb/source/Symbol/Type.cpp @@ -230,6 +230,9 @@ void Type::GetDescription(Stream *s, lldb::DescriptionLevel level, case eEncodingIsSyntheticUID: s->PutCString(" (synthetic type)"); break; + case eEncodingIsLLVMPtrAuthUID: + s->PutCString(" (ptrauth type)"); + break; } } } @@ -291,6 +294,8 @@ void Type::Dump(Stream *s, bool show_context, lldb::DescriptionLevel level) { case eEncodingIsSyntheticUID: s->PutCString(" (synthetic type)"); break; + case eEncodingIsLLVMPtrAuthUID: + s->PutCString(" (ptrauth type)"); } } @@ -376,12 +381,13 @@ std::optional Type::GetByteSize(ExecutionContextScope *exe_scope) { // If we are a pointer or reference, then this is just a pointer size; case eEncodingIsPointerUID: case eEncodingIsLValueReferenceUID: - case eEncodingIsRValueReferenceUID: { - if (ArchSpec arch = m_symbol_file->GetObjectFile()->GetArchitecture()) { - m_byte_size = arch.GetAddressByteSize(); - m_byte_size_has_value = true; - return static_cast(m_byte_size); - } + case eEncodingIsRValueReferenceUID: + case eEncodingIsLLVMPtrAuthUID: { + if (ArchSpec arch = m_symbol_file->GetObjectFile()->GetArchitecture()) { + m_byte_size = arch.GetAddressByteSize(); + m_byte_size_has_value = true; + return static_cast(m_byte_size); + } } break; } return {}; diff --git a/lldb/source/Symbol/TypeSystem.cpp b/lldb/source/Symbol/TypeSystem.cpp index 24f202930565..47949518f7c6 100644 --- a/lldb/source/Symbol/TypeSystem.cpp +++ b/lldb/source/Symbol/TypeSystem.cpp @@ -93,6 +93,13 @@ CompilerType TypeSystem::AddConstModifier(lldb::opaque_compiler_type_t type) { return CompilerType(); } +CompilerType TypeSystem::AddPtrAuthModifier(lldb::opaque_compiler_type_t type, + unsigned key, + bool isAddressDiscriminated, + unsigned extraDiscriminator) { + return CompilerType(); +} + CompilerType TypeSystem::AddVolatileModifier(lldb::opaque_compiler_type_t type) { return CompilerType(); diff --git a/lldb/unittests/ObjectFile/ELF/TestObjectFileELF.cpp b/lldb/unittests/ObjectFile/ELF/TestObjectFileELF.cpp index 80abc5b80f84..b234f68b4227 100644 --- a/lldb/unittests/ObjectFile/ELF/TestObjectFileELF.cpp +++ b/lldb/unittests/ObjectFile/ELF/TestObjectFileELF.cpp @@ -117,6 +117,161 @@ TEST_F(ObjectFileELFTest, SectionsResolveConsistently) { EXPECT_EQ(text_sp, start->GetAddress().GetSection()); } +TEST_F(ObjectFileELFTest, GNUPropertyAArch64PAuthABI) { + // Successful parsing + { + llvm::StringRef SinglePropertyYaml = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_AARCH64 +Sections: + - Name: .note.gnu.property + Type: SHT_NOTE + Flags: [ SHF_ALLOC ] + AddressAlign: 0x8 + Notes: + - Name: GNU + Desc: 010000C01000000002000000000000001F00000000000000 + # ^^^^^^^^ + # type = 0xC0000001 = GNU_PROPERTY_AARCH64_FEATURE_PAUTH + # ^^^^^^^^ + # size = 0x00000010 = 16 + # ^^^^^^^^^^^^^^^^ + # platform = 0x0000000000000002 = 2 + # ^^^^^^^^^^^^^^^^ + # version = 0x000000000000001F = 31 + Type: NT_GNU_PROPERTY_TYPE_0 +... +)"; + llvm::StringRef MultiplePropertiesYaml = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_AARCH64 +Sections: + - Name: .note.gnu.property + Type: SHT_NOTE + Flags: [ SHF_ALLOC ] + AddressAlign: 0x8 + Notes: + - Name: GNU + Desc: 42424242040000001234567800000000010000C01000000002000000000000001F00000000000000 + # ^^^^^^^^ + # dummy property type = 0x42424242 + # ^^^^^^^^ + # dummy property size = 0x00000008 = 4 + # ^^^^^^^^________ + # dummy property contents = 0x78563412 and padding + # ^^^^^^^^ + # type = 0xC0000001 = GNU_PROPERTY_AARCH64_FEATURE_PAUTH + # ^^^^^^^^ + # size = 0x00000010 = 16 + # ^^^^^^^^^^^^^^^^ + # platform = 0x0000000000000002 = 2 + # ^^^^^^^^^^^^^^^^ + # version = 0x000000000000001F = 31 + Type: NT_GNU_PROPERTY_TYPE_0 +... +)"; + std::array, 2> TestFiles = { + TestFile::fromYaml(SinglePropertyYaml), + TestFile::fromYaml(MultiplePropertiesYaml)}; + + for (auto &TF : TestFiles) { + ASSERT_THAT_EXPECTED(TF, llvm::Succeeded()); + + auto module_sp = std::make_shared(TF->moduleSpec()); + ObjectFile *obj_file = module_sp->GetObjectFile(); + ASSERT_NE(nullptr, obj_file); + + std::optional> pauthabi = + obj_file->ParseGNUPropertyAArch64PAuthABI(); + ASSERT_NE(std::nullopt, pauthabi); + ASSERT_EQ(uint64_t(2), pauthabi->first); + ASSERT_EQ(uint64_t(31), pauthabi->second); + } + } + + // Error during parsing + { + llvm::StringRef InvalidNameSizeYaml = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_AARCH64 +Sections: + - Name: .note.gnu.property + Type: SHT_NOTE + Flags: [ SHF_ALLOC ] + AddressAlign: 0x8 + Notes: + - Name: XXXXX + Desc: 010000C01000000002000000000000001F00000000000000 + Type: NT_GNU_PROPERTY_TYPE_0 +... +)"; + llvm::StringRef InvalidNameYaml = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_AARCH64 +Sections: + - Name: .note.gnu.property + Type: SHT_NOTE + Flags: [ SHF_ALLOC ] + AddressAlign: 0x8 + Notes: + - Name: XXX + Desc: 010000C01000000002000000000000001F00000000000000 + Type: NT_GNU_PROPERTY_TYPE_0 +... +)"; + llvm::StringRef InvalidPropertySizeYaml = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_AARCH64 +Sections: + - Name: .note.gnu.property + Type: SHT_NOTE + Flags: [ SHF_ALLOC ] + AddressAlign: 0x8 + Notes: + - Name: XXXXX + Desc: 0000000000000000 + Type: NT_GNU_PROPERTY_TYPE_0 +... +)"; + std::array, 3> TestFiles = { + TestFile::fromYaml(InvalidNameSizeYaml), + TestFile::fromYaml(InvalidNameYaml), + TestFile::fromYaml(InvalidPropertySizeYaml)}; + + for (auto &TF : TestFiles) { + ASSERT_THAT_EXPECTED(TF, llvm::Succeeded()); + + auto module_sp = std::make_shared(TF->moduleSpec()); + ObjectFile *obj_file = module_sp->GetObjectFile(); + ASSERT_NE(nullptr, obj_file); + + std::optional> pauthabi = + obj_file->ParseGNUPropertyAArch64PAuthABI(); + ASSERT_EQ(std::nullopt, pauthabi); + } + } +} + // Test that GetModuleSpecifications works on an "atypical" object file which // has section headers right after the ELF header (instead of the more common // layout where the section headers are at the very end of the object file). diff --git a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp index 20a085741f73..48e09abf4472 100644 --- a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp +++ b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp @@ -286,6 +286,552 @@ TEST_F(DWARFASTParserClangTests, TestCallingConventionParsing) { ASSERT_EQ(found_function_types, expected_function_types); } +TEST_F(DWARFASTParserClangTests, TestPtrAuthParsing) { + // Tests parsing values with type DW_TAG_LLVM_ptrauth_type corresponding to + // explicitly signed raw function pointers + + // This is Dwarf for the following C code: + // ``` + // void (*__ptrauth(0, 0, 42) a)(); + // ``` + + const char *yamldata = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_AARCH64 +DWARF: + debug_str: + - a + debug_abbrev: + - ID: 0 + Table: + - Code: 0x01 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_language + Form: DW_FORM_data2 + - Code: 0x02 + Tag: DW_TAG_variable + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_external + Form: DW_FORM_flag_present + - Code: 0x03 + Tag: DW_TAG_LLVM_ptrauth_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_LLVM_ptrauth_key + Form: DW_FORM_data1 + - Attribute: DW_AT_LLVM_ptrauth_extra_discriminator + Form: DW_FORM_data2 + - Code: 0x04 + Tag: DW_TAG_pointer_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Code: 0x05 + Tag: DW_TAG_subroutine_type + Children: DW_CHILDREN_yes + - Code: 0x06 + Tag: DW_TAG_unspecified_parameters + Children: DW_CHILDREN_no + + debug_info: + - Version: 5 + UnitType: DW_UT_compile + AddrSize: 8 + Entries: +# 0x0c: DW_TAG_compile_unit +# DW_AT_language [DW_FORM_data2] (DW_LANG_C99) + - AbbrCode: 0x01 + Values: + - Value: 0x0c + +# 0x0f: DW_TAG_variable +# DW_AT_name [DW_FORM_strp] (\"a\") +# DW_AT_type [DW_FORM_ref4] (0x00000018 \"void (*__ptrauth(0, 0, 0x02a)\") +# DW_AT_external [DW_FORM_flag_present] (true) + - AbbrCode: 0x02 + Values: + - Value: 0x00 + - Value: 0x18 + +# 0x18: DW_TAG_LLVM_ptrauth_type +# DW_AT_type [DW_FORM_ref4] (0x00000020 \"void (*)(...)\") +# DW_AT_LLVM_ptrauth_key [DW_FORM_data1] (0x00) +# DW_AT_LLVM_ptrauth_extra_discriminator [DW_FORM_data2] (0x002a) + - AbbrCode: 0x03 + Values: + - Value: 0x20 + - Value: 0x00 + - Value: 0x2a + +# 0x20: DW_TAG_pointer_type +# DW_AT_type [DW_AT_type [DW_FORM_ref4] (0x00000025 \"void (...)\") + - AbbrCode: 0x04 + Values: + - Value: 0x25 + +# 0x25: DW_TAG_subroutine_type + - AbbrCode: 0x05 + +# 0x26: DW_TAG_unspecified_parameters + - AbbrCode: 0x06 + + - AbbrCode: 0x00 # end of child tags of 0x25 + - AbbrCode: 0x00 # end of child tags of 0x0c +... +)"; + YAMLModuleTester t(yamldata); + + DWARFUnit *unit = t.GetDwarfUnit(); + ASSERT_NE(unit, nullptr); + const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE(); + ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit); + DWARFDIE cu_die(unit, cu_entry); + + auto holder = std::make_unique("ast"); + auto &ast_ctx = *holder->GetAST(); + DWARFASTParserClangStub ast_parser(ast_ctx); + + DWARFDIE ptrauth_variable = cu_die.GetFirstChild(); + ASSERT_EQ(ptrauth_variable.Tag(), DW_TAG_variable); + DWARFDIE ptrauth_type = + ptrauth_variable.GetAttributeValueAsReferenceDIE(DW_AT_type); + ASSERT_EQ(ptrauth_type.Tag(), DW_TAG_LLVM_ptrauth_type); + + SymbolContext sc; + bool new_type = false; + lldb::TypeSP type = + ast_parser.ParseTypeFromDWARF(sc, ptrauth_type, &new_type); + std::string type_as_string = + type->GetForwardCompilerType().GetTypeName().AsCString(); + ASSERT_EQ(type_as_string, "void (*__ptrauth(0,0,42))(...)"); +} + +TEST_F(DWARFASTParserClangTests, TestVTablePtrAuthParsing) { + // Tests parsing dynamic structure types with explicit vtable pointer + // authentication + + // This is Dwarf for the following C++ code: + // ``` + // struct [[clang::ptrauth_vtable_pointer(process_dependent, + // address_discrimination, + // custom_discrimination, 42)]] A { + // virtual void foo() {} + // }; + // A a; + // ``` + + const char *yamldata = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_AARCH64 +DWARF: + debug_str: + - a + - A + - _vptr$A + - foo + - __vtbl_ptr_type + - int + debug_abbrev: + - ID: 0 + Table: + - Code: 0x1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_language + Form: DW_FORM_data2 + - Code: 0x2 + Tag: DW_TAG_variable + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_external + Form: DW_FORM_flag_present + - Code: 0x3 + Tag: DW_TAG_structure_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_containing_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Code: 0x4 + Tag: DW_TAG_member + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_artificial + Form: DW_FORM_flag_present + - Code: 0x5 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_virtuality + Form: DW_FORM_data1 + - Attribute: DW_AT_containing_type + Form: DW_FORM_ref4 + - Code: 0x6 + Tag: DW_TAG_formal_parameter + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_artificial + Form: DW_FORM_flag_present + - Code: 0x7 + Tag: DW_TAG_LLVM_ptrauth_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_LLVM_ptrauth_key + Form: DW_FORM_data1 + - Attribute: DW_AT_LLVM_ptrauth_extra_discriminator + Form: DW_FORM_data2 + - Attribute: DW_AT_LLVM_ptrauth_address_discriminated + Form: DW_FORM_flag_present + - Code: 0x8 + Tag: DW_TAG_pointer_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Code: 0x9 + Tag: DW_TAG_pointer_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Code: 0xA + Tag: DW_TAG_subroutine_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Code: 0xB + Tag: DW_TAG_base_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_encoding + Form: DW_FORM_data1 + - Attribute: DW_AT_byte_size + Form: DW_FORM_data1 + - Code: 0xC + Tag: DW_TAG_pointer_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + + debug_info: + - Version: 5 + UnitType: DW_UT_compile + AddrSize: 8 + Entries: +# 0x0c: DW_TAG_compile_unit +# DW_AT_language [DW_FORM_data2] (DW_LANG_C_plus_plus_11) + - AbbrCode: 0x1 + Values: + - Value: 0x1A + +# 0x0f: DW_TAG_variable +# DW_AT_name [DW_FORM_strp] (\"a\") +# DW_AT_type [DW_FORM_ref4] (0x00000018 \"A\") +# DW_AT_external [DW_FORM_flag_present] (true) + - AbbrCode: 0x2 + Values: + - Value: 0x00 + - Value: 0x18 + +# 0x18: DW_TAG_structure_type +# DW_AT_containing_type [DW_FORM_ref4] (0x00000018 \"A\") +# DW_AT_name [DW_FORM_strp] (\"A\") + - AbbrCode: 0x3 + Values: + - Value: 0x18 + - Value: 0x02 + +# 0x21: DW_TAG_member +# DW_AT_name [DW_FORM_strp] (\"_vptr$A\") +# DW_AT_type [DW_FORM_ref4] (0x0000002f) +# DW_AT_artificial [DW_FORM_flag_present] (true) + - AbbrCode: 0x4 + Values: + - Value: 0x04 + - Value: 0x3B + +# 0x2a: DW_TAG_subprogram +# DW_AT_name [DW_FORM_strp] (\"foo\") +# DW_AT_virtuality [DW_FORM_data1] (DW_VIRTUALITY_virtual) +# DW_AT_containing_type [DW_FORM_ref4] (0x00000018 \"A\") + - AbbrCode: 0x5 + Values: + - Value: 0x0C + - Value: 0x01 + - Value: 0x18 + +# 0x34: DW_TAG_formal_parameter +# DW_AT_type [DW_FORM_ref4] (0x0000005d \"A *\") +# DW_AT_artificial [DW_FORM_flag_present] (true) + - AbbrCode: 0x6 + Values: + - Value: 0x5D + + - AbbrCode: 0x0 # end of child tags of 0x2a + - AbbrCode: 0x0 # end of child tags of 0x18 + +# 0x3b: DW_TAG_LLVM_ptrauth_type +# DW_AT_type [DW_FORM_ref4] (0x00000043 \"int (**)()\") +# DW_AT_LLVM_ptrauth_key [DW_FORM_data1] (0x02) +# DW_AT_LLVM_ptrauth_extra_discriminator [DW_FORM_data2] (0x002a) +# DW_AT_LLVM_ptrauth_address_discriminated [DW_FORM_flag_present] (true) + - AbbrCode: 0x7 + Values: + - Value: 0x43 + - Value: 0x02 + - Value: 0x2A + +# 0x43: DW_TAG_pointer_type +# DW_AT_type [DW_FORM_ref4] (0x00000048 \"int (*)()\") + - AbbrCode: 0x8 + Values: + - Value: 0x48 + +# 0x48: DW_TAG_pointer_type +# DW_AT_type [DW_FORM_ref4] (0x00000051 \"int ()\") +# DW_AT_name [DW_FORM_strp] (\"__vtbl_ptr_type\") + - AbbrCode: 0x9 + Values: + - Value: 0x51 + - Value: 0x10 + +# 0x51: DW_TAG_subroutine_type +# DW_AT_type [DW_FORM_ref4] (0x00000056 \"int\") + - AbbrCode: 0xA + Values: + - Value: 0x56 + +# 0x56: DW_TAG_base_type +# DW_AT_name [DW_FORM_strp] (\"int\") +# DW_AT_encoding [DW_FORM_data1] (DW_ATE_signed) +# DW_AT_byte_size [DW_FORM_data1] (0x04) + - AbbrCode: 0xB + Values: + - Value: 0x20 + - Value: 0x05 + - Value: 0x04 + +# 0x5d: DW_TAG_pointer_type +# DW_AT_type [DW_FORM_ref4] (0x00000018 \"A\") + - AbbrCode: 0xC + Values: + - Value: 0x18 + + - AbbrCode: 0x0 # end of child tags of 0x0c +... +)"; + YAMLModuleTester t(yamldata); + + DWARFUnit *unit = t.GetDwarfUnit(); + ASSERT_NE(unit, nullptr); + const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE(); + ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit); + DWARFDIE cu_die(unit, cu_entry); + + auto holder = std::make_unique("ast"); + auto &ast_ctx = *holder->GetAST(); + DWARFASTParserClangStub ast_parser(ast_ctx); + + DWARFDIE struct_object = cu_die.GetFirstChild(); + ASSERT_EQ(struct_object.Tag(), DW_TAG_variable); + DWARFDIE structure_type = + struct_object.GetAttributeValueAsReferenceDIE(DW_AT_type); + ASSERT_EQ(structure_type.Tag(), DW_TAG_structure_type); + + SymbolContext sc; + bool new_type = false; + lldb::TypeSP type = + ast_parser.ParseTypeFromDWARF(sc, structure_type, &new_type); + clang::RecordDecl *record_decl = + TypeSystemClang::GetAsRecordDecl(type->GetForwardCompilerType()); + auto *attr = record_decl->getAttr(); + ASSERT_NE(attr, nullptr); + ASSERT_EQ(attr->getKey(), + clang::VTablePointerAuthenticationAttr::ProcessDependent); + ASSERT_EQ(attr->getAddressDiscrimination(), + clang::VTablePointerAuthenticationAttr::AddressDiscrimination); + ASSERT_EQ(attr->getExtraDiscrimination(), + clang::VTablePointerAuthenticationAttr::CustomDiscrimination); + ASSERT_EQ(attr->getCustomDiscriminationValue(), 42); +} + +TEST_F(DWARFASTParserClangTests, TestPtrAuthStructAttr) { + // Tests parsing types with ptrauth_struct attribute + // authentication + + // This is Dwarf for the following C code: + // ``` + // struct [[clang::ptrauth_struct(2, 42)]] A {}; + // struct A a; + // ``` + + const char *yamldata = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_AARCH64 +DWARF: + debug_str: + - a + - A + - ptrauth_struct_key + - ptrauth_struct_disc + debug_abbrev: + - ID: 0 + Table: + - Code: 0x1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_language + Form: DW_FORM_data2 + - Code: 0x2 + Tag: DW_TAG_variable + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Attribute: DW_AT_external + Form: DW_FORM_flag_present + - Code: 0x3 + Tag: DW_TAG_structure_type + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Code: 0x4 + Tag: DW_TAG_LLVM_annotation + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_const_value + Form: DW_FORM_udata + + debug_info: + - Version: 5 + UnitType: DW_UT_compile + AddrSize: 8 + Entries: +# 0x0c: DW_TAG_compile_unit +# DW_AT_language [DW_FORM_data2] (DW_LANG_C99) + - AbbrCode: 0x1 + Values: + - Value: 0x0c + +# 0x0f: DW_TAG_variable +# DW_AT_name [DW_FORM_strp] (\"a\") +# DW_AT_type [DW_FORM_ref4] (0x00000018 \"A\") +# DW_AT_external [DW_FORM_flag_present] (true) + - AbbrCode: 0x2 + Values: + - Value: 0x00 + - Value: 0x18 + +# 0x18: DW_TAG_structure_type +# DW_AT_name [DW_FORM_strp] (\"A\") + - AbbrCode: 0x3 + Values: + - Value: 0x02 + +# 0x1d: DW_TAG_LLVM_annotation +# DW_AT_name [DW_FORM_strp] (\"ptrauth_struct_key\") +# DW_AT_const_value [DW_FORM_udata] (2) + - AbbrCode: 0x4 + Values: + - Value: 0x04 + - Value: 0x02 + +# 0x23: DW_TAG_LLVM_annotation +# DW_AT_name [DW_FORM_strp] (\"ptrauth_struct_disc\") +# DW_AT_const_value [DW_FORM_udata] (42) + - AbbrCode: 0x4 + Values: + - Value: 0x17 + - Value: 0x2a + + - AbbrCode: 0x0 # end of child tags of 0x18 + - AbbrCode: 0x0 # end of child tags of 0x0c +... +)"; + YAMLModuleTester t(yamldata); + + DWARFUnit *unit = t.GetDwarfUnit(); + ASSERT_NE(unit, nullptr); + const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE(); + ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit); + DWARFDIE cu_die(unit, cu_entry); + + auto holder = std::make_unique("ast"); + auto &ast_ctx = *holder->GetAST(); + DWARFASTParserClangStub ast_parser(ast_ctx); + + DWARFDIE struct_object = cu_die.GetFirstChild(); + ASSERT_EQ(struct_object.Tag(), DW_TAG_variable); + DWARFDIE structure_type = + struct_object.GetAttributeValueAsReferenceDIE(DW_AT_type); + ASSERT_EQ(structure_type.Tag(), DW_TAG_structure_type); + + SymbolContext sc; + bool new_type = false; + lldb::TypeSP type = + ast_parser.ParseTypeFromDWARF(sc, structure_type, &new_type); + clang::RecordDecl *record_decl = + TypeSystemClang::GetAsRecordDecl(type->GetForwardCompilerType()); + auto [is_key_val_independent, key] = + ast_ctx.getASTContext().getPointerAuthStructKey(record_decl); + auto [is_disc_val_independent, disc] = + ast_ctx.getASTContext().getPointerAuthStructDisc(record_decl); + ASSERT_TRUE(is_key_val_independent); + ASSERT_TRUE(is_disc_val_independent); + ASSERT_EQ(key, 2); + ASSERT_EQ(disc, 42); +} + struct ExtractIntFromFormValueTest : public testing::Test { SubsystemRAII subsystems; clang_utils::TypeSystemClangHolder holder; diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h index 326c737afa1f..2cbde7afa7cc 100644 --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -1673,6 +1673,7 @@ enum : unsigned { GNU_PROPERTY_STACK_SIZE = 1, GNU_PROPERTY_NO_COPY_ON_PROTECTED = 2, GNU_PROPERTY_AARCH64_FEATURE_1_AND = 0xc0000000, + GNU_PROPERTY_AARCH64_FEATURE_PAUTH = 0xc0000001, GNU_PROPERTY_X86_FEATURE_1_AND = 0xc0000002, GNU_PROPERTY_X86_UINT32_OR_LO = 0xc0008000, @@ -1690,6 +1691,13 @@ enum : unsigned { GNU_PROPERTY_AARCH64_FEATURE_1_PAC = 1 << 1, }; +// aarch64 PAuth platforms +enum : unsigned { + GNU_PROPERTY_AARCH64_FEATURE_PAUTH_PLATFORM_INVALID = 0, + GNU_PROPERTY_AARCH64_FEATURE_PAUTH_PLATFORM_BAREMETAL = 1, + GNU_PROPERTY_AARCH64_FEATURE_PAUTH_PLATFORM_LINUX = 2, +}; + // x86 processor feature bits. enum : unsigned { GNU_PROPERTY_X86_FEATURE_1_IBT = 1 << 0, diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp index 97eda02a730b..f42cf9cf8727 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp @@ -805,7 +805,7 @@ void DwarfUnit::constructTypeDIE(DIE &Buffer, const DIDerivedType *DTy) { if (auto Key = DTy->getPtrAuthKey()) addUInt(Buffer, dwarf::DW_AT_LLVM_ptrauth_key, dwarf::DW_FORM_data1, *Key); if (auto AddrDisc = DTy->isPtrAuthAddressDiscriminated()) - if (AddrDisc) + if (AddrDisc.value()) addFlag(Buffer, dwarf::DW_AT_LLVM_ptrauth_address_discriminated); if (auto Disc = DTy->getPtrAuthExtraDiscriminator()) addUInt(Buffer, dwarf::DW_AT_LLVM_ptrauth_extra_discriminator, diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp index 152105c20b11..1095a1f5787a 100644 --- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -265,6 +265,8 @@ void AArch64AsmPrinter::emitStartOfAsmFile(Module &M) { S, MCConstantExpr::create(Feat00Value, MMI->getContext())); } + // TODO: enhance naming to distinguish MachO ptrauth ABI version and ELF + // pauthabi platform and version if (TM.getTargetTriple().isOSBinFormatMachO()) EmitPtrAuthVersion(M); @@ -283,13 +285,24 @@ void AArch64AsmPrinter::emitStartOfAsmFile(Module &M) { if (Sign->getZExtValue()) Flags |= ELF::GNU_PROPERTY_AARCH64_FEATURE_1_PAC; - if (Flags == 0) - return; + uint64_t PAuthABIPlatform = -1; + if (const auto *PAP = mdconst::extract_or_null( + M.getModuleFlag("aarch64-elf-pauthabi-platform"))) + PAuthABIPlatform = PAP->getZExtValue(); + uint64_t PAuthABIVersion = -1; + if (const auto *PAV = mdconst::extract_or_null( + M.getModuleFlag("aarch64-elf-pauthabi-version"))) + PAuthABIVersion = PAV->getZExtValue(); + + if ((PAuthABIPlatform == uint64_t(-1)) != (PAuthABIVersion == uint64_t(-1))) + report_fatal_error( + "either both or no 'aarch64-elf-pauthabi-platform' and " + "'aarch64-elf-pauthabi-version' module flags must be present"); // Emit a .note.gnu.property section with the flags. auto *TS = static_cast(OutStreamer->getTargetStreamer()); - TS->emitNoteSection(Flags); + TS->emitNoteSection(Flags, PAuthABIPlatform, PAuthABIVersion); } void AArch64AsmPrinter::emitFunctionHeaderComment() { diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.cpp index e1d6dd7a056b..0b13f5868e60 100644 --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.cpp +++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.cpp @@ -56,10 +56,20 @@ void AArch64TargetStreamer::emitConstantPools() { void AArch64TargetStreamer::finish() { if (MarkBTIProperty) emitNoteSection(ELF::GNU_PROPERTY_AARCH64_FEATURE_1_BTI); + // TODO: do we need to handle GNU_PROPERTY_AARCH64_FEATURE_PAUTH here? } -void AArch64TargetStreamer::emitNoteSection(unsigned Flags) { - if (Flags == 0) +void AArch64TargetStreamer::emitNoteSection(unsigned Flags, + uint64_t PAuthABIPlatform, + uint64_t PAuthABIVersion) { + assert((PAuthABIPlatform == uint64_t(-1)) == + (PAuthABIVersion == uint64_t(-1))); + uint64_t DescSz = 0; + if (Flags != 0) + DescSz += 4 * 4; + if (PAuthABIPlatform != uint64_t(-1)) + DescSz += 4 + 4 + 8 * 2; + if (DescSz == 0) return; MCStreamer &OutStreamer = getStreamer(); @@ -80,15 +90,25 @@ void AArch64TargetStreamer::emitNoteSection(unsigned Flags) { // Emit the note header. OutStreamer.emitValueToAlignment(Align(8)); OutStreamer.emitIntValue(4, 4); // data size for "GNU\0" - OutStreamer.emitIntValue(4 * 4, 4); // Elf_Prop size + OutStreamer.emitIntValue(DescSz, 4); // Elf_Prop array size OutStreamer.emitIntValue(ELF::NT_GNU_PROPERTY_TYPE_0, 4); OutStreamer.emitBytes(StringRef("GNU", 4)); // note name // Emit the PAC/BTI properties. - OutStreamer.emitIntValue(ELF::GNU_PROPERTY_AARCH64_FEATURE_1_AND, 4); - OutStreamer.emitIntValue(4, 4); // data size - OutStreamer.emitIntValue(Flags, 4); // data - OutStreamer.emitIntValue(0, 4); // pad + if (Flags != 0) { + OutStreamer.emitIntValue(ELF::GNU_PROPERTY_AARCH64_FEATURE_1_AND, 4); + OutStreamer.emitIntValue(4, 4); // data size + OutStreamer.emitIntValue(Flags, 4); // data + OutStreamer.emitIntValue(0, 4); // pad + } + + // Emit the PAuth ABI compatibility info + if (PAuthABIPlatform != uint64_t(-1)) { + OutStreamer.emitIntValue(ELF::GNU_PROPERTY_AARCH64_FEATURE_PAUTH, 4); + OutStreamer.emitIntValue(8 * 2, 4); // data size + OutStreamer.emitIntValue(PAuthABIPlatform, 8); + OutStreamer.emitIntValue(PAuthABIVersion, 8); + } OutStreamer.endSection(Nt); OutStreamer.switchSection(Cur); diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h index b3bce9960772..716ca1b3286c 100644 --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h +++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h @@ -35,7 +35,8 @@ class AArch64TargetStreamer : public MCTargetStreamer { void emitCurrentConstantPool(); /// Callback used to implement the .note.gnu.property section. - void emitNoteSection(unsigned Flags); + void emitNoteSection(unsigned Flags, uint64_t PAuthABIPlatform = -1, + uint64_t PAuthABIVersion = -1); /// Callback used to implement the .inst directive. virtual void emitInst(uint32_t Inst); diff --git a/llvm/test/CodeGen/AArch64/note-gnu-property-elf-pauthabi.ll b/llvm/test/CodeGen/AArch64/note-gnu-property-elf-pauthabi.ll new file mode 100644 index 000000000000..46d88c69690d --- /dev/null +++ b/llvm/test/CodeGen/AArch64/note-gnu-property-elf-pauthabi.ll @@ -0,0 +1,50 @@ +; RUN: rm -rf %t && split-file %s %t && cd %t + +;--- ok.ll + +; RUN: llc -mtriple=aarch64-linux ok.ll -o - | \ +; RUN: FileCheck %s --check-prefix=ASM +; RUN: llc -mtriple=aarch64-linux ok.ll -filetype=obj -o - | \ +; RUN: llvm-readelf --notes - | FileCheck %s --check-prefix=OBJ + +!llvm.module.flags = !{!0, !1} + +!0 = !{i32 1, !"aarch64-elf-pauthabi-platform", i32 2} +!1 = !{i32 1, !"aarch64-elf-pauthabi-version", i32 31} + +; ASM: .section .note.gnu.property,"a",@note +; ASM-NEXT: .p2align 3, 0x0 +; ASM-NEXT: .word 4 +; ASM-NEXT: .word 24 +; ASM-NEXT: .word 5 +; ASM-NEXT: .asciz "GNU" +; 3221225473 = 0xc0000001 = GNU_PROPERTY_AARCH64_FEATURE_PAUTH +; ASM-NEXT: .word 3221225473 +; ASM-NEXT: .word 16 +; ASM-NEXT: .xword 2 +; ASM-NEXT: .xword 31 + +; OBJ: Displaying notes found in: .note.gnu.property +; OBJ-NEXT: Owner Data size Description +; OBJ-NEXT: GNU 0x00000018 NT_GNU_PROPERTY_TYPE_0 (property note) +; OBJ-NEXT: AArch64 PAuth ABI tag: platform 0x2 (linux), version 0x1f (PointerAuthCalls, PointerAuthReturns, PointerAuthVTPtrAddressDiscrimination, PointerAuthVTPtrTypeDiscrimination, PointerAuthInitFini) + +; ERR: either both or no 'aarch64-elf-pauthabi-platform' and 'aarch64-elf-pauthabi-version' module flags must be present + +;--- err1.ll + +; RUN: not --crash llc -mtriple=aarch64-linux err1.ll 2>&1 -o - | \ +; RUN: FileCheck %s --check-prefix=ERR + +!llvm.module.flags = !{!0} + +!0 = !{i32 1, !"aarch64-elf-pauthabi-platform", i32 2} + +;--- err2.ll + +; RUN: not --crash llc -mtriple=aarch64-linux err2.ll 2>&1 -o - | \ +; RUN: FileCheck %s --check-prefix=ERR + +!llvm.module.flags = !{!0} + +!0 = !{i32 1, !"aarch64-elf-pauthabi-version", i32 31} diff --git a/llvm/test/DebugInfo/AArch64/ptrauth.ll b/llvm/test/DebugInfo/AArch64/ptrauth.ll index 067063c4d1d5..4f84fe4f9629 100644 --- a/llvm/test/DebugInfo/AArch64/ptrauth.ll +++ b/llvm/test/DebugInfo/AArch64/ptrauth.ll @@ -1,35 +1,41 @@ ; RUN: llc %s -filetype=obj -mtriple arm64e-apple-darwin -o - \ ; RUN: | llvm-dwarfdump - | FileCheck %s -; CHECK: DW_AT_type (0x{{0+}}[[TY:.*]] "void *__ptrauth(4, 1, 0x04d2)") +; CHECK: DW_AT_type (0x{{0+}}[[TY:.*]] "void *__ptrauth(4, 0, 0x04d2)") ; CHECK: 0x{{0+}}[[TY]]: DW_TAG_LLVM_ptrauth_type ; CHECK-NEXT: DW_AT_type {{.*}}"void *" ; CHECK-NEXT: DW_AT_LLVM_ptrauth_key (0x04) -; CHECK-NEXT: DW_AT_LLVM_ptrauth_address_discriminated (true) ; CHECK-NEXT: DW_AT_LLVM_ptrauth_extra_discriminator (0x04d2) -; CHECK: DW_AT_type (0x{{0+}}[[TY:.*]] "void *__ptrauth(4, 1, 0x04d3, "isa-pointer")") +; CHECK: DW_AT_type (0x{{0+}}[[TY:.*]] "void *__ptrauth(4, 1, 0x04d3)") ; CHECK: 0x{{0+}}[[TY]]: DW_TAG_LLVM_ptrauth_type ; CHECK-NEXT: DW_AT_type {{.*}}"void *" ; CHECK-NEXT: DW_AT_LLVM_ptrauth_key (0x04) ; CHECK-NEXT: DW_AT_LLVM_ptrauth_address_discriminated (true) ; CHECK-NEXT: DW_AT_LLVM_ptrauth_extra_discriminator (0x04d3) -; CHECK-NEXT: DW_AT_LLVM_ptrauth_isa_pointer (true) -; CHECK: DW_AT_type (0x{{0+}}[[TY:.*]] "void *__ptrauth(4, 1, 0x04d4, "authenticates-null-values")") +; CHECK: DW_AT_type (0x{{0+}}[[TY:.*]] "void *__ptrauth(4, 1, 0x04d4, "isa-pointer")") ; CHECK: 0x{{0+}}[[TY]]: DW_TAG_LLVM_ptrauth_type ; CHECK-NEXT: DW_AT_type {{.*}}"void *" ; CHECK-NEXT: DW_AT_LLVM_ptrauth_key (0x04) ; CHECK-NEXT: DW_AT_LLVM_ptrauth_address_discriminated (true) ; CHECK-NEXT: DW_AT_LLVM_ptrauth_extra_discriminator (0x04d4) -; CHECK-NEXT: DW_AT_LLVM_ptrauth_authenticates_null_values (true) +; CHECK-NEXT: DW_AT_LLVM_ptrauth_isa_pointer (true) -; CHECK: DW_AT_type (0x{{0+}}[[TY:.*]] "void *__ptrauth(4, 1, 0x04d5, "isa-pointer,authenticates-null-values")") +; CHECK: DW_AT_type (0x{{0+}}[[TY:.*]] "void *__ptrauth(4, 1, 0x04d5, "authenticates-null-values")") ; CHECK: 0x{{0+}}[[TY]]: DW_TAG_LLVM_ptrauth_type ; CHECK-NEXT: DW_AT_type {{.*}}"void *" ; CHECK-NEXT: DW_AT_LLVM_ptrauth_key (0x04) ; CHECK-NEXT: DW_AT_LLVM_ptrauth_address_discriminated (true) ; CHECK-NEXT: DW_AT_LLVM_ptrauth_extra_discriminator (0x04d5) +; CHECK-NEXT: DW_AT_LLVM_ptrauth_authenticates_null_values (true) + +; CHECK: DW_AT_type (0x{{0+}}[[TY:.*]] "void *__ptrauth(4, 1, 0x04d6, "isa-pointer,authenticates-null-values")") +; CHECK: 0x{{0+}}[[TY]]: DW_TAG_LLVM_ptrauth_type +; CHECK-NEXT: DW_AT_type {{.*}}"void *" +; CHECK-NEXT: DW_AT_LLVM_ptrauth_key (0x04) +; CHECK-NEXT: DW_AT_LLVM_ptrauth_address_discriminated (true) +; CHECK-NEXT: DW_AT_LLVM_ptrauth_extra_discriminator (0x04d6) ; CHECK-NEXT: DW_AT_LLVM_ptrauth_isa_pointer (true) ; CHECK-NEXT: DW_AT_LLVM_ptrauth_authenticates_null_values (true) @@ -37,25 +43,28 @@ target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" @p = common global i8* null, align 8, !dbg !0 -!llvm.dbg.cu = !{!8} -!llvm.module.flags = !{!16, !17} - -!0 = !DIGlobalVariableExpression(var: !4, expr: !DIExpression()) -!1 = !DIGlobalVariableExpression(var: !5, expr: !DIExpression()) -!2 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression()) -!3 = !DIGlobalVariableExpression(var: !7, expr: !DIExpression()) -!4 = distinct !DIGlobalVariable(name: "p1", scope: !8, file: !9, line: 1, type: !12, isLocal: false, isDefinition: true) -!5 = distinct !DIGlobalVariable(name: "p2", scope: !8, file: !9, line: 1, type: !13, isLocal: false, isDefinition: true) -!6 = distinct !DIGlobalVariable(name: "p3", scope: !8, file: !9, line: 1, type: !14, isLocal: false, isDefinition: true) -!7 = distinct !DIGlobalVariable(name: "p4", scope: !8, file: !9, line: 1, type: !15, isLocal: false, isDefinition: true) -!8 = distinct !DICompileUnit(language: DW_LANG_C99, file: !9, emissionKind: FullDebug, globals: !11) -!9 = !DIFile(filename: "/tmp/p.c", directory: "/") -!10 = !{} -!11 = !{!0,!1,!2,!3} -!12 = !DIDerivedType(tag: DW_TAG_LLVM_ptrauth_type, baseType: !18, ptrAuthKey: 4, ptrAuthIsAddressDiscriminated: true, ptrAuthExtraDiscriminator: 1234) -!13 = !DIDerivedType(tag: DW_TAG_LLVM_ptrauth_type, baseType: !18, ptrAuthKey: 4, ptrAuthIsAddressDiscriminated: true, ptrAuthExtraDiscriminator: 1235, ptrAuthIsaPointer: true) -!14 = !DIDerivedType(tag: DW_TAG_LLVM_ptrauth_type, baseType: !18, ptrAuthKey: 4, ptrAuthIsAddressDiscriminated: true, ptrAuthExtraDiscriminator: 1236, ptrAuthAuthenticatesNullValues: true) -!15 = !DIDerivedType(tag: DW_TAG_LLVM_ptrauth_type, baseType: !18, ptrAuthKey: 4, ptrAuthIsAddressDiscriminated: true, ptrAuthExtraDiscriminator: 1237, ptrAuthIsaPointer: true, ptrAuthAuthenticatesNullValues: true) -!16 = !{i32 2, !"Dwarf Version", i32 4} -!17 = !{i32 2, !"Debug Info Version", i32 3} -!18 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null) +!llvm.dbg.cu = !{!10} +!llvm.module.flags = !{!19, !20} + +!0 = !DIGlobalVariableExpression(var: !5, expr: !DIExpression()) +!1 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression()) +!2 = !DIGlobalVariableExpression(var: !7, expr: !DIExpression()) +!3 = !DIGlobalVariableExpression(var: !8, expr: !DIExpression()) +!4 = !DIGlobalVariableExpression(var: !9, expr: !DIExpression()) +!5 = distinct !DIGlobalVariable(name: "p1", scope: !10, file: !11, line: 1, type: !14, isLocal: false, isDefinition: true) +!6 = distinct !DIGlobalVariable(name: "p2", scope: !10, file: !11, line: 1, type: !15, isLocal: false, isDefinition: true) +!7 = distinct !DIGlobalVariable(name: "p3", scope: !10, file: !11, line: 1, type: !16, isLocal: false, isDefinition: true) +!8 = distinct !DIGlobalVariable(name: "p4", scope: !10, file: !11, line: 1, type: !17, isLocal: false, isDefinition: true) +!9 = distinct !DIGlobalVariable(name: "p5", scope: !10, file: !11, line: 1, type: !18, isLocal: false, isDefinition: true) +!10 = distinct !DICompileUnit(language: DW_LANG_C99, file: !11, emissionKind: FullDebug, globals: !13) +!11 = !DIFile(filename: "/tmp/p.c", directory: "/") +!12 = !{} +!13 = !{!0,!1,!2,!3,!4} +!14 = !DIDerivedType(tag: DW_TAG_LLVM_ptrauth_type, baseType: !21, ptrAuthKey: 4, ptrAuthIsAddressDiscriminated: false, ptrAuthExtraDiscriminator: 1234) +!15 = !DIDerivedType(tag: DW_TAG_LLVM_ptrauth_type, baseType: !21, ptrAuthKey: 4, ptrAuthIsAddressDiscriminated: true, ptrAuthExtraDiscriminator: 1235) +!16 = !DIDerivedType(tag: DW_TAG_LLVM_ptrauth_type, baseType: !21, ptrAuthKey: 4, ptrAuthIsAddressDiscriminated: true, ptrAuthExtraDiscriminator: 1236, ptrAuthIsaPointer: true) +!17 = !DIDerivedType(tag: DW_TAG_LLVM_ptrauth_type, baseType: !21, ptrAuthKey: 4, ptrAuthIsAddressDiscriminated: true, ptrAuthExtraDiscriminator: 1237, ptrAuthAuthenticatesNullValues: true) +!18 = !DIDerivedType(tag: DW_TAG_LLVM_ptrauth_type, baseType: !21, ptrAuthKey: 4, ptrAuthIsAddressDiscriminated: true, ptrAuthExtraDiscriminator: 1238, ptrAuthIsaPointer: true, ptrAuthAuthenticatesNullValues: true) +!19 = !{i32 2, !"Dwarf Version", i32 4} +!20 = !{i32 2, !"Debug Info Version", i32 3} +!21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null) diff --git a/llvm/test/tools/llvm-readobj/ELF/AArch64/aarch64-feature-pauth.s b/llvm/test/tools/llvm-readobj/ELF/AArch64/aarch64-feature-pauth.s index 1bbc6729bc70..cc3affaea9f2 100644 --- a/llvm/test/tools/llvm-readobj/ELF/AArch64/aarch64-feature-pauth.s +++ b/llvm/test/tools/llvm-readobj/ELF/AArch64/aarch64-feature-pauth.s @@ -1,69 +1,8 @@ +# TODO: it looks like that tests might be shortened using some kind of templates + # RUN: rm -rf %t && split-file %s %t && cd %t -# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu abi-tag.s -o tag.o -# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu abi-tag-short.s -o tag-short.o -# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu abi-tag-long.s -o tag-long.o - -# RUN: llvm-readelf --notes tag.o | FileCheck --check-prefix NORMAL %s -# RUN: llvm-readelf --notes tag-short.o | FileCheck --check-prefix SHORT %s -# RUN: llvm-readelf --notes tag-long.o | FileCheck --check-prefix LONG %s - -# NORMAL: AArch64 PAuth ABI tag: platform 0x2a, version 0x1 -# SHORT: AArch64 PAuth ABI tag: -# LONG: AArch64 PAuth ABI tag: platform 0x2a, version 0x1, additional info 0xEFCDAB8967452301 - -# RUN: llvm-readobj --notes tag.o | FileCheck --check-prefix LLVM-NORMAL %s -# RUN: llvm-readobj --notes tag-short.o | FileCheck --check-prefix LLVM-SHORT %s -# RUN: llvm-readobj --notes tag-long.o | FileCheck --check-prefix LLVM-LONG %s - -// LLVM-SHORT: Notes [ -// LLVM-SHORT-NEXT: NoteSection { -// LLVM-SHORT-NEXT: Name: .note.AARCH64-PAUTH-ABI-tag -// LLVM-SHORT-NEXT: Offset: 0x40 -// LLVM-SHORT-NEXT: Size: 0x1C -// LLVM-SHORT-NEXT: Note { -// LLVM-SHORT-NEXT: Owner: ARM -// LLVM-SHORT-NEXT: Data size: 0xC -// LLVM-SHORT-NEXT: Type: NT_ARM_TYPE_PAUTH_ABI_TAG -// LLVM-SHORT-NEXT: Description data ( -// LLVM-SHORT-NEXT: 0000: 2A000000 00000000 01000000 -// LLVM-SHORT-NEXT: ) -// LLVM-SHORT-NEXT: } -// LLVM-SHORT-NEXT: } -// LLVM-SHORT-NEXT: ] - -// LLVM-NORMAL: Notes [ -// LLVM-NORMAL-NEXT: NoteSection { -// LLVM-NORMAL-NEXT: Name: .note.AARCH64-PAUTH-ABI-tag -// LLVM-NORMAL-NEXT: Offset: 0x40 -// LLVM-NORMAL-NEXT: Size: 0x20 -// LLVM-NORMAL-NEXT: Note { -// LLVM-NORMAL-NEXT: Owner: ARM -// LLVM-NORMAL-NEXT: Data size: 0x10 -// LLVM-NORMAL-NEXT: Type: NT_ARM_TYPE_PAUTH_ABI_TAG -// LLVM-NORMAL-NEXT: Platform: 42 -// LLVM-NORMAL-NEXT: Version: 1 -// LLVM-NORMAL-NEXT: } -// LLVM-NORMAL-NEXT: } -// LLVM-NORMAL-NEXT: ] - -// LLVM-LONG: Notes [ -// LLVM-LONG-NEXT: NoteSection { -// LLVM-LONG-NEXT: Name: .note.AARCH64-PAUTH-ABI-tag -// LLVM-LONG-NEXT: Offset: 0x40 -// LLVM-LONG-NEXT: Size: 0x28 -// LLVM-LONG-NEXT: Note { -// LLVM-LONG-NEXT: Owner: ARM -// LLVM-LONG-NEXT: Data size: 0x18 -// LLVM-LONG-NEXT: Type: NT_ARM_TYPE_PAUTH_ABI_TAG -// LLVM-LONG-NEXT: Platform: 42 -// LLVM-LONG-NEXT: Version: 1 -// LLVM-LONG-NEXT: Additional info: EFCDAB8967452301 -// LLVM-LONG-NEXT: } -// LLVM-LONG-NEXT: } -// LLVM-LONG-NEXT: ] - -#--- abi-tag.s +#--- tag-42-1.s .section ".note.AARCH64-PAUTH-ABI-tag", "a" .long 4 @@ -74,7 +13,82 @@ .quad 42 // platform .quad 1 // version -#--- abi-tag-short.s +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu tag-42-1.s -o tag-42-1.o +# RUN: llvm-readelf --notes tag-42-1.o | \ +# RUN: FileCheck --check-prefix ELF-TAG -DPLATFORM=0x2a -DVERSION=0x1 %s +# RUN: llvm-readobj --notes tag-42-1.o | \ +# RUN: FileCheck --check-prefix OBJ-TAG -DPLATFORM=42 -DVERSION=1 %s + +# ELF-TAG: AArch64 PAuth ABI tag: platform [[PLATFORM]], version [[VERSION]] + +# OBJ-TAG: Notes [ +# OBJ-TAG-NEXT: NoteSection { +# OBJ-TAG-NEXT: Name: .note.AARCH64-PAUTH-ABI-tag +# OBJ-TAG-NEXT: Offset: 0x40 +# OBJ-TAG-NEXT: Size: 0x20 +# OBJ-TAG-NEXT: Note { +# OBJ-TAG-NEXT: Owner: ARM +# OBJ-TAG-NEXT: Data size: 0x10 +# OBJ-TAG-NEXT: Type: NT_ARM_TYPE_PAUTH_ABI_TAG +# OBJ-TAG-NEXT: Platform: [[PLATFORM]] +# OBJ-TAG-NEXT: Version: [[VERSION]] +# OBJ-TAG-NEXT: } +# OBJ-TAG-NEXT: } +# OBJ-TAG-NEXT: ] + +#--- tag-0-0.s + +.section ".note.AARCH64-PAUTH-ABI-tag", "a" +.long 4 +.long 16 +.long 1 +.asciz "ARM" + +.quad 0 // platform +.quad 0 // version + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu tag-0-0.s -o tag-0-0.o +# RUN: llvm-readelf --notes tag-0-0.o | \ +# RUN: FileCheck --check-prefix ELF-TAG -DPLATFORM="0x0 (invalid)" -DVERSION=0x0 %s +# RUN: llvm-readobj --notes tag-0-0.o | \ +# RUN: FileCheck --check-prefix OBJ-TAG -DPLATFORM=0 -DVERSION=0 %s + +#--- tag-1-0.s + +.section ".note.AARCH64-PAUTH-ABI-tag", "a" +.long 4 +.long 16 +.long 1 +.asciz "ARM" + +.quad 1 // platform +.quad 0 // version + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu tag-1-0.s -o tag-1-0.o +# RUN: llvm-readelf --notes tag-1-0.o | \ +# RUN: FileCheck --check-prefix ELF-TAG -DPLATFORM="0x1 (baremetal)" -DVERSION=0x0 %s +# RUN: llvm-readobj --notes tag-1-0.o | \ +# RUN: FileCheck --check-prefix OBJ-TAG -DPLATFORM=1 -DVERSION=0 %s + +#--- tag-2-21.s + +.section ".note.AARCH64-PAUTH-ABI-tag", "a" +.long 4 +.long 16 +.long 1 +.asciz "ARM" + +.quad 2 // platform +.quad 21 // version + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu tag-2-21.s -o tag-2-21.o +# RUN: llvm-readelf --notes tag-2-21.o | \ +# RUN: FileCheck --check-prefix ELF-TAG -DPLATFORM="0x2 (linux)" \ +# RUN: -DVERSION="0x15 (PointerAuthCalls, !PointerAuthReturns, PointerAuthVTPtrAddressDiscrimination, !PointerAuthVTPtrTypeDiscrimination, PointerAuthInitFini)" %s +# RUN: llvm-readobj --notes tag-2-21.o | \ +# RUN: FileCheck --check-prefix OBJ-TAG -DPLATFORM=2 -DVERSION=21 %s + +#--- tag-short.s .section ".note.AARCH64-PAUTH-ABI-tag", "a" .long 4 @@ -85,7 +99,29 @@ .quad 42 .word 1 -#--- abi-tag-long.s +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu tag-short.s -o tag-short.o +# RUN: llvm-readelf --notes tag-short.o | FileCheck --check-prefix ELF-TAG-SHORT %s +# RUN: llvm-readobj --notes tag-short.o | FileCheck --check-prefix OBJ-TAG-SHORT %s + +# ELF-TAG-SHORT: AArch64 PAuth ABI tag: + +# OBJ-TAG-SHORT: Notes [ +# OBJ-TAG-SHORT-NEXT: NoteSection { +# OBJ-TAG-SHORT-NEXT: Name: .note.AARCH64-PAUTH-ABI-tag +# OBJ-TAG-SHORT-NEXT: Offset: 0x40 +# OBJ-TAG-SHORT-NEXT: Size: 0x1C +# OBJ-TAG-SHORT-NEXT: Note { +# OBJ-TAG-SHORT-NEXT: Owner: ARM +# OBJ-TAG-SHORT-NEXT: Data size: 0xC +# OBJ-TAG-SHORT-NEXT: Type: NT_ARM_TYPE_PAUTH_ABI_TAG +# OBJ-TAG-SHORT-NEXT: Description data ( +# OBJ-TAG-SHORT-NEXT: 0000: 2A000000 00000000 01000000 +# OBJ-TAG-SHORT-NEXT: ) +# OBJ-TAG-SHORT-NEXT: } +# OBJ-TAG-SHORT-NEXT: } +# OBJ-TAG-SHORT-NEXT: ] + +#--- tag-long.s .section ".note.AARCH64-PAUTH-ABI-tag", "a" .long 4 @@ -96,3 +132,213 @@ .quad 42 // platform .quad 1 // version .quad 0x0123456789ABCDEF // extra data + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu tag-long.s -o tag-long.o +# RUN: llvm-readelf --notes tag-long.o | FileCheck --check-prefix ELF-TAG-LONG %s +# RUN: llvm-readobj --notes tag-long.o | FileCheck --check-prefix OBJ-TAG-LONG %s + +# ELF-TAG-LONG: AArch64 PAuth ABI tag: + +# OBJ-TAG-LONG: Notes [ +# OBJ-TAG-LONG-NEXT: NoteSection { +# OBJ-TAG-LONG-NEXT: Name: .note.AARCH64-PAUTH-ABI-tag +# OBJ-TAG-LONG-NEXT: Offset: 0x40 +# OBJ-TAG-LONG-NEXT: Size: 0x28 +# OBJ-TAG-LONG-NEXT: Note { +# OBJ-TAG-LONG-NEXT: Owner: ARM +# OBJ-TAG-LONG-NEXT: Data size: 0x18 +# OBJ-TAG-LONG-NEXT: Type: NT_ARM_TYPE_PAUTH_ABI_TAG +# OBJ-TAG-LONG-NEXT: Description data ( +# OBJ-TAG-LONG-NEXT: 0000: 2A000000 00000000 01000000 00000000 +# OBJ-TAG-LONG-NEXT: 0010: EFCDAB89 67452301 +# OBJ-TAG-LONG-NEXT: ) +# OBJ-TAG-LONG-NEXT: } +# OBJ-TAG-LONG-NEXT: } +# OBJ-TAG-LONG-NEXT: ] + +#--- gnu-42-1.s + +.section ".note.gnu.property", "a" + .long 4 /* Name length is always 4 ("GNU") */ + .long end - begin /* Data length */ + .long 5 /* Type: NT_GNU_PROPERTY_TYPE_0 */ + .asciz "GNU" /* Name */ + .p2align 3 +begin: + /* PAuth ABI property note */ + .long 0xc0000001 /* Type: GNU_PROPERTY_AARCH64_FEATURE_PAUTH */ + .long 16 /* Data size */ + .quad 42 /* PAuth ABI platform */ + .quad 1 /* PAuth ABI version */ + .p2align 3 /* Align to 8 byte for 64 bit */ +end: + +# RUN: llvm-mc -filetype=obj -triple aarch64-linux-gnu gnu-42-1.s -o gnu-42-1.o +# RUN: llvm-readelf --notes gnu-42-1.o | \ +# RUN: FileCheck --check-prefix=ELF-GNU -DPLATFORM=0x2a -DVERSION=0x1 %s +# RUN: llvm-readobj --notes gnu-42-1.o | \ +# RUN: FileCheck --check-prefix=OBJ-GNU -DPLATFORM=0x2a -DVERSION=0x1 %s + +# ELF-GNU: Displaying notes found in: .note.gnu.property +# ELF-GNU-NEXT: Owner Data size Description +# ELF-GNU-NEXT: GNU 0x00000018 NT_GNU_PROPERTY_TYPE_0 (property note) +# ELF-GNU-NEXT: AArch64 PAuth ABI tag: platform [[PLATFORM]], version [[VERSION]] + +# OBJ-GNU: Notes [ +# OBJ-GNU-NEXT: NoteSection { +# OBJ-GNU-NEXT: Name: .note.gnu.property +# OBJ-GNU-NEXT: Offset: 0x40 +# OBJ-GNU-NEXT: Size: 0x28 +# OBJ-GNU-NEXT: Note { +# OBJ-GNU-NEXT: Owner: GNU +# OBJ-GNU-NEXT: Data size: 0x18 +# OBJ-GNU-NEXT: Type: NT_GNU_PROPERTY_TYPE_0 (property note) +# OBJ-GNU-NEXT: Property [ +# OBJ-GNU-NEXT: AArch64 PAuth ABI tag: platform [[PLATFORM]], version [[VERSION]] +# OBJ-GNU-NEXT: ] +# OBJ-GNU-NEXT: } +# OBJ-GNU-NEXT: } +# OBJ-GNU-NEXT: ] + +#--- gnu-0-0.s + +.section ".note.gnu.property", "a" + .long 4 /* Name length is always 4 ("GNU") */ + .long end - begin /* Data length */ + .long 5 /* Type: NT_GNU_PROPERTY_TYPE_0 */ + .asciz "GNU" /* Name */ + .p2align 3 +begin: + /* PAuth ABI property note */ + .long 0xc0000001 /* Type: GNU_PROPERTY_AARCH64_FEATURE_PAUTH */ + .long 16 /* Data size */ + .quad 0 /* PAuth ABI platform */ + .quad 0 /* PAuth ABI version */ + .p2align 3 /* Align to 8 byte for 64 bit */ +end: + +# RUN: llvm-mc -filetype=obj -triple aarch64-linux-gnu gnu-0-0.s -o gnu-0-0.o +# RUN: llvm-readelf --notes gnu-0-0.o | \ +# RUN: FileCheck --check-prefix=ELF-GNU -DPLATFORM="0x0 (invalid)" -DVERSION=0x0 %s +# RUN: llvm-readobj --notes gnu-0-0.o | \ +# RUN: FileCheck --check-prefix=OBJ-GNU -DPLATFORM="0x0 (invalid)" -DVERSION=0x0 %s + +#--- gnu-1-0.s + +.section ".note.gnu.property", "a" + .long 4 /* Name length is always 4 ("GNU") */ + .long end - begin /* Data length */ + .long 5 /* Type: NT_GNU_PROPERTY_TYPE_0 */ + .asciz "GNU" /* Name */ + .p2align 3 +begin: + /* PAuth ABI property note */ + .long 0xc0000001 /* Type: GNU_PROPERTY_AARCH64_FEATURE_PAUTH */ + .long 16 /* Data size */ + .quad 1 /* PAuth ABI platform */ + .quad 0 /* PAuth ABI version */ + .p2align 3 /* Align to 8 byte for 64 bit */ +end: + +# RUN: llvm-mc -filetype=obj -triple aarch64-linux-gnu gnu-1-0.s -o gnu-1-0.o +# RUN: llvm-readelf --notes gnu-1-0.o | \ +# RUN: FileCheck --check-prefix=ELF-GNU -DPLATFORM="0x1 (baremetal)" -DVERSION=0x0 %s +# RUN: llvm-readobj --notes gnu-1-0.o | \ +# RUN: FileCheck --check-prefix=OBJ-GNU -DPLATFORM="0x1 (baremetal)" -DVERSION=0x0 %s + +#--- gnu-2-21.s + +.section ".note.gnu.property", "a" + .long 4 /* Name length is always 4 ("GNU") */ + .long end - begin /* Data length */ + .long 5 /* Type: NT_GNU_PROPERTY_TYPE_0 */ + .asciz "GNU" /* Name */ + .p2align 3 +begin: + /* PAuth ABI property note */ + .long 0xc0000001 /* Type: GNU_PROPERTY_AARCH64_FEATURE_PAUTH */ + .long 16 /* Data size */ + .quad 2 /* PAuth ABI platform */ + .quad 21 /* PAuth ABI version */ + .p2align 3 /* Align to 8 byte for 64 bit */ +end: + +# RUN: llvm-mc -filetype=obj -triple aarch64-linux-gnu gnu-2-21.s -o gnu-2-21.o +# RUN: llvm-readelf --notes gnu-2-21.o | \ +# RUN: FileCheck --check-prefix=ELF-GNU -DPLATFORM="0x2 (linux)" \ +# RUN: -DVERSION="0x15 (PointerAuthCalls, !PointerAuthReturns, PointerAuthVTPtrAddressDiscrimination, !PointerAuthVTPtrTypeDiscrimination, PointerAuthInitFini)" %s +# RUN: llvm-readobj --notes gnu-2-21.o | \ +# RUN: FileCheck --check-prefix=OBJ-GNU -DPLATFORM="0x2 (linux)" \ +# RUN: -DVERSION="0x15 (PointerAuthCalls, !PointerAuthReturns, PointerAuthVTPtrAddressDiscrimination, !PointerAuthVTPtrTypeDiscrimination, PointerAuthInitFini)" %s + +#--- gnu-short.s + +.section ".note.gnu.property", "a" + .long 4 /* Name length is always 4 ("GNU") */ + .long end - begin /* Data length */ + .long 5 /* Type: NT_GNU_PROPERTY_TYPE_0 */ + .asciz "GNU" /* Name */ + .p2align 3 +begin: + /* PAuth ABI property note */ + .long 0xc0000001 /* Type: GNU_PROPERTY_AARCH64_FEATURE_PAUTH */ + .long 12 /* Data size */ + .quad 42 /* PAuth ABI platform */ + .word 1 /* PAuth ABI version */ + .p2align 3 /* Align to 8 byte for 64 bit */ +end: + +# RUN: llvm-mc -filetype=obj -triple aarch64-linux-gnu gnu-short.s -o gnu-short.o +# RUN: llvm-readelf --notes gnu-short.o | \ +# RUN: FileCheck --check-prefix=ELF-GNU-ERR -DSIZE=28 -DDATASIZE=18 \ +# RUN: -DERR="" %s +# RUN: llvm-readobj --notes gnu-short.o | \ +# RUN: FileCheck --check-prefix=OBJ-GNU-ERR -DSIZE=28 -DDATASIZE=18 \ +# RUN: -DERR="" %s + +# ELF-GNU-ERR: Displaying notes found in: .note.gnu.property +# ELF-GNU-ERR-NEXT: Owner Data size Description +# ELF-GNU-ERR-NEXT: GNU 0x000000[[DATASIZE]] NT_GNU_PROPERTY_TYPE_0 (property note) +# ELF-GNU-ERR-NEXT: AArch64 PAuth ABI tag: [[ERR]] + +# OBJ-GNU-ERR: Notes [ +# OBJ-GNU-ERR-NEXT: NoteSection { +# OBJ-GNU-ERR-NEXT: Name: .note.gnu.property +# OBJ-GNU-ERR-NEXT: Offset: 0x40 +# OBJ-GNU-ERR-NEXT: Size: 0x[[SIZE]] +# OBJ-GNU-ERR-NEXT: Note { +# OBJ-GNU-ERR-NEXT: Owner: GNU +# OBJ-GNU-ERR-NEXT: Data size: 0x[[DATASIZE]] +# OBJ-GNU-ERR-NEXT: Type: NT_GNU_PROPERTY_TYPE_0 (property note) +# OBJ-GNU-ERR-NEXT: Property [ +# OBJ-GNU-ERR-NEXT: AArch64 PAuth ABI tag: [[ERR]] +# OBJ-GNU-ERR-NEXT: ] +# OBJ-GNU-ERR-NEXT: } +# OBJ-GNU-ERR-NEXT: } +# OBJ-GNU-ERR-NEXT: ] + +#--- gnu-long.s + +.section ".note.gnu.property", "a" + .long 4 /* Name length is always 4 ("GNU") */ + .long end - begin /* Data length */ + .long 5 /* Type: NT_GNU_PROPERTY_TYPE_0 */ + .asciz "GNU" /* Name */ + .p2align 3 +begin: + /* PAuth ABI property note */ + .long 0xc0000001 /* Type: GNU_PROPERTY_AARCH64_FEATURE_PAUTH */ + .long 24 /* Data size */ + .quad 42 /* PAuth ABI platform */ + .quad 1 /* PAuth ABI version */ + .quad 0x0123456789ABCDEF + .p2align 3 /* Align to 8 byte for 64 bit */ +end: + +# RUN: llvm-mc -filetype=obj -triple aarch64-linux-gnu gnu-long.s -o gnu-long.o +# RUN: llvm-readelf --notes gnu-long.o | \ +# RUN: FileCheck --check-prefix=ELF-GNU-ERR -DSIZE=30 -DDATASIZE=20 \ +# RUN: -DERR="" %s +# RUN: llvm-readobj --notes gnu-long.o | \ +# RUN: FileCheck --check-prefix=OBJ-GNU-ERR -DSIZE=30 -DDATASIZE=20 \ +# RUN: -DERR="" %s diff --git a/llvm/test/tools/llvm-readobj/ELF/AArch64/aarch64-note-gnu-property.s b/llvm/test/tools/llvm-readobj/ELF/AArch64/aarch64-note-gnu-property.s index 872a3f150fdf..b680bf48cd1a 100644 --- a/llvm/test/tools/llvm-readobj/ELF/AArch64/aarch64-note-gnu-property.s +++ b/llvm/test/tools/llvm-readobj/ELF/AArch64/aarch64-note-gnu-property.s @@ -1,3 +1,5 @@ +// See tests for GNU_PROPERTY_AARCH64_FEATURE_PAUTH in aarch64-feature-pauth.s + // RUN: llvm-mc -filetype=obj -triple aarch64-linux-gnu %s -o %t // RUN: llvm-readelf --notes %t | FileCheck %s --check-prefix=GNU // RUN: llvm-readobj --notes %t | FileCheck %s --check-prefix=LLVM diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp index 25b78a94fe7a..23a149ca6335 100644 --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -5105,6 +5105,51 @@ template void GNUELFDumper::printAddrsig() { } } +template +static bool printAArch64PAuthABITag(raw_ostream &OS, uint32_t DataSize, + ArrayRef Desc) { + OS << " AArch64 PAuth ABI tag: "; + // DataSize - size without padding, Desc.size() - size with padding + if (DataSize != 16) { + OS << format("", DataSize); + return false; + } + + uint64_t Platform = + support::endian::read64(Desc.data() + 0); + std::string PlatformDesc = [Platform]() { + switch (Platform) { + case GNU_PROPERTY_AARCH64_FEATURE_PAUTH_PLATFORM_INVALID: + return " (invalid)"; + case GNU_PROPERTY_AARCH64_FEATURE_PAUTH_PLATFORM_BAREMETAL: + return " (baremetal)"; + case GNU_PROPERTY_AARCH64_FEATURE_PAUTH_PLATFORM_LINUX: + return " (linux)"; + default: + return ""; + } + }(); + + uint64_t Version = + support::endian::read64(Desc.data() + 8); + std::string VersionDesc = [Platform, Version]() -> std::string { + if (Platform != GNU_PROPERTY_AARCH64_FEATURE_PAUTH_PLATFORM_LINUX) + return ""; + // TODO: notify if Version contains bits higher than 4 set + return std::string(" (") + ((Version & (1 << 0)) ? "" : "!") + + "PointerAuthCalls, " + ((Version & (1 << 1)) ? "" : "!") + + "PointerAuthReturns, " + ((Version & (1 << 2)) ? "" : "!") + + "PointerAuthVTPtrAddressDiscrimination, " + + ((Version & (1 << 3)) ? "" : "!") + + "PointerAuthVTPtrTypeDiscrimination, " + + ((Version & (1 << 4)) ? "" : "!") + "PointerAuthInitFini)"; + }(); + OS << format("platform 0x%x%s, version 0x%x%s", Platform, + PlatformDesc.c_str(), Version, VersionDesc.c_str()); + + return true; +} + template static std::string getGNUProperty(uint32_t Type, uint32_t DataSize, ArrayRef Data) { @@ -5161,6 +5206,9 @@ static std::string getGNUProperty(uint32_t Type, uint32_t DataSize, if (PrData) OS << format("", PrData); return OS.str(); + case GNU_PROPERTY_AARCH64_FEATURE_PAUTH: + printAArch64PAuthABITag(OS, DataSize, Data); + return OS.str(); case GNU_PROPERTY_X86_FEATURE_2_NEEDED: case GNU_PROPERTY_X86_FEATURE_2_USED: OS << "x86 feature " @@ -5365,26 +5413,13 @@ static bool printAndroidNote(raw_ostream &OS, uint32_t NoteType, template static bool printAArch64Note(raw_ostream &OS, uint32_t NoteType, ArrayRef Desc) { - if (NoteType != NT_ARM_TYPE_PAUTH_ABI_TAG) - return false; - - OS << " AArch64 PAuth ABI tag: "; - if (Desc.size() < 16) { - OS << format("", Desc.size()); - return false; + switch (NoteType) { + case NT_ARM_TYPE_PAUTH_ABI_TAG: + // TODO: if Desc.size() includes alignment, pass the real length from the + // caller + return printAArch64PAuthABITag(OS, Desc.size(), Desc); } - - uint64_t platform = - support::endian::read64(Desc.data() + 0); - uint64_t version = - support::endian::read64(Desc.data() + 8); - OS << format("platform 0x%x, version 0x%x", platform, version); - - if (Desc.size() > 16) - OS << ", additional info 0x" - << toHex(ArrayRef(Desc.data() + 16, Desc.size() - 16)); - - return true; + return false; } template @@ -7640,7 +7675,7 @@ static bool printAarch64NoteLLVMStyle(uint32_t NoteType, ArrayRef Desc, if (NoteType != NT_ARM_TYPE_PAUTH_ABI_TAG) return false; - if (Desc.size() < 16) + if (Desc.size() != 16) return false; uint64_t platform = @@ -7650,9 +7685,8 @@ static bool printAarch64NoteLLVMStyle(uint32_t NoteType, ArrayRef Desc, W.printNumber("Platform", platform); W.printNumber("Version", version); - if (Desc.size() > 16) - W.printString("Additional info", - toHex(ArrayRef(Desc.data() + 16, Desc.size() - 16))); + // TODO: print nice description for supported tuples (see + // printAArch64PAuthABITag). return true; } @@ -7796,6 +7830,8 @@ template void LLVMELFDumper::printNotes() { } else if (Name == "ARM") { if (printAarch64NoteLLVMStyle(Type, Descriptor, W)) return Error::success(); + // TODO: notify about parsing failure, now we just silently print raw + // section contents } if (!Descriptor.empty()) { W.printBinaryBlock("Description data", Descriptor);