diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -148,6 +148,8 @@ struct Config { uint8_t osabi = 0; uint32_t andFeatures = 0; + SmallVector aarch64PauthAbiTag; + SmallVector gnuPropAarch64Pauth; llvm::CachePruningPolicy thinLTOCachePolicy; llvm::SetVector dependencyFiles; // for --dependency-file llvm::StringMap sectionStartMap; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -2591,6 +2591,43 @@ return ret; } +static void getAarch64PauthInfo() { + if (config->emachine != EM_AARCH64) + return; + + if (ctx.objectFiles.empty()) + return; + + config->aarch64PauthAbiTag = ctx.objectFiles.front()->aarch64PauthAbiTag; + config->gnuPropAarch64Pauth = ctx.objectFiles.front()->gnuPropAarch64Pauth; + for (ELFFileBase *f : ctx.objectFiles) { + StringRef f1 = ctx.objectFiles.front()->getName(); + StringRef f2 = f->getName(); + auto helper = [f1, f2](const SmallVector &d1, + const SmallVector &d2) { + if (d1.empty() ^ d2.empty()) + fatal((d1.empty() ? f1 : f2) + + " has no aarch64 pauth compatibility info while " + + (d1.empty() ? f2 : f1) + + " has one. Either all or no link units must have it."); + + if (!std::equal(d1.begin(), d1.end(), d2.begin(), d2.end())) + fatal("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()))); + }; + helper(config->aarch64PauthAbiTag, f->aarch64PauthAbiTag); + helper(config->gnuPropAarch64Pauth, f->gnuPropAarch64Pauth); + } + + if (!config->aarch64PauthAbiTag.empty() && + !config->gnuPropAarch64Pauth.empty()) + fatal("Input files contain both a .note.AARCH64-PAUTH-ABI-tag section and " + "a GNU_PROPERTY_AARCH64_FEATURE_PAUTH features in a " + ".note.gnu.property section. All the link units must use the same " + "way of specifying aarch64 pauth compatibility info."); +} + static void initSectionsAndLocalSyms(ELFFileBase *file, bool ignoreComdats) { switch (file->ekind) { case ELF32LEKind: @@ -2924,6 +2961,8 @@ // contain a hint to tweak linker's and loader's behaviors. config->andFeatures = getAndFeatures(); + getAarch64PauthInfo(); + // The Target instance handles target-specific stuff, such as applying // relocations or writing a PLT section. It also contains target-dependent // values such as a default image base address. diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -217,6 +217,8 @@ public: uint32_t andFeatures = 0; + SmallVector aarch64PauthAbiTag; + SmallVector gnuPropAarch64Pauth; bool hasCommonSyms = false; }; diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -877,25 +877,23 @@ handleSectionGroup(this->sections, entries); } -// If a source file is compiled with x86 hardware-assisted call flow control -// enabled, the generated object file contains feature flags indicating that -// fact. This function reads the feature flags and returns it. -// -// Essentially we want to read a single 32-bit value in this function, but this -// function is rather complicated because the value is buried deep inside a -// .note.gnu.property section. -// -// The section consists of one or more NOTE records. Each NOTE record consists -// 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) { +// Extract and write to the corresponding ObjFile fields the following values +// from the .note.gnu.property section: +// - andFeatures (storing info about hardware-assisted CFI on x86 and aarch64) +// - compatibility info for aarch64 pointer authentication according to the +// following ABI documentation: +// https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst#appendix-alternative-elf-marking-using-gnu-program-properties +template +static void parseGnuProperty(const InputSection &sec, ObjFile &f) { + if (config->emachine != EM_386 && config->emachine != EM_X86_64 && + config->emachine != EM_AARCH64) + return; + using Elf_Nhdr = typename ELFT::Nhdr; using Elf_Note = typename ELFT::Note; - uint32_t featuresSet = 0; ArrayRef data = sec.content(); - auto reportFatal = [&](const uint8_t *place, const char *msg) { + auto reportFatal = [&](const uint8_t *place, const Twine &msg) { fatal(toString(sec.file) + ":(" + sec.name + "+0x" + Twine::utohexstr(place - sec.content().data()) + "): " + msg); }; @@ -924,9 +922,12 @@ reportFatal(place, "program property is too short"); uint32_t type = read32(desc.data()); uint32_t size = read32(desc.data() + 4); + uint32_t sizeWithPadding = alignTo<(ELFT::Is64Bits ? 8 : 4)>(size); desc = desc.slice(8); if (desc.size() < size) reportFatal(place, "program property is too short"); + if (desc.size() < sizeWithPadding) + reportFatal(place, "missing required padding after program property"); if (type == featureAndType) { // We found a FEATURE_1_AND field. There may be more than one of these @@ -935,17 +936,65 @@ 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 (size < 16) + reportFatal(place, "too short aarch64 pauth compatibility info " + "(at least 16 bytes expected)"); + f.gnuPropAarch64Pauth = SmallVector(iterator_range(desc)); } // Padding is present in the note descriptor, if necessary. - desc = desc.slice(alignTo<(ELFT::Is64Bits ? 8 : 4)>(size)); + desc = desc.slice(sizeWithPadding); } // Go to next NOTE record to look for more FEATURE_1_AND descriptions. data = data.slice(nhdr->getSize(sec.addralign)); } - return featuresSet; + f.andFeatures = featuresSet; +} + +// Extract compatibility info for aarch64 pointer authentication from the +// .note.AARCH64-PAUTH-ABI-tag section and write it to the corresponding ObjFile +// field. See the following ABI documentation: +// https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst#elf-marking +template +static void parseAarch64PauthAbiTag(const InputSection &sec, ObjFile &f) { + if (config->emachine != EM_AARCH64) + return; + + using Elf_Nhdr = typename ELFT::Nhdr; + using Elf_Note = typename ELFT::Note; + ArrayRef data = sec.content(); + auto reportFatal = [&](const Twine &msg) { + fatal(toString(sec.file) + ":(" + sec.name + "): " + msg); + }; + + // Such a cast is a bad idea since values in the struct's fields depend on + // host's endianness. We already do the same in multiple other places (for + // example, parseGnuProperty above), so use it here as a temporary solution. + // TODO: here and in other similar places read the ELF note header data + // properly independently of host's endianness. + auto *nhdr = reinterpret_cast(data.data()); + if (data.size() < sizeof(Elf_Nhdr) || + data.size() < nhdr->getSize(sec.addralign)) + reportFatal("section is too short"); + + Elf_Note note(*nhdr); + if (nhdr->n_type != NT_ARM_TYPE_PAUTH_ABI_TAG) + reportFatal("invalid type field value " + Twine(nhdr->n_type) + " (" + + Twine(NT_ARM_TYPE_PAUTH_ABI_TAG) + " expected)"); + if (note.getName() != "ARM") + reportFatal("invalid name field value " + note.getName() + + " (ARM expected)"); + + ArrayRef desc = note.getDesc(sec.addralign); + if (desc.size() < 16) + reportFatal("too short aarch64 pauth compatibility info " + "(at least 16 bytes expected)"); + + f.aarch64PauthAbiTag = SmallVector(iterator_range(desc)); } template @@ -1002,7 +1051,12 @@ // .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)); + parseGnuProperty(InputSection(*this, sec, name), *this); + return &InputSection::discarded; + } + + if (name == ".note.AARCH64-PAUTH-ABI-tag") { + parseAarch64PauthAbiTag(InputSection(*this, sec, name), *this); return &InputSection::discarded; } diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h --- a/lld/ELF/SyntheticSections.h +++ b/lld/ELF/SyntheticSections.h @@ -142,6 +142,29 @@ GnuPropertySection(); void writeTo(uint8_t *buf) override; size_t getSize() const override; + void finalizeContents() override; + bool isNeeded() const override; + +private: + class Elf_Prop { + public: + Elf_Prop(uint32_t type, SmallVector data); + const uint32_t type; + const SmallVector data; + const uint32_t sizeWithPadding; + }; + + SmallVector props; +}; + +// .note.AARCH64-PAUTH-ABI-tag section. See +// https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst#elf-marking +class Aarch64PauthAbiTag final : public SyntheticSection { +public: + Aarch64PauthAbiTag(); + void writeTo(uint8_t *buf) override; + size_t getSize() const override; + bool isNeeded() const override; }; // .note.gnu.build-id section. @@ -1354,6 +1377,8 @@ std::unique_ptr strTab; std::unique_ptr symTab; std::unique_ptr symTabShndx; + std::unique_ptr gnuProp; + std::unique_ptr aarch64PauthAbiTag; void reset(); }; diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -313,23 +313,87 @@ : SyntheticSection(llvm::ELF::SHF_ALLOC, llvm::ELF::SHT_NOTE, config->wordsize, ".note.gnu.property") {} +void GnuPropertySection::finalizeContents() { + if (config->andFeatures) { + uint32_t andFeaturesType = config->emachine == EM_AARCH64 + ? GNU_PROPERTY_AARCH64_FEATURE_1_AND + : GNU_PROPERTY_X86_FEATURE_1_AND; + SmallVector andFeaturesData(4); + write32(andFeaturesData.data(), config->andFeatures); + props.emplace_back(andFeaturesType, std::move(andFeaturesData)); + } + + if (!config->gnuPropAarch64Pauth.empty()) { + assert(config->emachine == EM_AARCH64); + SmallVector data(iterator_range(config->gnuPropAarch64Pauth)); + props.emplace_back(GNU_PROPERTY_AARCH64_FEATURE_PAUTH, std::move(data)); + } +} + +bool GnuPropertySection::isNeeded() const { + // We call this before finalizeContents, so need to duplicate the logic here. + return config->andFeatures || !config->gnuPropAarch64Pauth.empty(); +} + void GnuPropertySection::writeTo(uint8_t *buf) { - 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 + assert(!props.empty()); + + 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 + + buf += 16; + for (const Elf_Prop &prop : props) { + write32(buf + 0, prop.type); + write32(buf + 4, prop.data.size()); + memcpy(buf + 8, prop.data.data(), prop.data.size()); + memset(buf + 8 + prop.data.size(), 0, + prop.sizeWithPadding - (4 + 4 + prop.data.size())); + buf += prop.sizeWithPadding; + } } -size_t GnuPropertySection::getSize() const { return config->is64 ? 32 : 28; } +size_t GnuPropertySection::getSize() const { + // We call this after finalizeContents, so props are already here. + if (props.empty()) + return 0; + + return 16 + std::accumulate(props.begin(), props.end(), 0, + [](uint32_t sum, const Elf_Prop &e) { + return sum + e.sizeWithPadding; + }); +} + +GnuPropertySection::Elf_Prop::Elf_Prop(uint32_t type, + SmallVector data) + : type(type), data(std::move(data)), + sizeWithPadding( + alignToPowerOf2(4 + 4 + this->data.size(), config->is64 ? 8 : 4)) {} + +Aarch64PauthAbiTag::Aarch64PauthAbiTag() + : SyntheticSection(llvm::ELF::SHF_ALLOC, llvm::ELF::SHT_NOTE, + config->wordsize, ".note.AARCH64-PAUTH-ABI-tag") {} + +bool Aarch64PauthAbiTag::isNeeded() const { + return !config->aarch64PauthAbiTag.empty(); +} + +void Aarch64PauthAbiTag::writeTo(uint8_t *buf) { + assert(isNeeded()); + const SmallVector &data = config->aarch64PauthAbiTag; + write32(buf, 4); // Name size + write32(buf + 4, data.size()); // Content size + write32(buf + 8, NT_ARM_TYPE_PAUTH_ABI_TAG); // Type + memcpy(buf + 12, "ARM", 4); // Name string + memcpy(buf + 16, data.data(), data.size()); + memset(buf + 16 + data.size(), 0, getSize() - 16 - data.size()); // Padding +} + +size_t Aarch64PauthAbiTag::getSize() const { + return alignToPowerOf2(16 + config->aarch64PauthAbiTag.size(), + config->is64 ? 8 : 4); +} BuildIdSection::BuildIdSection() : SyntheticSection(SHF_ALLOC, SHT_NOTE, 4, ".note.gnu.build-id"), diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -514,8 +514,15 @@ in.iplt = std::make_unique(); add(*in.iplt); - if (config->andFeatures) - add(*make()); + if (config->andFeatures || !config->gnuPropAarch64Pauth.empty()) { + in.gnuProp = std::make_unique(); + add(*in.gnuProp); + } + + if (!config->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 @@ -2096,6 +2103,7 @@ finalizeSynthetic(in.iplt.get()); finalizeSynthetic(in.ppc32Got2.get()); finalizeSynthetic(in.partIndex.get()); + finalizeSynthetic(in.gnuProp.get()); // Dynamic section must be the last one in this list and dynamic // symbol table section (dynSymTab) must be the first one. diff --git a/lld/test/ELF/aarch64-feature-pauth.s b/lld/test/ELF/aarch64-feature-pauth.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/aarch64-feature-pauth.s @@ -0,0 +1,168 @@ +# Require: aarch64 + +# RUN: split-file %s %tsplit + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %tsplit/gnu-note1.s -o %tgnu11.o +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %tsplit/gnu-note1.s -o %tgnu12.o +# RUN: ld.lld %tgnu11.o %tgnu12.o -o %tgnuok.so +# RUN: llvm-readelf -n %tgnuok.so | FileCheck --check-prefix OK1 %s + +# OK1: Properties: aarch64 feature PAUTH: platform 0x2a, version 0x1 + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %tsplit/abi-tag1.s -o %ttag11.o +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %tsplit/abi-tag1.s -o %ttag12.o +# RUN: ld.lld %ttag11.o %ttag12.o -o %ttagok.so +# RUN: llvm-readelf -n %ttagok.so | FileCheck --check-prefix OK2 -dump-input=always %s + +# OK2: aarch64 PAUTH ABI tag: platform 0x2a, version 0x1 + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %tsplit/gnu-note2.s -o %tgnu2.o +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %tsplit/abi-tag2.s -o %ttag2.o +# RUN: not ld.lld %tgnu11.o %tgnu12.o %tgnu2.o -o /dev/null 2>&1 | FileCheck --check-prefix ERR1 %s +# RUN: not ld.lld %ttag11.o %ttag12.o %ttag2.o -o /dev/null 2>&1 | FileCheck --check-prefix ERR1 %s + +# ERR1: ld.lld: 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 %tsplit/gnu-note-invalid-length.s -o %tgnulen.o +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %tsplit/abi-tag-invalid-length.s -o %ttaglen.o +# RUN: not ld.lld %tgnulen.o -o /dev/null 2>&1 | FileCheck --check-prefix ERR2 %s +# RUN: not ld.lld %ttaglen.o -o /dev/null 2>&1 | FileCheck --check-prefix ERR2 %s + +# ERR2: ld.lld: error: {{.*}}: too short aarch64 pauth compatibility info (at least 16 bytes expected) + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %tsplit/abi-tag-invalid-name.s -o %ttagname.o +# RUN: not ld.lld %ttagname.o -o /dev/null 2>&1 | FileCheck --check-prefix ERR3 %s + +# ERR3: ld.lld: error: {{.*}}: invalid name field value XXX (ARM expected) + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %tsplit/abi-tag-invalid-type.s -o %ttagtype.o +# RUN: not ld.lld %ttagtype.o -o /dev/null 2>&1 | FileCheck --check-prefix ERR4 %s + +# ERR4: ld.lld: error: {{.*}}: invalid type field value 42 (1 expected) + +# RUN: cat %tsplit/abi-tag1.s %tsplit/gnu-note1.s > %tboth1.s +# RUN: cat %tsplit/abi-tag1.s %tsplit/gnu-note1.s > %tboth2.s +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %tboth1.s -o %tboth1.o +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %tboth2.s -o %tboth2.o +# RUN: not ld.lld %tboth1.o %tboth2.o -o /dev/null 2>&1 | FileCheck --check-prefix ERR5 %s + +# ERR5: ld.lld: error: Input files contain both a .note.AARCH64-PAUTH-ABI-tag section and a GNU_PROPERTY_AARCH64_FEATURE_PAUTH features in a .note.gnu.property section. All the link units must use the same way of specifying aarch64 pauth compatibility info. + +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %tsplit/missing-in-gnu-note.s -o %tgnumiss.o +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %tsplit/no-info.s -o %tnoinfo.o +# RUN: not ld.lld %tgnu11.o %tgnumiss.o -o /dev/null 2>&1 | FileCheck --check-prefix ERR6 %s +# RUN: not ld.lld %tgnu11.o %tnoinfo.o -o /dev/null 2>&1 | FileCheck --check-prefix ERR6 %s +# RUN: not ld.lld %ttag11.o %tnoinfo.o -o /dev/null 2>&1 | FileCheck --check-prefix ERR6 %s + +# ERR6: ld.lld: error: {{.*}} has no aarch64 pauth compatibility info while {{.*}} has one. Either all or no link units must have it. + +#--- abi-tag-invalid-length.s + +.section ".note.AARCH64-PAUTH-ABI-tag", "a" +.long 4 +.long 8 +.long 1 +.asciz "ARM" + +.quad 42 + +#--- abi-tag-invalid-name.s + +.section ".note.AARCH64-PAUTH-ABI-tag", "a" +.long 4 +.long 16 +.long 1 +.asciz "XXX" + +.quad 42 // platform +.quad 1 // version + +#--- abi-tag-invalid-type.s + +.section ".note.AARCH64-PAUTH-ABI-tag", "a" +.long 4 +.long 16 +.long 42 +.asciz "ARM" + +.quad 42 // platform +.quad 1 // version + +#--- abi-tag1.s + +.section ".note.AARCH64-PAUTH-ABI-tag", "a" +.long 4 +.long 16 +.long 1 +.asciz "ARM" + +.quad 42 // platform +.quad 1 // version + +#--- abi-tag2.s + +.section ".note.AARCH64-PAUTH-ABI-tag", "a" +.long 4 +.long 16 +.long 1 +.asciz "ARM" + +.quad 42 // platform +.quad 2 // version + +#--- gnu-note-invalid-length.s + +.section ".note.gnu.property", "a" +.long 4 +.long 16 +.long 5 +.asciz "GNU" + +.long 0xc0000001 // GNU_PROPERTY_AARCH64_FEATURE_PAUTH +.long 8 +.quad 42 + +#--- gnu-note1.s + +.section ".note.gnu.property", "a" +.long 4 +.long 24 +.long 5 +.asciz "GNU" + +.long 0xc0000001 // GNU_PROPERTY_AARCH64_FEATURE_PAUTH +.long 16 +.quad 42 // platform +.quad 1 // version + +#--- gnu-note2.s + +.section ".note.gnu.property", "a" +.long 4 +.long 24 +.long 5 +.asciz "GNU" + +.long 0xc0000001 // GNU_PROPERTY_AARCH64_FEATURE_PAUTH +.long 16 +.quad 42 // platform +.quad 2 // version + +#--- missing-in-gnu-note.s + +.section ".note.gnu.property", "a" +.long 4 +.long 16 +.long 5 +.asciz "GNU" + +.long 0x42424242 // some dummy type +.long 8 +.long 0 +.long 0 + +#--- no-info.s + +.section ".test", "a" diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -1645,6 +1645,11 @@ NT_ANDROID_TYPE_MEMTAG = 4, }; +// ARM note types +enum { + NT_ARM_TYPE_PAUTH_ABI_TAG = 1, +}; + // Memory tagging values used in NT_ANDROID_TYPE_MEMTAG notes. enum { // Enumeration to determine the tagging mode. In Android-land, 'SYNC' means @@ -1668,6 +1673,7 @@ 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, diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -5077,6 +5077,24 @@ } } +template +static void printAarch64PauthInfo(raw_ostream &OS, ArrayRef Desc, + uint32_t DataSize) { + if (DataSize < 16) { + OS << format("", DataSize); + return; + } + + 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 (DataSize > 16) + OS << ", additional info 0x" + << toHex(ArrayRef(Desc.data() + 16, DataSize - 16)); +} + template static std::string getGNUProperty(uint32_t Type, uint32_t DataSize, ArrayRef Data) { @@ -5179,6 +5197,11 @@ if (PrData) OS << format("", PrData); return OS.str(); + case GNU_PROPERTY_AARCH64_FEATURE_PAUTH: { + OS << "aarch64 feature PAUTH: "; + printAarch64PauthInfo(OS, Data, DataSize); + return OS.str(); + } } } @@ -5334,6 +5357,17 @@ return true; } +template +static bool printARMNote(raw_ostream &OS, uint32_t NoteType, + ArrayRef Desc) { + if (NoteType != NT_ARM_TYPE_PAUTH_ABI_TAG) + return false; + + OS << " aarch64 PAUTH ABI tag: "; + printAarch64PauthInfo(OS, Desc, Desc.size()); + return true; +} + template void GNUELFDumper::printMemtag( const ArrayRef> DynamicEntries, @@ -5733,6 +5767,10 @@ "NT_ANDROID_TYPE_MEMTAG (Android memory tagging information)"}, }; +const NoteType ARMNoteTypes[] = { + {ELF::NT_ARM_TYPE_PAUTH_ABI_TAG, "NT_ARM_TYPE_PAUTH_ABI_TAG"}, +}; + const NoteType CoreNoteTypes[] = { {ELF::NT_PRSTATUS, "NT_PRSTATUS (prstatus structure)"}, {ELF::NT_FPREGSET, "NT_FPREGSET (floating point registers)"}, @@ -5849,6 +5887,8 @@ return FindNote(LLVMOMPOFFLOADNoteTypes); if (Name == "Android") return FindNote(AndroidNoteTypes); + if (Name == "ARM") + return FindNote(ARMNoteTypes); if (ELFType == ELF::ET_CORE) return FindNote(CoreNoteTypes); @@ -6004,6 +6044,9 @@ } else if (Name == "Android") { if (printAndroidNote(OS, Type, Descriptor)) return Error::success(); + } else if (Name == "ARM") { + if (printARMNote(OS, Type, Descriptor)) + return Error::success(); } if (!Descriptor.empty()) { OS << " description data:";