diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 71e72e7184b9f..55a04c43056c4 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -21,6 +21,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/LTO/LTO.h" #include "llvm/Object/IRObjectFile.h" +#include "llvm/Support/AArch64AttributeParser.h" #include "llvm/Support/ARMAttributeParser.h" #include "llvm/Support/ARMBuildAttributes.h" #include "llvm/Support/Endian.h" @@ -537,6 +538,52 @@ uint32_t ObjFile::getSectionIndex(const Elf_Sym &sym) const { this); } +template +static void +handleAArch64BAAndGnuProperties(ObjFile *file, Ctx &ctx, bool hasGP, + const AArch64BuildAttrSubsections &baInfo, + const GnuPropertiesInfo &gpInfo) { + if (hasGP) { + // Check for data mismatch + if (gpInfo.pauthAbiCoreInfo) { + if (baInfo.Pauth.TagPlatform != gpInfo.pauthAbiCoreInfo->platform || + baInfo.Pauth.TagSchema != gpInfo.pauthAbiCoreInfo->version) + Err(ctx) + << file + << " Pauth Data mismatch: file contains both GNU properties and " + "AArch64 build attributes sections with different Pauth data"; + } + if (baInfo.AndFeatures != gpInfo.andFeatures) + Err(ctx) << file + << " Features Data mismatch: file contains both GNU " + "properties and AArch64 build attributes sections with " + "different And Features data"; + } else { + // Write missing data + // We can only know when Pauth is missing. + // Unlike AArch64 Build Attributes, GNU properties does not give a way to + // distinguish between no-value given to value of '0' given. + if (baInfo.Pauth.TagPlatform || baInfo.Pauth.TagSchema) { + // According to the BuildAttributes specification Build Attributes + // default to a value of 0 when not present. A (TagPlatform, TagSchema) of + // (0, 0) maps to 'no PAuth property present'. A (TagPlatform, TagSchema) + // of (0, 1) maps to an explicit PAuth property of platform = 0, version = + // 0 ('Invalid'). + if (baInfo.Pauth.TagPlatform == 0 && baInfo.Pauth.TagSchema == 1) { + file->aarch64PauthAbiCoreInfo = {0, 0}; + } else { + file->aarch64PauthAbiCoreInfo = {baInfo.Pauth.TagPlatform, + baInfo.Pauth.TagSchema}; + } + } + file->andFeatures = baInfo.AndFeatures; + } +} + +template +static GnuPropertiesInfo readGnuProperty(Ctx &, const InputSection &, + ObjFile &); + template void ObjFile::parse(bool ignoreComdats) { object::ELFFile obj = this->getObj(); // Read a section table. justSymbols is usually false. @@ -552,8 +599,31 @@ template void ObjFile::parse(bool ignoreComdats) { StringRef shstrtab = CHECK2(obj.getSectionStringTable(objSections), this); uint64_t size = objSections.size(); sections.resize(size); + + // For handling AArch64 Build attributes and GNU properties + AArch64BuildAttrSubsections aarch64BAsubSections; + GnuPropertiesInfo gnuProperty; + bool hasAArch64BuildAttributes = false; + bool hasGNUProperties = false; + for (size_t i = 0; i != size; ++i) { const Elf_Shdr &sec = objSections[i]; + // Object files that use processor features such as Intel Control-Flow + // Enforcement (CET) or AArch64 Branch Target Identification BTI, use a + // .note.gnu.property section containing a bitfield of feature bits like the + // GNU_PROPERTY_X86_FEATURE_1_IBT flag. Read a bitmap containing the flag. + if (check(obj.getSectionName(sec, shstrtab)) == ".note.gnu.property") { + gnuProperty = readGnuProperty( + ctx, + InputSection(*this, sec, check(obj.getSectionName(sec, shstrtab))), + *this); + hasGNUProperties = true; + // Since we merge bitmaps from multiple object files to create a new + // .note.gnu.property containing a single AND'ed bitmap, we discard an + // input file's .note.gnu.property section. + sections[i] = &InputSection::discarded; + } + if (LLVM_LIKELY(sec.sh_type == SHT_PROGBITS)) continue; if (LLVM_LIKELY(sec.sh_type == SHT_GROUP)) { @@ -637,13 +707,27 @@ template void ObjFile::parse(bool ignoreComdats) { } break; case EM_AARCH64: - // FIXME: BuildAttributes have been implemented in llvm, but not yet in - // lld. Remove the section so that it does not accumulate in the output - // file. When support is implemented we expect not to output a build - // attributes section in files of type ET_EXEC or ET_SHARED, but ld -r - // ouptut will need a single merged attributes section. - if (sec.sh_type == SHT_AARCH64_ATTRIBUTES) + // At this stage AArch64 Build Attributes does not replace GNU Properties. + // When both exists, their values must match. + // When both exists and contain different attributes, they complement each + // other. Currently attributes are represented in the linked object file + // as GNU properties, which are already supported by the Linux kernel and + // the dynamic loader. In the future, when relocatable linking (`-r` flag) + // is performed, a single merged AArch64 Build Attributes section will be + // emitted. + if (sec.sh_type == SHT_AARCH64_ATTRIBUTES) { + ArrayRef contents = check(obj.getSectionContents(sec)); + AArch64AttributeParser attributes; + StringRef name = check(obj.getSectionName(sec, shstrtab)); + InputSection isec(*this, sec, name); + if (Error e = attributes.parse(contents, ELFT::Endianness)) { + Warn(ctx) << &isec << ": " << std::move(e); + } else { + aarch64BAsubSections = extractBuildAttributesSubsections(attributes); + hasAArch64BuildAttributes = true; + } sections[i] = &InputSection::discarded; + } // Producing a static binary with MTE globals is not currently supported, // remove all SHT_AARCH64_MEMTAG_GLOBALS_STATIC sections as they're unused // medatada, and we don't want them to end up in the output file for @@ -655,6 +739,14 @@ template void ObjFile::parse(bool ignoreComdats) { } } + if (hasAArch64BuildAttributes) { + // Handle AArch64 Build Attributes and GNU properties: + // - Err on mismatched values. + // - Store missing values as GNU properties. + handleAArch64BAAndGnuProperties(this, ctx, hasGNUProperties, + aarch64BAsubSections, gnuProperty); + } + // Read a symbol table. initializeSymbols(obj); } @@ -974,8 +1066,8 @@ static void parseGnuPropertyNote(Ctx &ctx, ELFFileBase &f, // hardware-assisted call flow control; // - AArch64 PAuth ABI core info (16 bytes). template -static void readGnuProperty(Ctx &ctx, const InputSection &sec, - ObjFile &f) { +static GnuPropertiesInfo readGnuProperty(Ctx &ctx, const InputSection &sec, + ObjFile &f) { using Elf_Nhdr = typename ELFT::Nhdr; using Elf_Note = typename ELFT::Note; @@ -992,7 +1084,7 @@ static void readGnuProperty(Ctx &ctx, const InputSection &sec, featureAndType = GNU_PROPERTY_RISCV_FEATURE_1_AND; break; default: - return; + return GnuPropertiesInfo{}; } ArrayRef data = sec.content(); @@ -1007,7 +1099,7 @@ static void readGnuProperty(Ctx &ctx, const InputSection &sec, auto *nhdr = reinterpret_cast(data.data()); if (data.size() < sizeof(Elf_Nhdr) || data.size() < nhdr->getSize(sec.addralign)) - return void(err(data.data()) << "data is too short"); + return (err(data.data()) << "data is too short", GnuPropertiesInfo{}); Elf_Note note(*nhdr); if (nhdr->n_type != NT_GNU_PROPERTY_TYPE_0 || note.getName() != "GNU") { @@ -1023,6 +1115,7 @@ static void readGnuProperty(Ctx &ctx, const InputSection &sec, // Go to next NOTE record to look for more FEATURE_1_AND descriptions. data = data.slice(nhdr->getSize(sec.addralign)); } + return GnuPropertiesInfo{f.andFeatures, f.aarch64PauthAbiCoreInfo}; } template diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h index ba844ad18f637..9d40030f22fcc 100644 --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -244,6 +244,11 @@ class ELFFileBase : public InputFile { std::optional aarch64PauthAbiCoreInfo; }; +struct GnuPropertiesInfo { + uint32_t andFeatures = 0; + std::optional pauthAbiCoreInfo; +}; + // .o file. template class ObjFile : public ELFFileBase { LLVM_ELF_IMPORT_TYPES_ELFT(ELFT) diff --git a/lld/test/ELF/aarch64-build-attributes-be.s b/lld/test/ELF/aarch64-build-attributes-be.s new file mode 100644 index 0000000000000..8ae9ce5d7f60b --- /dev/null +++ b/lld/test/ELF/aarch64-build-attributes-be.s @@ -0,0 +1,29 @@ +// REQUIRES: aarch64 +// RUN: llvm-mc -triple=aarch64_be %s -filetype=obj -o %t.o +// RUN: ld.lld %t.o --shared -o %t.so +// RUN: llvm-readelf -n %t.so | FileCheck %s --check-prefix=NOTE + +// RUN: llvm-mc -triple=aarch64_be %s -filetype=obj -o %t.o +// RUN: ld.lld %t.o --shared -o %t.so +// RUN: llvm-readelf -n %t.so | FileCheck %s --check-prefix=NOTE +// RUN: ld.lld %t.o -o %t +// RUN: llvm-readelf -n %t.so | FileCheck %s --check-prefix=NOTE +// RUN: ld.lld -r %t.o -o %t2.o +// RUN: llvm-readelf -n %t.so | FileCheck %s --check-prefix=NOTE + +/// Test that lld can read big-endian build-attributes. + +// NOTE: Displaying notes found in: .note.gnu.property +// NOTE-NEXT: Owner Data size Description +// NOTE-NEXT: GNU 0x00000028 NT_GNU_PROPERTY_TYPE_0 (property note) +// NOTE-NEXT: Properties: aarch64 feature: BTI, PAC, GCS +// NOTE-NEXT: AArch64 PAuth ABI core info: platform 0x89abcdef (unknown), version 0x89abcdef + + +.aeabi_subsection aeabi_pauthabi, required, uleb128 +.aeabi_attribute Tag_PAuth_Platform, 0x123456789ABCDEF +.aeabi_attribute Tag_PAuth_Schema, 0x123456789ABCDEF +.aeabi_subsection aeabi_feature_and_bits, optional, uleb128 +.aeabi_attribute Tag_Feature_BTI, 1 +.aeabi_attribute Tag_Feature_PAC, 1 +.aeabi_attribute Tag_Feature_GCS, 1 diff --git a/lld/test/ELF/aarch64-build-attributes-err.s b/lld/test/ELF/aarch64-build-attributes-err.s new file mode 100644 index 0000000000000..029cb1a68a5e7 --- /dev/null +++ b/lld/test/ELF/aarch64-build-attributes-err.s @@ -0,0 +1,35 @@ +// REQUIRES: aarch64 + +// RUN: llvm-mc -triple=aarch64 %s -filetype=obj -o %t.o +// RUN: not ld.lld %t.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR + +// ERR: Pauth Data mismatch: file contains both GNU properties and AArch64 build attributes sections with different Pauth data +// ERR-NEXT: Features Data mismatch: file contains both GNU properties and AArch64 build attributes sections with different And Features data + +.aeabi_subsection aeabi_pauthabi, required, uleb128 +.aeabi_attribute Tag_PAuth_Platform, 5 +.aeabi_attribute Tag_PAuth_Schema, 5 +.aeabi_subsection aeabi_feature_and_bits, optional, uleb128 +.aeabi_attribute Tag_Feature_BTI, 1 +.aeabi_attribute Tag_Feature_PAC, 1 +.aeabi_attribute Tag_Feature_GCS, 1 + +.section ".note.gnu.property", "a" +.long 0x4 +.long 0x10 +.long 0x5 +.asciz "GNU" +.long 0xc0000000 // GNU_PROPERTY_AARCH64_FEATURE_1_AND +.long 0x4 +.long 0x2 // GNU_PROPERTY_AARCH64_FEATURE_1_PAC +.long 0x0 + +.section ".note.gnu.property", "a" +.long 0x4 +.long 0x18 +.long 0x5 +.asciz "GNU" +.long 0xc0000001 +.long 0x10 +.quad 0x12345678 // platform +.quad 0x87654321 // version diff --git a/lld/test/ELF/aarch64-build-attributes-invalid.s b/lld/test/ELF/aarch64-build-attributes-invalid.s new file mode 100644 index 0000000000000..7cd4723087de2 --- /dev/null +++ b/lld/test/ELF/aarch64-build-attributes-invalid.s @@ -0,0 +1,18 @@ +// REQUIRES: aarch64 + +// RUN: llvm-mc -triple=aarch64 -filetype=obj %s -o %t.o +// RUN: ld.lld -r %t.o -o %t.invalid.o +// RUN: llvm-readelf -n %t.invalid.o | FileCheck %s + +/// According to the BuildAttributes specification Build Attributes +/// A (TagPlatform, TagSchema)of (0, 1) maps to an explicit PAuth property +/// of platform = 0, version = 0 ('Invalid'). + +// CHECK: Displaying notes found in: .note.gnu.property +// CHECK-NEXT: Owner Data size Description +// CHECK-NEXT: GNU 0x00000018 NT_GNU_PROPERTY_TYPE_0 (property note) +// CHECK-NEXT: Properties: AArch64 PAuth ABI core info: platform 0x0 (invalid), version 0x0 + +.aeabi_subsection aeabi_pauthabi, required, uleb128 +.aeabi_attribute Tag_PAuth_Platform, 0 +.aeabi_attribute Tag_PAuth_Schema, 1 diff --git a/lld/test/ELF/aarch64-build-attributes-malformed.s b/lld/test/ELF/aarch64-build-attributes-malformed.s new file mode 100644 index 0000000000000..f40da1ab7e8a1 --- /dev/null +++ b/lld/test/ELF/aarch64-build-attributes-malformed.s @@ -0,0 +1,18 @@ +# REQUIRES: aarch64 + +# RUN: llvm-mc -triple=aarch64 -filetype=obj %s -o %t.o +# RUN: ld.lld %t.o /dev/null 2>&1 | FileCheck %s + +# CHECK: (.ARM.attributes): unexpected end of data at offset 0x3f while reading [0x3d, 0x41) + +.section .ARM.attributes,"",%0x70000003 +.byte 0x41 // Tag 'A' (format version) +.long 0x00000019 // Subsection length +.asciz "aeabi_pauthabi" // Subsection name +.byte 0x00, 0x00 // Optionality and Type +.byte 0x01, 0x01, 0x02, 0x01 // PAuth_Platform and PAuth_Schema +.long 0x00000023 // Subsection length +.asciz "aeabi_feature_and_bits" // Subsection name +.byte 0x01, 0x00 // Optionality and Type +.byte 0x00, 0x01, 0x01, 0x01, 0x02, 0x01 // BTI, PAC, GCS +.byte 0x00, 0x00 // This is the malformation, data is too long. diff --git a/lld/test/ELF/aarch64-build-attributes-mixed.s b/lld/test/ELF/aarch64-build-attributes-mixed.s new file mode 100644 index 0000000000000..68e9a1fbffda5 --- /dev/null +++ b/lld/test/ELF/aarch64-build-attributes-mixed.s @@ -0,0 +1,67 @@ +// REQUIRES: aarch64 + +// RUN: rm -rf %t && split-file %s %t && cd %t + +// RUN: llvm-mc -triple=aarch64 -filetype=obj %s -o %t11.o +// RUN: llvm-mc -triple=aarch64 -filetype=obj merged-property.s -o %t12.o +// RUN: llvm-mc -triple=aarch64 -filetype=obj merged-property2.s -o %t13.o +// RUN: ld.lld -r %t11.o %t12.o %t13.o -o %t.merged1.o +// RUN: llvm-readelf -n %t.merged1.o | FileCheck %s --check-prefix=NOTE-MIXED + +/// This test verifies merging of AArch64 build attributes and GNU property notes. +/// Three object files are combined: one with build attributes (PAuth information, BTI, PAC, GCS), +/// and two with GNU property notes encoding the same feature bits. +/// PAuth ABI info is provided in one of the files and it is expected to be preserved in the merged output. + +// NOTE-MIXED: Displaying notes found in: .note.gnu.property +// NOTE-MIXED-NEXT: Owner Data size Description +// NOTE-MIXED-NEXT: GNU 0x00000028 NT_GNU_PROPERTY_TYPE_0 (property note) +// NOTE-MIXED-NEXT: Properties: aarch64 feature: BTI, PAC +// NOTE-MIXED-NEXT: AArch64 PAuth ABI core info: platform 0x31 (unknown), version 0x13 + +// CHECK: .note.gnu.property +// CHECK-NOT: .ARM.attributes + +.aeabi_subsection aeabi_pauthabi, required, uleb128 +.aeabi_attribute Tag_PAuth_Platform, 49 +.aeabi_attribute Tag_PAuth_Schema, 19 +.aeabi_subsection aeabi_feature_and_bits, optional, uleb128 +.aeabi_attribute Tag_Feature_BTI, 1 +.aeabi_attribute Tag_Feature_PAC, 1 +.aeabi_attribute Tag_Feature_GCS, 1 + + +//--- merged-property.s +.section ".note.gnu.property", "a" + .long 0x4 // Name length is always 4 ("GNU") + .long end - begin // Data length + .long 0x5 // Type: NT_GNU_PROPERTY_TYPE_0 + .asciz "GNU" // Name + .p2align 0x3 +begin: + .long 0xc0000000 // GNU_PROPERTY_AARCH64_FEATURE_1_AND + .long 0x4 + .long 0x7 // pr_data: BTI (1), PAC (2), GCS (4) = 0b111 = 7 + .long 0x0 + // PAuth ABI property note + .long 0xc0000001 // GNU_PROPERTY_AARCH64_FEATURE_PAUTH + .long 0x10 // Data length + .quad 0x31 // PAuth ABI platform + .quad 0x13 // PAuth ABI version + .p2align 0x3 // Align to 8 byte for 64 bit +end: + +//--- merged-property2.s +.section .note.gnu.property, "a" + .align 0x4 + .long 0x4 // Name length is always 4 ("GNU") + .long end2 - begin2 // Data length + .long 0x5 // Type: NT_GNU_PROPERTY_TYPE_0 + .asciz "GNU" // Name +begin2: + .align 0x4 + .long 0xc0000000 // Type: GNU_PROPERTY_AARCH64_FEATURE_1_AND + .long 0x4 // Data length + .long 0x7 // pr_data: BTI (1), PAC (2), GCS (4) = 0b111 = 7 + .long 0x0 +end2: diff --git a/lld/test/ELF/aarch64-build-attributes.s b/lld/test/ELF/aarch64-build-attributes.s index 24e15f94e3d4a..f2d542150897e 100644 --- a/lld/test/ELF/aarch64-build-attributes.s +++ b/lld/test/ELF/aarch64-build-attributes.s @@ -1,26 +1,50 @@ // REQUIRES: aarch64 -// RUN: llvm-mc -triple=aarch64 %s -filetype=obj -o %t.o -// RUN: ld.lld %t.o --shared -o %t.so -// RUN: llvm-readelf --sections %t.so | FileCheck %s -// RUN: ld.lld %t.o -o %t -// RUN: llvm-readelf --sections %t | FileCheck %s -// RUN: ld.lld -r %t.o -o %t2.o -// RUN: llvm-readelf --sections %t2.o | FileCheck %s - -/// File has a Build attributes section. This should not appear in -/// ET_EXEC or ET_SHARED files as there is no requirement for it to -/// do so. FIXME, the ld -r (relocatable link) should output a single -/// merged build attributes section. When full support is added in -/// ld.lld this test should be updated. +// RUN: rm -rf %t && split-file %s %t && cd %t +// RUN: llvm-mc -triple=aarch64 -filetype=obj %s -o %t1.o +// RUN: llvm-mc -triple=aarch64 -filetype=obj pauth-bti-gcs.s -o %t2.o +// RUN: llvm-mc -triple=aarch64 -filetype=obj pauth-bti-pac.s -o %t3.o +// RUN: ld.lld -r %t1.o %t2.o %t3.o -o %t.merged.o +// RUN: llvm-readelf -n %t.merged.o | FileCheck %s --check-prefix=NOTE + +/// This test merges three object files with AArch64 build attributes. +/// All contain identical PAuth ABI info (platform/version), which must be preserved. +/// Only BTI is common across all three in the AND feature set, so the merged output +/// must show BTI only. PAC and GCS are present in subsets and should not appear. + +// NOTE: Displaying notes found in: .note.gnu.property +// NOTE-NEXT: Owner Data size Description +// NOTE-NEXT: GNU 0x00000028 NT_GNU_PROPERTY_TYPE_0 (property note) +// NOTE-NEXT: Properties: aarch64 feature: BTI +// NOTE-NEXT: AArch64 PAuth ABI core info: platform 0x31 (unknown), version 0x13 + +// CHECK: .note.gnu.property // CHECK-NOT: .ARM.attributes +.aeabi_subsection aeabi_pauthabi, required, uleb128 +.aeabi_attribute Tag_PAuth_Platform, 49 +.aeabi_attribute Tag_PAuth_Schema, 19 .aeabi_subsection aeabi_feature_and_bits, optional, uleb128 .aeabi_attribute Tag_Feature_BTI, 1 .aeabi_attribute Tag_Feature_PAC, 1 .aeabi_attribute Tag_Feature_GCS, 1 -.global _start -.type _start, %function -_start: -ret + +//--- pauth-bti-gcs.s +.aeabi_subsection aeabi_pauthabi, required, uleb128 +.aeabi_attribute Tag_PAuth_Platform, 49 +.aeabi_attribute Tag_PAuth_Schema, 19 +.aeabi_subsection aeabi_feature_and_bits, optional, uleb128 +.aeabi_attribute Tag_Feature_BTI, 1 +.aeabi_attribute Tag_Feature_PAC, 0 +.aeabi_attribute Tag_Feature_GCS, 1 + + +//--- pauth-bti-pac.s +.aeabi_subsection aeabi_pauthabi, required, uleb128 +.aeabi_attribute Tag_PAuth_Platform, 49 +.aeabi_attribute Tag_PAuth_Schema, 19 +.aeabi_subsection aeabi_feature_and_bits, optional, uleb128 +.aeabi_attribute Tag_Feature_BTI, 1 +.aeabi_attribute Tag_Feature_PAC, 1 +.aeabi_attribute Tag_Feature_GCS, 0 diff --git a/llvm/include/llvm/Support/AArch64AttributeParser.h b/llvm/include/llvm/Support/AArch64AttributeParser.h index aa82ca13668d5..796dbfd6f4162 100644 --- a/llvm/include/llvm/Support/AArch64AttributeParser.h +++ b/llvm/include/llvm/Support/AArch64AttributeParser.h @@ -25,6 +25,17 @@ class AArch64AttributeParser : public ELFExtendedAttrParser { : ELFExtendedAttrParser(nullptr, returnTagsNamesMap()) {} }; +// Used for extracting AArch64 Build Attributes +struct AArch64BuildAttrSubsections { + struct PauthSubSection { + uint64_t TagPlatform = 0; + uint64_t TagSchema = 0; + } Pauth; + uint32_t AndFeatures = 0; +}; + +AArch64BuildAttrSubsections +extractBuildAttributesSubsections(const llvm::AArch64AttributeParser &); } // namespace llvm #endif // LLVM_SUPPORT_AARCH64ATTRIBUTEPARSER_H diff --git a/llvm/lib/Support/AArch64AttributeParser.cpp b/llvm/lib/Support/AArch64AttributeParser.cpp index c675ef2a3b3df..eed8dba5d250a 100644 --- a/llvm/lib/Support/AArch64AttributeParser.cpp +++ b/llvm/lib/Support/AArch64AttributeParser.cpp @@ -8,6 +8,7 @@ //===---------------------------------------------------------------------===// #include "llvm/Support/AArch64AttributeParser.h" +#include "llvm/Support/AArch64BuildAttributes.h" std::vector & llvm::AArch64AttributeParser::returnTagsNamesMap() { @@ -19,3 +20,29 @@ llvm::AArch64AttributeParser::returnTagsNamesMap() { {"aeabi_feature_and_bits", 2, "Tag_Feature_GCS"}}; return TagsNamesMap; } + +llvm::AArch64BuildAttrSubsections llvm::extractBuildAttributesSubsections( + const llvm::AArch64AttributeParser &Attributes) { + + llvm::AArch64BuildAttrSubsections SubSections; + auto GetPauthValue = [&Attributes](unsigned Tag) { + return Attributes.getAttributeValue("aeabi_pauthabi", Tag).value_or(0); + }; + SubSections.Pauth.TagPlatform = + GetPauthValue(llvm::AArch64BuildAttributes::TAG_PAUTH_PLATFORM); + SubSections.Pauth.TagSchema = + GetPauthValue(llvm::AArch64BuildAttributes::TAG_PAUTH_SCHEMA); + + auto GetFeatureValue = [&Attributes](unsigned Tag) { + return Attributes.getAttributeValue("aeabi_feature_and_bits", Tag) + .value_or(0); + }; + SubSections.AndFeatures |= + GetFeatureValue(llvm::AArch64BuildAttributes::TAG_FEATURE_BTI); + SubSections.AndFeatures |= + GetFeatureValue(llvm::AArch64BuildAttributes::TAG_FEATURE_PAC) << 1; + SubSections.AndFeatures |= + GetFeatureValue(llvm::AArch64BuildAttributes::TAG_FEATURE_GCS) << 2; + + return SubSections; +}