Skip to content

Commit 80268ef

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.
1 parent 5ff6e9e commit 80268ef

12 files changed

+509
-26
lines changed

lld/ELF/InputFiles.cpp

Lines changed: 92 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "llvm/ADT/STLExtras.h"
2323
#include "llvm/LTO/LTO.h"
2424
#include "llvm/Object/IRObjectFile.h"
25+
#include "llvm/Support/AArch64AttributeParser.h"
2526
#include "llvm/Support/ARMAttributeParser.h"
2627
#include "llvm/Support/ARMBuildAttributes.h"
2728
#include "llvm/Support/Endian.h"
@@ -539,6 +540,42 @@ uint32_t ObjFile<ELFT>::getSectionIndex(const Elf_Sym &sym) const {
539540
this);
540541
}
541542

543+
template <class ELFT>
544+
static void
545+
handleAArch64BAAndGnuProperties(ObjFile<ELFT> *file, Ctx &ctx, bool hasGP,
546+
const AArch64BuildAttrSubsections &baInfo,
547+
const GnuPropertiesInfo &gpInfo) {
548+
if (hasGP) {
549+
// Check for data mismatch
550+
if (gpInfo.pauthAbiCoreInfo) {
551+
if (baInfo.Pauth.TagPlatform != gpInfo.pauthAbiCoreInfo->platform)
552+
Err(ctx)
553+
<< file
554+
<< " Pauth Data mismatch: file contains both GNU properties and "
555+
"AArch64 build attributes sections with different Pauth data";
556+
}
557+
if (baInfo.AndFeatures != gpInfo.andFeatures)
558+
Err(ctx) << file
559+
<< " Features Data mismatch: file contains both GNU "
560+
"properties and AArch64 build attributes sections with "
561+
"different And Features data";
562+
} else {
563+
// Write missing data
564+
// We can only know when Pauth is missing.
565+
// Unlike AArch64 Build Attributes, GNU properties does not give a way to
566+
// distinguish between no-value given to value of '0' given.
567+
if (baInfo.Pauth.TagPlatform || baInfo.Pauth.TagSchema) {
568+
file->aarch64PauthAbiCoreInfo = {baInfo.Pauth.TagPlatform,
569+
baInfo.Pauth.TagSchema};
570+
}
571+
file->andFeatures = baInfo.AndFeatures;
572+
}
573+
}
574+
575+
template <typename ELFT>
576+
static GnuPropertiesInfo readGnuProperty(Ctx &, const InputSection &,
577+
ObjFile<ELFT> &);
578+
542579
template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
543580
object::ELFFile<ELFT> obj = this->getObj();
544581
// Read a section table. justSymbols is usually false.
@@ -554,8 +591,31 @@ template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
554591
StringRef shstrtab = CHECK2(obj.getSectionStringTable(objSections), this);
555592
uint64_t size = objSections.size();
556593
sections.resize(size);
594+
595+
// For handling AArch64 Build attributes and GNU properties
596+
AArch64BuildAttrSubsections aarch64BAsubSections;
597+
GnuPropertiesInfo gnuProperty;
598+
bool hasAArch64BuildAttributes = false;
599+
bool hasGNUProperties = false;
600+
557601
for (size_t i = 0; i != size; ++i) {
558602
const Elf_Shdr &sec = objSections[i];
603+
// Object files that use processor features such as Intel Control-Flow
604+
// Enforcement (CET) or AArch64 Branch Target Identification BTI, use a
605+
// .note.gnu.property section containing a bitfield of feature bits like the
606+
// GNU_PROPERTY_X86_FEATURE_1_IBT flag. Read a bitmap containing the flag.
607+
if (check(obj.getSectionName(sec, shstrtab)) == ".note.gnu.property") {
608+
gnuProperty = readGnuProperty(
609+
ctx,
610+
InputSection(*this, sec, check(obj.getSectionName(sec, shstrtab))),
611+
*this);
612+
hasGNUProperties = true;
613+
// Since we merge bitmaps from multiple object files to create a new
614+
// .note.gnu.property containing a single AND'ed bitmap, we discard an
615+
// input file's .note.gnu.property section.
616+
sections[i] = &InputSection::discarded;
617+
}
618+
559619
if (LLVM_LIKELY(sec.sh_type == SHT_PROGBITS))
560620
continue;
561621
if (LLVM_LIKELY(sec.sh_type == SHT_GROUP)) {
@@ -639,13 +699,27 @@ template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
639699
}
640700
break;
641701
case EM_AARCH64:
642-
// FIXME: BuildAttributes have been implemented in llvm, but not yet in
643-
// lld. Remove the section so that it does not accumulate in the output
644-
// file. When support is implemented we expect not to output a build
645-
// attributes section in files of type ET_EXEC or ET_SHARED, but ld -r
646-
// ouptut will need a single merged attributes section.
647-
if (sec.sh_type == SHT_AARCH64_ATTRIBUTES)
702+
// At this stage AArch64 Build Attributes does not replace GNU Properties.
703+
// When both exists, their values must match.
704+
// When both exists and contain different attributes, they complement each
705+
// other. Currently attributes are represented in the linked object file
706+
// as GNU properties, which are already supported by the Linux kernel and
707+
// the dynamic loader. In the future, when relocatable linking (`-r` flag)
708+
// is performed, a single merged AArch64 Build Attributes section will be
709+
// emitted.
710+
if (sec.sh_type == SHT_AARCH64_ATTRIBUTES) {
711+
ArrayRef<uint8_t> contents = check(obj.getSectionContents(sec));
712+
AArch64AttributeParser attributes;
713+
StringRef name = check(obj.getSectionName(sec, shstrtab));
714+
InputSection isec(*this, sec, name);
715+
if (Error e = attributes.parse(contents, ELFT::Endianness)) {
716+
Warn(ctx) << &isec << ": " << std::move(e);
717+
} else {
718+
aarch64BAsubSections = extractBuildAttributesSubsections(attributes);
719+
hasAArch64BuildAttributes = true;
720+
}
648721
sections[i] = &InputSection::discarded;
722+
}
649723
// Producing a static binary with MTE globals is not currently supported,
650724
// remove all SHT_AARCH64_MEMTAG_GLOBALS_STATIC sections as they're unused
651725
// medatada, and we don't want them to end up in the output file for
@@ -657,6 +731,14 @@ template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
657731
}
658732
}
659733

