Skip to content

Commit d9ec4f9

Browse files
committed
[lld][AArch64][Build Attributes] Add support for AArch64 Build Attributes
This patch enables lld to read AArch64 Build Attributes and convert them into GNU Properties. Changes: - Parses AArch64 Build Attributes from input object files. - Converts known attributes into corresponding GNU Properties. - Merges attributes when linking multiple objects. Spec reference: https://github.com/ARM-software/abi-aa/pull/230/files#r1030
1 parent e2c27fd commit d9ec4f9

13 files changed

+538
-27
lines changed

lld/ELF/InputFiles.cpp

Lines changed: 103 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "llvm/ADT/STLExtras.h"
2222
#include "llvm/LTO/LTO.h"
2323
#include "llvm/Object/IRObjectFile.h"
24+
#include "llvm/Support/AArch64AttributeParser.h"
2425
#include "llvm/Support/ARMAttributeParser.h"
2526
#include "llvm/Support/ARMBuildAttributes.h"
2627
#include "llvm/Support/Endian.h"
@@ -537,6 +538,52 @@ uint32_t ObjFile<ELFT>::getSectionIndex(const Elf_Sym &sym) const {
537538
this);
538539
}
539540

541+
template <class ELFT>
542+
static void
543+
handleAArch64BAAndGnuProperties(ObjFile<ELFT> *file, Ctx &ctx, bool hasGP,
544+
const AArch64BuildAttrSubsections &baInfo,
545+
const GnuPropertiesInfo &gpInfo) {
546+
if (hasGP) {
547+
// Check for data mismatch
548+
if (gpInfo.pauthAbiCoreInfo) {
549+
if (baInfo.Pauth.TagPlatform != gpInfo.pauthAbiCoreInfo->platform ||
550+
baInfo.Pauth.TagSchema != gpInfo.pauthAbiCoreInfo->version)
551+
Err(ctx)
552+
<< file
553+
<< " Pauth Data mismatch: file contains both GNU properties and "
554+
"AArch64 build attributes sections with different Pauth data";
555+
}
556+
if (baInfo.AndFeatures != gpInfo.andFeatures)
557+
Err(ctx) << file
558+
<< " Features Data mismatch: file contains both GNU "
559+
"properties and AArch64 build attributes sections with "
560+
"different And Features data";
561+
} else {
562+
// Write missing data
563+
// We can only know when Pauth is missing.
564+
// Unlike AArch64 Build Attributes, GNU properties does not give a way to
565+
// distinguish between no-value given to value of '0' given.
566+
if (baInfo.Pauth.TagPlatform || baInfo.Pauth.TagSchema) {
567+
// According to the BuildAttributes specification Build Attributes
568+
// default to a value of 0 when not present. A (TagPlatform, TagSchema) of
569+
// (0, 0) maps to 'no PAuth property present'. A (TagPlatform, TagSchema)
570+
// of (0, 1) maps to an explicit PAuth property of platform = 0, version =
571+
// 0 ('Invalid').
572+
if (baInfo.Pauth.TagPlatform == 0 && baInfo.Pauth.TagSchema == 1) {
573+
file->aarch64PauthAbiCoreInfo = {0, 0};
574+
} else {
575+
file->aarch64PauthAbiCoreInfo = {baInfo.Pauth.TagPlatform,
576+
baInfo.Pauth.TagSchema};
577+
}
578+
}
579+
file->andFeatures = baInfo.AndFeatures;
580+
}
581+
}
582+
583+
template <typename ELFT>
584+
static GnuPropertiesInfo readGnuProperty(Ctx &, const InputSection &,
585+
ObjFile<ELFT> &);
586+
540587
template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
541588
object::ELFFile<ELFT> obj = this->getObj();
542589
// Read a section table. justSymbols is usually false.
@@ -552,8 +599,31 @@ template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
552599
StringRef shstrtab = CHECK2(obj.getSectionStringTable(objSections), this);
553600
uint64_t size = objSections.size();
554601
sections.resize(size);
602+
603+
// For handling AArch64 Build attributes and GNU properties
604+
AArch64BuildAttrSubsections aarch64BAsubSections;
605+
GnuPropertiesInfo gnuProperty;
606+
bool hasAArch64BuildAttributes = false;
607+
bool hasGNUProperties = false;
608+
555609
for (size_t i = 0; i != size; ++i) {
556610
const Elf_Shdr &sec = objSections[i];
611+
// Object files that use processor features such as Intel Control-Flow
612+
// Enforcement (CET) or AArch64 Branch Target Identification BTI, use a
613+
// .note.gnu.property section containing a bitfield of feature bits like the
614+
// GNU_PROPERTY_X86_FEATURE_1_IBT flag. Read a bitmap containing the flag.
615+
if (check(obj.getSectionName(sec, shstrtab)) == ".note.gnu.property") {
616+
gnuProperty = readGnuProperty(
617+
ctx,
618+
InputSection(*this, sec, check(obj.getSectionName(sec, shstrtab))),
619+
*this);
620+
hasGNUProperties = true;
621+
// Since we merge bitmaps from multiple object files to create a new
622+
// .note.gnu.property containing a single AND'ed bitmap, we discard an
623+
// input file's .note.gnu.property section.
624+
sections[i] = &InputSection::discarded;
625+
}
626+
557627
if (LLVM_LIKELY(sec.sh_type == SHT_PROGBITS))
558628
continue;
559629
if (LLVM_LIKELY(sec.sh_type == SHT_GROUP)) {
@@ -637,13 +707,27 @@ template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
637707
}
638708
break;
639709
case EM_AARCH64:
640-
// FIXME: BuildAttributes have been implemented in llvm, but not yet in
641-
// lld. Remove the section so that it does not accumulate in the output
642-
// file. When support is implemented we expect not to output a build
643-
// attributes section in files of type ET_EXEC or ET_SHARED, but ld -r
644-
// ouptut will need a single merged attributes section.
645-
if (sec.sh_type == SHT_AARCH64_ATTRIBUTES)
710+
// At this stage AArch64 Build Attributes does not replace GNU Properties.
711+
// When both exists, their values must match.
712+
// When both exists and contain different attributes, they complement each
713+
// other. Currently attributes are represented in the linked object file
714+
// as GNU properties, which are already supported by the Linux kernel and
715+
// the dynamic loader. In the future, when relocatable linking (`-r` flag)
716+
// is performed, a single merged AArch64 Build Attributes section will be
717+
// emitted.
718+
if (sec.sh_type == SHT_AARCH64_ATTRIBUTES) {
719+
ArrayRef<uint8_t> contents = check(obj.getSectionContents(sec));
720+
AArch64AttributeParser attributes;
721+
StringRef name = check(obj.getSectionName(sec, shstrtab));
722+
InputSection isec(*this, sec, name);
723+
if (Error e = attributes.parse(contents, ELFT::Endianness)) {
724+
Warn(ctx) << &isec << ": " << std::move(e);
725+
} else {
726+
aarch64BAsubSections = extractBuildAttributesSubsections(attributes);
727+
hasAArch64BuildAttributes = true;
728+
}
646729
sections[i] = &InputSection::discarded;
730+
}
647731
// Producing a static binary with MTE globals is not currently supported,
648732
// remove all SHT_AARCH64_MEMTAG_GLOBALS_STATIC sections as they're unused
649733
// medatada, and we don't want them to end up in the output file for
@@ -655,6 +739,14 @@ template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
655739
}
656740
}
657741

