diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 5f6d2b6b647ee..2412e63048c7d 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -22,6 +22,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" @@ -539,6 +540,65 @@ uint32_t ObjFile::getSectionIndex(const Elf_Sym &sym) const { this); } +template +static void +handleAArch64BAAndGnuProperties(const ELFT &tPointer, Ctx &ctx, bool isBE, + bool hasBA, bool hasGP, + const AArch64BuildAttrSubsections &baInfo, + const gnuPropertiesInfo &gpInfo) { + + auto serializeUnsigned = [&](unsigned valueLow, unsigned valueHigh, + bool isBE) -> std::array { + std::array arr; + for (size_t i = 0; i < 8; ++i) { + arr[i] = static_cast( + (static_cast(valueLow) >> (8 * (isBE ? (7 - i) : i))) & + 0xFF); + arr[i + 8] = static_cast( + (static_cast(valueHigh) >> (8 * (isBE ? (7 - i) : i))) & + 0xFF); + }; + return arr; + }; + + if (hasBA && hasGP) { + // Check for data mismatch + if (!gpInfo.aarch64PauthAbiCoreInfo.empty()) { + auto baPauth = serializeUnsigned(baInfo.Pauth.TagPlatform, + baInfo.Pauth.TagSchema, isBE); + if (gpInfo.aarch64PauthAbiCoreInfo != ArrayRef(baPauth)) + Err(ctx) + << tPointer + << " Pauth Data mismatch: file contains both GNU properties and " + "AArch64 build attributes sections with different Pauth data"; + } + if (baInfo.AndFeatures != gpInfo.andFeatures) + Err(ctx) << tPointer + << " Features Data mismatch: file contains both GNU " + "properties and AArch64 build attributes sections with " + "different And Features data"; + } + + if (hasBA && !hasGP) { + // 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) { + tPointer->aarch64PauthAbiCoreInfoStorage = serializeUnsigned( + baInfo.Pauth.TagPlatform, baInfo.Pauth.TagSchema, isBE); + tPointer->aarch64PauthAbiCoreInfo = + tPointer->aarch64PauthAbiCoreInfoStorage; + } + tPointer->andFeatures = baInfo.AndFeatures; + } +} + +// Forward declaration: +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. @@ -554,8 +614,32 @@ 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 gnuPropertiesInformation; + 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") { + gnuPropertiesInformation = 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)) { @@ -639,16 +723,30 @@ 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 + // metadata, and we don't want them to end up in the output file for // static executables. if (sec.sh_type == SHT_AARCH64_MEMTAG_GLOBALS_STATIC && !canHaveMemtagGlobals(ctx)) @@ -657,6 +755,16 @@ template void ObjFile::parse(bool ignoreComdats) { } } + if (hasAArch64BuildAttributes) { + bool isBE = ELFT::Endianness == llvm::endianness::big; + // Handle AArch64 Build Attributes and GNU properties: + // - Err on mismatched values. + // - Store missing values as GNU properties. + handleAArch64BAAndGnuProperties(this, ctx, isBE, hasAArch64BuildAttributes, + hasGNUProperties, aarch64BAsubSections, + gnuPropertiesInformation); + } + // Read a symbol table. initializeSymbols(obj); } @@ -968,14 +1076,15 @@ static void parseGnuPropertyNote(Ctx &ctx, ELFFileBase &f, desc = desc.slice(alignTo<(ELFT::Is64Bits ? 8 : 4)>(size)); } } + // Read the following info from the .note.gnu.property section and write it to // the corresponding fields in `ObjFile`: // - Feature flags (32 bits) representing x86 or AArch64 features for // 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; @@ -991,7 +1100,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") { @@ -1011,6 +1120,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 @@ -1065,19 +1175,6 @@ InputSectionBase *ObjFile::createInputSection(uint32_t idx, return &InputSection::discarded; } - // 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. - // - // 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. - if (name == ".note.gnu.property") { - readGnuProperty(ctx, 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, // see https://gcc.gnu.org/wiki/SplitStacks. An object file compiled diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h index 808cb5d24079f..f9f9b8561c4d5 100644 --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -242,6 +242,12 @@ class ELFFileBase : public InputFile { uint32_t andFeatures = 0; bool hasCommonSyms = false; ArrayRef aarch64PauthAbiCoreInfo; + std::array aarch64PauthAbiCoreInfoStorage; +}; + +struct gnuPropertiesInfo { + uint32_t andFeatures = 0; + ArrayRef aarch64PauthAbiCoreInfo; }; // .o file. @@ -268,7 +274,6 @@ template class ObjFile : public ELFFileBase { uint32_t getSectionIndex(const Elf_Sym &sym) const; - // Pointer to this input file's .llvm_addrsig section, if it has one. const Elf_Shdr *addrsigSec = nullptr; diff --git a/lld/test/ELF/Inputs/aarch64-func3-pac-replace.s b/lld/test/ELF/Inputs/aarch64-func3-pac-replace.s new file mode 100644 index 0000000000000..b6133844d07f7 --- /dev/null +++ b/lld/test/ELF/Inputs/aarch64-func3-pac-replace.s @@ -0,0 +1,10 @@ +// Declare file properties exclusively with aarch64 build attributes. + +.aeabi_subsection aeabi_feature_and_bits, optional, uleb128 +.aeabi_attribute Tag_Feature_PAC, 1 + +.text +.globl func3 +.type func3,@function +func3: + ret diff --git a/lld/test/ELF/Inputs/aarch64-pac1-replace.s b/lld/test/ELF/Inputs/aarch64-pac1-replace.s new file mode 100644 index 0000000000000..f763f9de2342a --- /dev/null +++ b/lld/test/ELF/Inputs/aarch64-pac1-replace.s @@ -0,0 +1,13 @@ +// This file replace gnu properties with aarch64 build attributes. + +.aeabi_subsection aeabi_feature_and_bits, optional, uleb128 +.aeabi_attribute Tag_Feature_PAC, 1 + +.text +.globl func2 +.type func2,@function +func2: + .globl func3 + .type func3, @function + bl func3 + ret 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..985e19348cf66 --- /dev/null +++ b/lld/test/ELF/aarch64-build-attributes-be.s @@ -0,0 +1,50 @@ +// 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 + +/// The Build attributes section appearing in the output of +/// llvm-mc should not appear in the output of lld, because +/// AArch64 build attributes are being transformed into .gnu.properties. + +/// Test mc -> big endian, lld -> little endian +// 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 mc -> little endian, lld -> big endian +// RUN: llvm-mc -triple=aarch64 %s -filetype=obj -o %t.o +// RUN: ld.lld --EB %t.o --shared -o %t.so +// RUN: llvm-readelf -n %t.so | FileCheck %s --check-prefix=NOTE +// RUN: ld.lld --EB %t.o -o %t +// RUN: llvm-readelf -n %t.so | FileCheck %s --check-prefix=NOTE +// RUN: ld.lld --EB -r %t.o -o %t2.o +// RUN: llvm-readelf -n %t.so | FileCheck %s --check-prefix=NOTE + +/// Test mc -> big endian, lld -> big endian +// RUN: llvm-mc -triple=aarch64_be %s -filetype=obj -o %t.o +// RUN: ld.lld --EB %t.o --shared -o %t.so +// RUN: llvm-readelf -n %t.so | FileCheck %s --check-prefix=NOTE +// RUN: ld.lld --EB %t.o -o %t +// RUN: llvm-readelf -n %t.so | FileCheck %s --check-prefix=NOTE +// RUN: ld.lld --EB -r %t.o -o %t2.o +// RUN: llvm-readelf -n %t.so | FileCheck %s --check-prefix=NOTE + +// 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, 81985529216486895 +.aeabi_attribute Tag_PAuth_Schema, 81985529216486895 +.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..965f13673eb5c --- /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 4 +.long 0x10 +.long 0x5 +.asciz "GNU" +.long 0xc0000000 // GNU_PROPERTY_AARCH64_FEATURE_1_AND +.long 4 +.long 2 // GNU_PROPERTY_AARCH64_FEATURE_1_PAC +.long 0 + +.section ".note.gnu.property", "a" +.long 4 +.long 24 +.long 5 +.asciz "GNU" +.long 0xc0000001 +.long 16 +.quad 305419896 // platform +.quad 2271560481 // version 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..208da0c224179 --- /dev/null +++ b/lld/test/ELF/aarch64-build-attributes-malformed.s @@ -0,0 +1,16 @@ +# 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): invalid Extended Build Attributes subsection size at offset: 39 + +.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..729aebcef8d75 --- /dev/null +++ b/lld/test/ELF/aarch64-build-attributes-mixed.s @@ -0,0 +1,62 @@ +// 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-mixed-2.s -o %t12.o +// RUN: llvm-mc -triple=aarch64 -filetype=obj merged-mixed-3.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 + +// 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 + +/// The Build attributes section appearing in the output of +/// llvm-mc should not appear in the output of lld, because +/// AArch64 build attributes are being transformed into .gnu.properties. + +// 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-mixed-2.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: + .long 0xc0000000 // GNU_PROPERTY_AARCH64_FEATURE_1_AND + .long 4 + .long 7 // GNU_PROPERTY_AARCH64_FEATURE_1_BTI, PAC and GCS + .long 0 + // PAuth ABI property note + .long 0xc0000001 // Type: GNU_PROPERTY_AARCH64_FEATURE_PAUTH + .long 16 // Data size + .quad 49 // PAuth ABI platform + .quad 19 // PAuth ABI version + .p2align 3 // Align to 8 byte for 64 bit +end: + +//--- merged-mixed-3.s +.section .note.gnu.property, "a" + .align 4 + .long 4 // namesz + .long 0x10 // descsz + .long 5 // type (NT_GNU_PROPERTY_TYPE_0) + .asciz "GNU" // name (null-terminated) + .align 4 + .long 0xc0000000 // pr_type (GNU_PROPERTY_AARCH64_FEATURE_1_AND) + .long 4 // pr_datasz + .long 7 // pr_data: BTI (1), PAC (2), GCS (4) = 0b111 = 7 + .long 0 // padding or next property diff --git a/lld/test/ELF/aarch64-build-attributes.s b/lld/test/ELF/aarch64-build-attributes.s index 24e15f94e3d4a..55dbadd470279 100644 --- a/lld/test/ELF/aarch64-build-attributes.s +++ b/lld/test/ELF/aarch64-build-attributes.s @@ -1,26 +1,48 @@ -// 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 merged-2.s -o %t2.o +// RUN: llvm-mc -triple=aarch64 -filetype=obj merged-3.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 + +// 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 + +/// The Build attributes section appearing in the output of +/// llvm-mc should not appear in the output of lld, because +/// AArch64 build attributes are being transformed into .gnu.properties. + +// 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 + +//--- merged-2.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 + + +//--- merged-3.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/lld/test/ELF/aarch64-feature-pac-replace.s b/lld/test/ELF/aarch64-feature-pac-replace.s new file mode 100644 index 0000000000000..866cb62d6bce1 --- /dev/null +++ b/lld/test/ELF/aarch64-feature-pac-replace.s @@ -0,0 +1,147 @@ +### This file replace .note.gnu.property with aarch64 build attributes in order to confirm +### interoperability. +### (Still using gnu properties in the helper files) + +# REQUIRES: aarch64 +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %p/Inputs/aarch64-pac1.s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %p/Inputs/aarch64-pac1-replace.s -o %t1-ba.o +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %p/Inputs/aarch64-func3.s -o %t2.o +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %p/Inputs/aarch64-func3-pac.s -o %t3.o +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %p/Inputs/aarch64-func3-pac-replace.s -o %t3-ba.o +# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %p/Inputs/aarch64-func2.s -o %tno.o + +## We do not add PAC support when the inputs don't have the .note.gnu.property +## field. + +# RUN: ld.lld %tno.o %t3.o --shared -o %tno.so +# RUN: ld.lld %tno.o %t3-ba.o --shared -o %tno-ba.so +# RUN: llvm-objdump --no-print-imm-hex -d --mattr=+v8.3a --no-show-raw-insn %tno.so | FileCheck --check-prefix=NOPAC %s +# RUN: llvm-objdump --no-print-imm-hex -d --mattr=+v8.3a --no-show-raw-insn %tno-ba.so | FileCheck --check-prefix=NOPAC %s +# RUN: llvm-readelf -x .got.plt %tno.so | FileCheck --check-prefix SOGOTPLT %s +# RUN: llvm-readelf -x .got.plt %tno-ba.so | FileCheck --check-prefix SOGOTPLT %s +# RUN: llvm-readelf --dynamic-table %tno.so | FileCheck --check-prefix NOPACDYN %s +# RUN: llvm-readelf --dynamic-table %tno-ba.so | FileCheck --check-prefix NOPACDYN %s + +# NOPAC: 00000000000102b8 : +# NOPAC-NEXT: 102b8: bl 0x102f0 +# NOPAC-NEXT: ret +# NOPAC: Disassembly of section .plt: +# NOPAC: 00000000000102d0 <.plt>: +# NOPAC-NEXT: 102d0: stp x16, x30, [sp, #-16]! +# NOPAC-NEXT: adrp x16, 0x30000 +# NOPAC-NEXT: ldr x17, [x16, #960] +# NOPAC-NEXT: add x16, x16, #960 +# NOPAC-NEXT: br x17 +# NOPAC-NEXT: nop +# NOPAC-NEXT: nop +# NOPAC-NEXT: nop +# NOPAC: 00000000000102f0 : +# NOPAC-NEXT: 102f0: adrp x16, 0x30000 +# NOPAC-NEXT: ldr x17, [x16, #968] +# NOPAC-NEXT: add x16, x16, #968 +# NOPAC-NEXT: br x17 + +# SOGOTPLT: Hex dump of section '.got.plt': +# SOGOTPLT-NEXT: 0x000303b0 00000000 00000000 00000000 00000000 +# SOGOTPLT-NEXT: 0x000303c0 00000000 00000000 d0020100 00000000 + +# NOPACDYN-NOT: 0x0000000070000001 (AARCH64_BTI_PLT) +# NOPACDYN-NOT: 0x0000000070000003 (AARCH64_PAC_PLT) + + +# RUN: ld.lld %t1.o %t3.o --shared --soname=t.so -o %t.so +# RUN: ld.lld %t1-ba.o %t3-ba.o --shared --soname=t.so -o %t-ba.so +# RUN: llvm-readelf -n %t.so | FileCheck --check-prefix PACPROP %s +# RUN: llvm-readelf -n %t-ba.so | FileCheck --check-prefix PACPROP %s +# RUN: llvm-objdump --no-print-imm-hex -d --mattr=+v8.3a --no-show-raw-insn %t.so | FileCheck --check-prefix PACSO %s +# RUN: llvm-objdump --no-print-imm-hex -d --mattr=+v8.3a --no-show-raw-insn %t-ba.so | FileCheck --check-prefix PACSO %s +# RUN: llvm-readelf -x .got.plt %t.so | FileCheck --check-prefix SOGOTPLT2 %s +# RUN: llvm-readelf -x .got.plt %t-ba.so | FileCheck --check-prefix SOGOTPLT2 %s +# RUN: llvm-readelf --dynamic-table %t.so | FileCheck --check-prefix PACDYN %s +# RUN: llvm-readelf --dynamic-table %t-ba.so | FileCheck --check-prefix PACDYN %s + +# PACPROP: Properties: aarch64 feature: PAC + +# PACSO: Disassembly of section .text: +# PACSO: 0000000000010348 : +# PACSO-NEXT: 10348: bl 0x10380 +# PACSO-NEXT: 1034c: ret +# PACSO: 0000000000010350 : +# PACSO-NEXT: 10350: ret +# PACSO: Disassembly of section .plt: +# PACSO: 0000000000010360 <.plt>: +# PACSO-NEXT: 10360: stp x16, x30, [sp, #-16]! +# PACSO-NEXT: 10364: adrp x16, 0x30000 +# PACSO-NEXT: 10368: ldr x17, [x16, #1120] +# PACSO-NEXT: 1036c: add x16, x16, #1120 +# PACSO-NEXT: 10370: br x17 +# PACSO-NEXT: 10374: nop +# PACSO-NEXT: 10378: nop +# PACSO-NEXT: 1037c: nop +# PACSO: 0000000000010380 : +# PACSO-NEXT: 10380: adrp x16, 0x30000 +# PACSO-NEXT: 10384: ldr x17, [x16, #1128] +# PACSO-NEXT: 10388: add x16, x16, #1128 +# PACSO-NEXT: 1038c: br x17 + +# SOGOTPLT2: Hex dump of section '.got.plt': +# SOGOTPLT2-NEXT: 0x00030450 00000000 00000000 00000000 00000000 +# SOGOTPLT2-NEXT: 0x00030460 00000000 00000000 60030100 00000000 + +# PACDYN-NOT: 0x0000000070000001 (AARCH64_BTI_PLT) +# PACDYN-NOT: 0x0000000070000003 (AARCH64_PAC_PLT) + + +# RUN: ld.lld %t.o %t2.o -z pac-plt %t.so -o %tpacplt.exe 2>&1 | FileCheck -DFILE=%t2.o --check-prefix WARN %s +# RUN: ld.lld %t.o %t2.o -z pac-plt %t-ba.so -o %tpacplt-ba.exe 2>&1 | FileCheck -DFILE=%t2.o --check-prefix WARN %s + +# WARN: warning: [[FILE]]: -z pac-plt: file does not have GNU_PROPERTY_AARCH64_FEATURE_1_PAC property and no valid PAuth core info present for this link job + + +# RUN: llvm-readelf -n %tpacplt.exe | FileCheck --check-prefix=PACPROP %s +# RUN: llvm-readelf -n %tpacplt-ba.exe | FileCheck --check-prefix=PACPROP %s +# RUN: llvm-readelf --dynamic-table %tpacplt.exe | FileCheck --check-prefix PACDYN2 %s +# RUN: llvm-readelf --dynamic-table %tpacplt-ba.exe | FileCheck --check-prefix PACDYN2 %s +# RUN: llvm-objdump --no-print-imm-hex -d --mattr=+v8.3a --no-show-raw-insn %tpacplt.exe | FileCheck --check-prefix PACPLT %s +# RUN: llvm-objdump --no-print-imm-hex -d --mattr=+v8.3a --no-show-raw-insn %tpacplt-ba.exe | FileCheck --check-prefix PACPLT %s + +# PACDYN2-NOT: 0x0000000070000001 (AARCH64_BTI_PLT) +# PACDYN2: 0x0000000070000003 (AARCH64_PAC_PLT) + +# PACPLT: Disassembly of section .text: +# PACPLT: 0000000000210370 : +# PACPLT-NEXT: 210370: bl 0x2103a0 +# PACPLT-NEXT: ret +# PACPLT: 0000000000210378 : +# PACPLT-NEXT: 210378: ret +# PACPLT: Disassembly of section .plt: +# PACPLT: 0000000000210380 <.plt>: +# PACPLT-NEXT: 210380: stp x16, x30, [sp, #-16]! +# PACPLT-NEXT: adrp x16, 0x230000 +# PACPLT-NEXT: ldr x17, [x16, #1192] +# PACPLT-NEXT: add x16, x16, #1192 +# PACPLT-NEXT: br x17 +# PACPLT-NEXT: nop +# PACPLT-NEXT: nop +# PACPLT-NEXT: nop +# PACPLT: 00000000002103a0 : +# PACPLT-NEXT: 2103a0: adrp x16, 0x230000 +# PACPLT-NEXT: ldr x17, [x16, #1200] +# PACPLT-NEXT: add x16, x16, #1200 +# PACPLT-NEXT: autia1716 +# PACPLT-NEXT: br x17 +# PACPLT-NEXT: nop + + +.aeabi_subsection aeabi_feature_and_bits, optional, uleb128 +.aeabi_attribute Tag_Feature_BTI, 0 +.aeabi_attribute Tag_Feature_PAC, 1 +.aeabi_attribute Tag_Feature_GCS, 0 + +.text +.globl _start +.type func1,%function +func1: + bl func2 + ret diff --git a/llvm/include/llvm/Support/AArch64AttributeParser.h b/llvm/include/llvm/Support/AArch64AttributeParser.h index 823ae180a5c5a..dbdcba8de4e7f 100644 --- a/llvm/include/llvm/Support/AArch64AttributeParser.h +++ b/llvm/include/llvm/Support/AArch64AttributeParser.h @@ -24,6 +24,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/include/llvm/Support/ELFAttributes.h b/llvm/include/llvm/Support/ELFAttributes.h index 6782aec6050ad..0e123f0c1a6ff 100644 --- a/llvm/include/llvm/Support/ELFAttributes.h +++ b/llvm/include/llvm/Support/ELFAttributes.h @@ -35,6 +35,7 @@ struct BuildAttributeItem { BuildAttributeItem(Types Ty, unsigned Tg, unsigned IV, std::string SV) : Type(Ty), Tag(Tg), IntValue(IV), StringValue(std::move(SV)) {} }; + struct BuildAttributeSubSection { StringRef Name; unsigned IsOptional; diff --git a/llvm/lib/Support/AArch64AttributeParser.cpp b/llvm/lib/Support/AArch64AttributeParser.cpp index c675ef2a3b3df..18c10f72c153a 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; +}