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