742+
if (hasAArch64BuildAttributes) {
743+
// Handle AArch64 Build Attributes and GNU properties:
744+
// - Err on mismatched values.
745+
// - Store missing values as GNU properties.
746+
handleAArch64BAAndGnuProperties<ELFT>(this, ctx, hasGNUProperties,
747+
aarch64BAsubSections, gnuProperty);
748+
}
749+
658750
// Read a symbol table.
659751
initializeSymbols(obj);
660752
}
@@ -974,8 +1066,8 @@ static void parseGnuPropertyNote(Ctx &ctx, ELFFileBase &f,
9741066
// hardware-assisted call flow control;
9751067
// - AArch64 PAuth ABI core info (16 bytes).
9761068
template <class ELFT>
977-
static void readGnuProperty(Ctx &ctx, const InputSection &sec,
978-
ObjFile<ELFT> &f) {
1069+
static GnuPropertiesInfo readGnuProperty(Ctx &ctx, const InputSection &sec,
1070+
ObjFile<ELFT> &f) {
9791071
using Elf_Nhdr = typename ELFT::Nhdr;
9801072
using Elf_Note = typename ELFT::Note;
9811073

@@ -992,7 +1084,7 @@ static void readGnuProperty(Ctx &ctx, const InputSection &sec,
9921084
featureAndType = GNU_PROPERTY_RISCV_FEATURE_1_AND;
9931085
break;
9941086
default:
995-
return;
1087+
return GnuPropertiesInfo{};
9961088
}
9971089

9981090
ArrayRef<uint8_t> data = sec.content();
@@ -1007,7 +1099,7 @@ static void readGnuProperty(Ctx &ctx, const InputSection &sec,
10071099
auto *nhdr = reinterpret_cast<const Elf_Nhdr *>(data.data());
10081100
if (data.size() < sizeof(Elf_Nhdr) ||
10091101
data.size() < nhdr->getSize(sec.addralign))
1010-
return void(err(data.data()) << "data is too short");
1102+
return (err(data.data()) << "data is too short", GnuPropertiesInfo{});
10111103

10121104
Elf_Note note(*nhdr);
10131105
if (nhdr->n_type != NT_GNU_PROPERTY_TYPE_0 || note.getName() != "GNU") {
@@ -1023,6 +1115,7 @@ static void readGnuProperty(Ctx &ctx, const InputSection &sec,
10231115
// Go to next NOTE record to look for more FEATURE_1_AND descriptions.
10241116
data = data.slice(nhdr->getSize(sec.addralign));
10251117
}
1118+
return GnuPropertiesInfo{f.andFeatures, f.aarch64PauthAbiCoreInfo};
10261119
}
10271120

10281121
template <class ELFT>

lld/ELF/InputFiles.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,11 @@ class ELFFileBase : public InputFile {
244244
std::optional<AArch64PauthAbiCoreInfo> aarch64PauthAbiCoreInfo;
245245
};
246246

247+
struct GnuPropertiesInfo {
248+
uint32_t andFeatures = 0;
249+
std::optional<AArch64PauthAbiCoreInfo> pauthAbiCoreInfo;
250+
};
251+
247252
// .o file.
248253
template <class ELFT> class ObjFile : public ELFFileBase {
249254
LLVM_ELF_IMPORT_TYPES_ELFT(ELFT)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Declare file properties exclusively with aarch64 build attributes.
2+
3+
.aeabi_subsection aeabi_feature_and_bits, optional, uleb128
4+
.aeabi_attribute Tag_Feature_PAC, 1
5+
6+
.text
7+
.globl func3
8+
.type func3,@function
9+
func3:
10+
ret
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// This file replace gnu properties with aarch64 build attributes.
2+
3+
.aeabi_subsection aeabi_feature_and_bits, optional, uleb128
4+
.aeabi_attribute Tag_Feature_PAC, 1
5+
6+
.text
7+
.globl func2
8+
.type func2,@function
9+
func2:
10+
.globl func3
11+
.type func3, @function
12+
bl func3
13+
ret
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// REQUIRES: aarch64
2+
// RUN: llvm-mc -triple=aarch64_be %s -filetype=obj -o %t.o
3+
// RUN: ld.lld %t.o --shared -o %t.so
4+
// RUN: llvm-readelf -n %t.so | FileCheck %s --check-prefix=NOTE
5+
6+
/// The Build attributes section appearing in the output of
7+
/// llvm-mc should not appear in the output of lld, because
8+
/// AArch64 build attributes are being transformed into .gnu.properties.
9+
10+
/// Test mc -> big endian, lld -> little endian
11+
// RUN: llvm-mc -triple=aarch64_be %s -filetype=obj -o %t.o
12+
// RUN: ld.lld %t.o --shared -o %t.so
13+
// RUN: llvm-readelf -n %t.so | FileCheck %s --check-prefix=NOTE
14+
// RUN: ld.lld %t.o -o %t
15+
// RUN: llvm-readelf -n %t.so | FileCheck %s --check-prefix=NOTE
16+
// RUN: ld.lld -r %t.o -o %t2.o
17+
// RUN: llvm-readelf -n %t.so | FileCheck %s --check-prefix=NOTE
18+
19+
/// Test mc -> little endian, lld -> big endian
20+
// RUN: llvm-mc -triple=aarch64 %s -filetype=obj -o %t.o
21+
// RUN: ld.lld --EB %t.o --shared -o %t.so
22+
// RUN: llvm-readelf -n %t.so | FileCheck %s --check-prefix=NOTE
23+
// RUN: ld.lld --EB %t.o -o %t
24+
// RUN: llvm-readelf -n %t.so | FileCheck %s --check-prefix=NOTE
25+
// RUN: ld.lld --EB -r %t.o -o %t2.o
26+
// RUN: llvm-readelf -n %t.so | FileCheck %s --check-prefix=NOTE
27+
28+
/// Test mc -> big endian, lld -> big endian
29+
// RUN: llvm-mc -triple=aarch64_be %s -filetype=obj -o %t.o
30+
// RUN: ld.lld --EB %t.o --shared -o %t.so
31+
// RUN: llvm-readelf -n %t.so | FileCheck %s --check-prefix=NOTE
32+
// RUN: ld.lld --EB %t.o -o %t
33+
// RUN: llvm-readelf -n %t.so | FileCheck %s --check-prefix=NOTE
34+
// RUN: ld.lld --EB -r %t.o -o %t2.o
35+
// RUN: llvm-readelf -n %t.so | FileCheck %s --check-prefix=NOTE
36+
37+
// NOTE: Displaying notes found in: .note.gnu.property
38+
// NOTE-NEXT: Owner Data size Description
39+
// NOTE-NEXT: GNU 0x00000028 NT_GNU_PROPERTY_TYPE_0 (property note)
40+
// NOTE-NEXT: Properties: aarch64 feature: BTI, PAC, GCS
41+
// NOTE-NEXT: AArch64 PAuth ABI core info: platform 0x89abcdef (unknown), version 0x89abcdef
42+
43+
44+
.aeabi_subsection aeabi_pauthabi, required, uleb128
45+
.aeabi_attribute Tag_PAuth_Platform, 81985529216486895
46+
.aeabi_attribute Tag_PAuth_Schema, 81985529216486895
47+
.aeabi_subsection aeabi_feature_and_bits, optional, uleb128
48+
.aeabi_attribute Tag_Feature_BTI, 1
49+
.aeabi_attribute Tag_Feature_PAC, 1
50+
.aeabi_attribute Tag_Feature_GCS, 1
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// REQUIRES: aarch64
2+
3+
// RUN: llvm-mc -triple=aarch64 %s -filetype=obj -o %t.o
4+
// RUN: not ld.lld %t.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR
5+
6+
// ERR: Pauth Data mismatch: file contains both GNU properties and AArch64 build attributes sections with different Pauth data
7+
// ERR-NEXT: Features Data mismatch: file contains both GNU properties and AArch64 build attributes sections with different And Features data
8+
9+
.aeabi_subsection aeabi_pauthabi, required, uleb128
10+
.aeabi_attribute Tag_PAuth_Platform, 5
11+
.aeabi_attribute Tag_PAuth_Schema, 5
12+
.aeabi_subsection aeabi_feature_and_bits, optional, uleb128
13+
.aeabi_attribute Tag_Feature_BTI, 1
14+
.aeabi_attribute Tag_Feature_PAC, 1
15+
.aeabi_attribute Tag_Feature_GCS, 1
16+
17+
.section ".note.gnu.property", "a"
18+
.long 4
19+
.long 0x10
20+
.long 0x5
21+
.asciz "GNU"
22+
.long 0xc0000000 // GNU_PROPERTY_AARCH64_FEATURE_1_AND
23+
.long 4
24+
.long 2 // GNU_PROPERTY_AARCH64_FEATURE_1_PAC
25+
.long 0
26+
27+
.section ".note.gnu.property", "a"
28+
.long 4
29+
.long 24
30+
.long 5
31+
.asciz "GNU"
32+
.long 0xc0000001
33+
.long 16
34+
.quad 305419896 // platform
35+
.quad 2271560481 // version
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// REQUIRES: aarch64
2+
3+
// RUN: llvm-mc -triple=aarch64 -filetype=obj %s -o %t.o
4+
// RUN: ld.lld -r %t.o -o %t.invalid.o
5+
// RUN: llvm-readelf -n %t.invalid.o | FileCheck %s
6+
7+
/// According to the BuildAttributes specification Build Attributes
8+
/// A (TagPlatform, TagSchema)of (0, 1) maps to an explicit PAuth property
9+
/// of platform = 0, version = 0 ('Invalid').
10+
11+
// CHECK: Displaying notes found in: .note.gnu.property
12+
// CHECK-NEXT: Owner Data size Description
13+
// CHECK-NEXT: GNU 0x00000018 NT_GNU_PROPERTY_TYPE_0 (property note)
14+
// CHECK-NEXT: Properties: AArch64 PAuth ABI core info: platform 0x0 (invalid), version 0x0
15+
16+
.aeabi_subsection aeabi_pauthabi, required, uleb128
17+
.aeabi_attribute Tag_PAuth_Platform, 0
18+
.aeabi_attribute Tag_PAuth_Schema, 1
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# RUN: llvm-mc -triple=aarch64 -filetype=obj %s -o %t.o
2+
# RUN: ld.lld %t.o /dev/null 2>&1 | FileCheck %s
3+
4+
# CHECK: (.ARM.attributes): unexpected end of data at offset 0x3f while reading [0x3d, 0x41)
5+
6+
.section .ARM.attributes,"",%0x70000003
7+
.byte 0x41 // Tag 'A' (format version)
8+
.long 0x00000019 // Subsection length
9+
.asciz "aeabi_pauthabi" // Subsection name
10+
.byte 0x00, 0x00 // Optionality and Type
11+
.byte 0x01, 0x01, 0x02, 0x01 // PAuth_Platform and PAuth_Schema
12+
.long 0x00000023 // Subsection length
13+
.asciz "aeabi_feature_and_bits" // Subsection name
14+
.byte 0x01, 0x00 // Optionality and Type
15+
.byte 0x00, 0x01, 0x01, 0x01, 0x02, 0x01 // BTI, PAC, GCS
16+
.byte 0x00, 0x00 // This is the malformation, data is too long.

0 commit comments

Comments
 (0)