734+
if (hasAArch64BuildAttributes) {
735+
// Handle AArch64 Build Attributes and GNU properties:
736+
// - Err on mismatched values.
737+
// - Store missing values as GNU properties.
738+
handleAArch64BAAndGnuProperties<ELFT>(this, ctx, hasGNUProperties,
739+
aarch64BAsubSections, gnuProperty);
740+
}
741+
660742
// Read a symbol table.
661743
initializeSymbols(obj);
662744
}
@@ -976,8 +1058,8 @@ static void parseGnuPropertyNote(Ctx &ctx, ELFFileBase &f,
9761058
// hardware-assisted call flow control;
9771059
// - AArch64 PAuth ABI core info (16 bytes).
9781060
template <class ELFT>
979-
static void readGnuProperty(Ctx &ctx, const InputSection &sec,
980-
ObjFile<ELFT> &f) {
1061+
static GnuPropertiesInfo readGnuProperty(Ctx &ctx, const InputSection &sec,
1062+
ObjFile<ELFT> &f) {
9811063
using Elf_Nhdr = typename ELFT::Nhdr;
9821064
using Elf_Note = typename ELFT::Note;
9831065

@@ -993,7 +1075,7 @@ static void readGnuProperty(Ctx &ctx, const InputSection &sec,
9931075
auto *nhdr = reinterpret_cast<const Elf_Nhdr *>(data.data());
9941076
if (data.size() < sizeof(Elf_Nhdr) ||
9951077
data.size() < nhdr->getSize(sec.addralign))
996-
return void(err(data.data()) << "data is too short");
1078+
return (err(data.data()) << "data is too short", GnuPropertiesInfo{});
9971079

9981080
Elf_Note note(*nhdr);
9991081
if (nhdr->n_type != NT_GNU_PROPERTY_TYPE_0 || note.getName() != "GNU") {
@@ -1013,6 +1095,7 @@ static void readGnuProperty(Ctx &ctx, const InputSection &sec,
10131095
// Go to next NOTE record to look for more FEATURE_1_AND descriptions.
10141096
data = data.slice(nhdr->getSize(sec.addralign));
10151097
}
1098+
return GnuPropertiesInfo{f.andFeatures, f.aarch64PauthAbiCoreInfo};
10161099
}
10171100

10181101
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: 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.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// RUN: rm -rf %t && split-file %s %t && cd %t
2+
3+
// RUN: llvm-mc -triple=aarch64 -filetype=obj %s -o %t11.o
4+
// RUN: llvm-mc -triple=aarch64 -filetype=obj merged-mixed-2.s -o %t12.o
5+
// RUN: llvm-mc -triple=aarch64 -filetype=obj merged-mixed-3.s -o %t13.o
6+
// RUN: ld.lld -r %t11.o %t12.o %t13.o -o %t.merged1.o
7+
// RUN: llvm-readelf -n %t.merged1.o | FileCheck %s --check-prefix=NOTE-MIXED
8+
9+
// NOTE-MIXED: Displaying notes found in: .note.gnu.property
10+
// NOTE-MIXED-NEXT: Owner Data size Description
11+
// NOTE-MIXED-NEXT: GNU 0x00000028 NT_GNU_PROPERTY_TYPE_0 (property note)
12+
// NOTE-MIXED-NEXT: Properties: aarch64 feature: BTI, PAC
13+
// NOTE-MIXED-NEXT: AArch64 PAuth ABI core info: platform 0x31 (unknown), version 0x13
14+
15+
/// The Build attributes section appearing in the output of
16+
/// llvm-mc should not appear in the output of lld, because
17+
/// AArch64 build attributes are being transformed into .gnu.properties.
18+
19+
// CHECK: .note.gnu.property
20+
// CHECK-NOT: .ARM.attributes
21+
22+
.aeabi_subsection aeabi_pauthabi, required, uleb128
23+
.aeabi_attribute Tag_PAuth_Platform, 49
24+
.aeabi_attribute Tag_PAuth_Schema, 19
25+
.aeabi_subsection aeabi_feature_and_bits, optional, uleb128
26+
.aeabi_attribute Tag_Feature_BTI, 1
27+
.aeabi_attribute Tag_Feature_PAC, 1
28+
.aeabi_attribute Tag_Feature_GCS, 1
29+
30+
31+
//--- merged-mixed-2.s
32+
.section ".note.gnu.property", "a"
33+
.long 4 // Name length is always 4 ("GNU")
34+
.long end - begin // Data length
35+
.long 5 // Type: NT_GNU_PROPERTY_TYPE_0
36+
.asciz "GNU" // Name
37+
.p2align 3
38+
begin:
39+
.long 0xc0000000 // GNU_PROPERTY_AARCH64_FEATURE_1_AND
40+
.long 4
41+
.long 7 // GNU_PROPERTY_AARCH64_FEATURE_1_BTI, PAC and GCS
42+
.long 0
43+
// PAuth ABI property note
44+
.long 0xc0000001 // Type: GNU_PROPERTY_AARCH64_FEATURE_PAUTH
45+
.long 16 // Data size
46+
.quad 49 // PAuth ABI platform
47+
.quad 19 // PAuth ABI version
48+
.p2align 3 // Align to 8 byte for 64 bit
49+
end:
50+
51+
//--- merged-mixed-3.s
52+
.section .note.gnu.property, "a"
53+
.align 4
54+
.long 4 // namesz
55+
.long 0x10 // descsz
56+
.long 5 // type (NT_GNU_PROPERTY_TYPE_0)
57+
.asciz "GNU" // name (null-terminated)
58+
.align 4
59+
.long 0xc0000000 // pr_type (GNU_PROPERTY_AARCH64_FEATURE_1_AND)
60+
.long 4 // pr_datasz
61+
.long 7 // pr_data: BTI (1), PAC (2), GCS (4) = 0b111 = 7
62+
.long 0 // padding or next property

0 commit comments

Comments
 (0)