Skip to content

Commit e4ff3bf

Browse files
committed
[AArch64][PAC][lld][ELF] Support GNU_PROPERTY_AARCH64_FEATURE_PAUTH
- In new PAuth ABI version, support for extra data (except platform and version) is dropped. Change container for holding the tag from `SmallVector<uint8_t>` to `std::optional<std::array<uint8_t, 16>>`. - In new PAuth ABI version, the GNU property section becomes the main way of ELF marking. Support for alternative way is temporarily disabled in this commit unless the corresponding mainline PR and ARM PAuth ABI PR are merged.
1 parent a3ddb4e commit e4ff3bf

9 files changed

+202
-47
lines changed

lld/ELF/Config.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "llvm/Support/FileSystem.h"
2828
#include "llvm/Support/GlobPattern.h"
2929
#include "llvm/Support/PrettyStackTrace.h"
30+
#include <array>
3031
#include <atomic>
3132
#include <memory>
3233
#include <optional>
@@ -492,7 +493,7 @@ struct Ctx {
492493

493494
llvm::raw_fd_ostream openAuxiliaryFile(llvm::StringRef, std::error_code &);
494495

495-
SmallVector<uint8_t, 0> aarch64PauthAbiTag;
496+
std::optional<std::array<uint8_t, 16>> aarch64PauthAbiTag;
496497
};
497498

498499
LLVM_LIBRARY_VISIBILITY extern Ctx ctx;

lld/ELF/Driver.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2662,17 +2662,17 @@ static void getAarch64PauthInfo() {
26622662

26632663
auto NonEmptyIt = std::find_if(
26642664
ctx.objectFiles.begin(), ctx.objectFiles.end(),
2665-
[](const ELFFileBase *f) { return !f->aarch64PauthAbiTag.empty(); });
2665+
[](const ELFFileBase *f) { return f->aarch64PauthAbiTag.has_value(); });
26662666
if (NonEmptyIt == ctx.objectFiles.end())
26672667
return;
26682668

26692669
ctx.aarch64PauthAbiTag = (*NonEmptyIt)->aarch64PauthAbiTag;
26702670
StringRef F1 = (*NonEmptyIt)->getName();
26712671
for (ELFFileBase *F : ArrayRef(ctx.objectFiles)) {
26722672
StringRef F2 = F->getName();
2673-
const SmallVector<uint8_t, 0> &D1 = ctx.aarch64PauthAbiTag;
2674-
const SmallVector<uint8_t, 0> &D2 = F->aarch64PauthAbiTag;
2675-
if (D1.empty() != D2.empty()) {
2673+
std::optional<std::array<uint8_t, 16>> D1 = ctx.aarch64PauthAbiTag;
2674+
std::optional<std::array<uint8_t, 16>> D2 = F->aarch64PauthAbiTag;
2675+
if (D1.has_value() != D2.has_value()) {
26762676
auto Helper = [](StringRef Report, const Twine &Msg) {
26772677
if (Report == "warning")
26782678
warn(Msg);
@@ -2681,19 +2681,19 @@ static void getAarch64PauthInfo() {
26812681
};
26822682

26832683
Helper(config->zPauthReport,
2684-
(D1.empty() ? F1.str() : F2.str()) +
2684+
(D1.has_value() ? F2.str() : F1.str()) +
26852685
" has no AArch64 PAuth compatibility info while " +
2686-
(D1.empty() ? F2.str() : F1.str()) +
2686+
(D1.has_value() ? F1.str() : F2.str()) +
26872687
" has one; either all or no input files must have it");
26882688
}
26892689

2690-
if (!D1.empty() && !D2.empty() &&
2691-
!std::equal(D1.begin(), D1.end(), D2.begin(), D2.end()))
2690+
if (D1.has_value() && D2.has_value() &&
2691+
!std::equal(D1->begin(), D1->end(), D2->begin(), D2->end()))
26922692
errorOrWarn(
26932693
"incompatible values of AArch64 PAuth compatibility info found"
26942694
"\n" +
2695-
F1 + ": 0x" + toHex(ArrayRef(D1.data(), D1.size())) + "\n" + F2 +
2696-
": 0x" + toHex(ArrayRef(D2.data(), D2.size())));
2695+
F1 + ": 0x" + toHex(ArrayRef(D1->data(), D1->size())) + "\n" + F2 +
2696+
": 0x" + toHex(ArrayRef(D2->data(), D2->size())));
26972697
}
26982698
}
26992699

lld/ELF/InputFiles.cpp

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
#include "llvm/Support/TarWriter.h"
3232
#include "llvm/Support/raw_ostream.h"
3333

34+
#include <tuple>
35+
3436
using namespace llvm;
3537
using namespace llvm::ELF;
3638
using namespace llvm::object;
@@ -878,11 +880,14 @@ void ObjFile<ELFT>::initializeSections(bool ignoreComdats,
878880
// of zero or more type-length-value fields. We want to find a field of a
879881
// certain type. It seems a bit too much to just store a 32-bit value, perhaps
880882
// the ABI is unnecessarily complicated.
881-
template <class ELFT> static uint32_t readAndFeatures(const InputSection &sec) {
883+
template <class ELFT>
884+
static std::pair<uint32_t, std::optional<std::array<uint8_t, 16>>>
885+
readGnuProperty(const InputSection &sec) {
882886
using Elf_Nhdr = typename ELFT::Nhdr;
883887
using Elf_Note = typename ELFT::Note;
884888

885889
uint32_t featuresSet = 0;
890+
std::optional<std::array<uint8_t, 16>> aarch64PauthAbiTag;
886891
ArrayRef<uint8_t> data = sec.content();
887892
auto reportFatal = [&](const uint8_t *place, const char *msg) {
888893
fatal(toString(sec.file) + ":(" + sec.name + "+0x" +
@@ -924,6 +929,19 @@ template <class ELFT> static uint32_t readAndFeatures(const InputSection &sec) {
924929
if (size < 4)
925930
reportFatal(place, "FEATURE_1_AND entry is too short");
926931
featuresSet |= read32<ELFT::TargetEndianness>(desc.data());
932+
} else if (config->emachine == EM_AARCH64 &&
933+
type == GNU_PROPERTY_AARCH64_FEATURE_PAUTH) {
934+
if (aarch64PauthAbiTag != std::nullopt)
935+
reportFatal(data.data(),
936+
"multiple GNU_PROPERTY_AARCH64_FEATURE_PAUTH properties "
937+
"are not allowed");
938+
if (size != 16)
939+
reportFatal(
940+
data.data(),
941+
"size of GNU_PROPERTY_AARCH64_FEATURE_PAUTH property must be 16");
942+
aarch64PauthAbiTag = std::array<uint8_t, 16>{};
943+
memcpy(aarch64PauthAbiTag->data(), desc.data(),
944+
aarch64PauthAbiTag->size());
927945
}
928946

929947
// Padding is present in the note descriptor, if necessary.
@@ -934,7 +952,7 @@ template <class ELFT> static uint32_t readAndFeatures(const InputSection &sec) {
934952
data = data.slice(nhdr->getSize(sec.addralign));
935953
}
936954

937-
return featuresSet;
955+
return {featuresSet, aarch64PauthAbiTag};
938956
}
939957

940958
// Extract compatibility info for aarch64 pointer authentication from the
@@ -966,13 +984,15 @@ static void readAArch64PauthAbiTag(const InputSection &sec, ObjFile<ELFT> &f) {
966984
" (ARM expected)");
967985

968986
ArrayRef<uint8_t> desc = note.getDesc(sec.addralign);
969-
if (desc.size() < 16) {
970-
reportError("too short AArch64 PAuth compatibility info "
971-
"(at least 16 bytes expected)");
987+
if (desc.size() != 16) {
988+
reportError("invalid AArch64 PAuth compatibility info length "
989+
"(exactly 16 bytes expected)");
972990
return;
973991
}
974992

975-
f.aarch64PauthAbiTag = SmallVector<uint8_t, 0>(iterator_range(desc));
993+
f.aarch64PauthAbiTag = std::array<uint8_t, 16>{};
994+
memcpy(f.aarch64PauthAbiTag->data(), desc.data(),
995+
f.aarch64PauthAbiTag->size());
976996
}
977997

978998
template <class ELFT>
@@ -1029,15 +1049,24 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(uint32_t idx,
10291049
// .note.gnu.property containing a single AND'ed bitmap, we discard an input
10301050
// file's .note.gnu.property section.
10311051
if (name == ".note.gnu.property") {
1032-
this->andFeatures = readAndFeatures<ELFT>(InputSection(*this, sec, name));
1052+
std::tie(this->andFeatures, this->aarch64PauthAbiTag) =
1053+
readGnuProperty<ELFT>(InputSection(*this, sec, name));
10331054
return &InputSection::discarded;
10341055
}
10351056

1036-
if (config->emachine == EM_AARCH64 &&
1037-
name == ".note.AARCH64-PAUTH-ABI-tag") {
1038-
readAArch64PauthAbiTag<ELFT>(InputSection(*this, sec, name), *this);
1039-
return &InputSection::discarded;
1040-
}
1057+
// TODO: alternative PAuth ELF marking way
1058+
// To be implemented after the following PRs are merged:
1059+
// - https://github.com/ARM-software/abi-aa/pull/240
1060+
// - https://github.com/llvm/llvm-project/pull/72714
1061+
//
1062+
// if (config->emachine == EM_AARCH64 &&
1063+
// name == ".note.AARCH64-PAUTH-ABI-tag") {
1064+
// if (this->aarch64PauthAbiTag != std::nullopt)
1065+
// error("cannot mix two PAuth ABI tag ELF marking ways in one object
1066+
// file");
1067+
// readAArch64PauthAbiTag<ELFT>(InputSection(*this, sec, name), *this);
1068+
// return &InputSection::discarded;
1069+
// }
10411070

10421071
// Split stacks is a feature to support a discontiguous stack,
10431072
// commonly used in the programming language Go. For the details,

lld/ELF/InputFiles.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ class ELFFileBase : public InputFile {
218218
public:
219219
uint32_t andFeatures = 0;
220220
bool hasCommonSyms = false;
221-
SmallVector<uint8_t, 0> aarch64PauthAbiTag;
221+
std::optional<std::array<uint8_t, 16>> aarch64PauthAbiTag;
222222
};
223223

224224
// .o file.

lld/ELF/SyntheticSections.cpp

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -314,33 +314,56 @@ GnuPropertySection::GnuPropertySection()
314314
config->wordsize, ".note.gnu.property") {}
315315

316316
void GnuPropertySection::writeTo(uint8_t *buf) {
317+
write32(buf, 4); // Name size
318+
write32(buf + 4, getSize() - 16); // Content size
319+
write32(buf + 8, NT_GNU_PROPERTY_TYPE_0); // Type
320+
memcpy(buf + 12, "GNU", 4); // Name string
321+
317322
uint32_t featureAndType = config->emachine == EM_AARCH64
318323
? GNU_PROPERTY_AARCH64_FEATURE_1_AND
319324
: GNU_PROPERTY_X86_FEATURE_1_AND;
320325

321-
write32(buf, 4); // Name size
322-
write32(buf + 4, config->is64 ? 16 : 12); // Content size
323-
write32(buf + 8, NT_GNU_PROPERTY_TYPE_0); // Type
324-
memcpy(buf + 12, "GNU", 4); // Name string
325-
write32(buf + 16, featureAndType); // Feature type
326-
write32(buf + 20, 4); // Feature size
327-
write32(buf + 24, config->andFeatures); // Feature flags
328-
if (config->is64)
329-
write32(buf + 28, 0); // Padding
326+
unsigned offset = 16;
327+
328+
if (config->andFeatures != 0) {
329+
write32(buf + offset + 0, featureAndType); // Feature type
330+
write32(buf + offset + 4, 4); // Feature size
331+
write32(buf + offset + 8, config->andFeatures); // Feature flags
332+
if (config->is64)
333+
write32(buf + offset + 12, 0); // Padding
334+
offset += 16;
335+
}
336+
337+
if (ctx.aarch64PauthAbiTag != std::nullopt) {
338+
write32(buf + offset + 0, GNU_PROPERTY_AARCH64_FEATURE_PAUTH);
339+
write32(buf + offset + 4, 8 * 2);
340+
memcpy(buf + offset + 8, ctx.aarch64PauthAbiTag->data(),
341+
ctx.aarch64PauthAbiTag->size());
342+
}
330343
}
331344

332-
size_t GnuPropertySection::getSize() const { return config->is64 ? 32 : 28; }
345+
size_t GnuPropertySection::getSize() const {
346+
uint32_t contentSize = 0;
347+
if (config->andFeatures != 0)
348+
contentSize += config->is64 ? 16 : 12;
349+
if (ctx.aarch64PauthAbiTag != std::nullopt) {
350+
assert(config->emachine == EM_AARCH64);
351+
contentSize += 4 + 4 + 8 * 2;
352+
}
353+
assert(contentSize != 0);
354+
return contentSize + 16;
355+
}
333356

334357
AArch64PauthAbiTag::AArch64PauthAbiTag()
335358
: SyntheticSection(llvm::ELF::SHF_ALLOC, llvm::ELF::SHT_NOTE,
336359
config->wordsize, ".note.AARCH64-PAUTH-ABI-tag") {}
337360

338361
bool AArch64PauthAbiTag::isNeeded() const {
339-
return !ctx.aarch64PauthAbiTag.empty();
362+
return ctx.aarch64PauthAbiTag != std::nullopt;
340363
}
341364

342365
void AArch64PauthAbiTag::writeTo(uint8_t *buf) {
343-
const SmallVector<uint8_t, 0> &data = ctx.aarch64PauthAbiTag;
366+
std::array<uint8_t, 16> data = ctx.aarch64PauthAbiTag.value();
344367
write32(buf, 4); // Name size
345368
write32(buf + 4, data.size()); // Content size
346369
write32(buf + 8, NT_ARM_TYPE_PAUTH_ABI_TAG); // Type
@@ -349,11 +372,6 @@ void AArch64PauthAbiTag::writeTo(uint8_t *buf) {
349372
memset(buf + 16 + data.size(), 0, getSize() - 16 - data.size()); // Padding
350373
}
351374

352-
size_t AArch64PauthAbiTag::getSize() const {
353-
return alignToPowerOf2(16 + ctx.aarch64PauthAbiTag.size(),
354-
config->is64 ? 8 : 4);
355-
}
356-
357375
BuildIdSection::BuildIdSection()
358376
: SyntheticSection(SHF_ALLOC, SHT_NOTE, 4, ".note.gnu.build-id"),
359377
hashSize(getHashSize()) {}

lld/ELF/SyntheticSections.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ class AArch64PauthAbiTag final : public SyntheticSection {
150150
public:
151151
AArch64PauthAbiTag();
152152
void writeTo(uint8_t *buf) override;
153-
size_t getSize() const override;
153+
size_t getSize() const override { return 32; };
154154
bool isNeeded() const override;
155155
};
156156

lld/ELF/Writer.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -521,13 +521,18 @@ template <class ELFT> void elf::createSyntheticSections() {
521521
in.iplt = std::make_unique<IpltSection>();
522522
add(*in.iplt);
523523

524-
if (config->andFeatures)
524+
if (config->andFeatures || ctx.aarch64PauthAbiTag.has_value())
525525
add(*make<GnuPropertySection>());
526526

527-
if (!ctx.aarch64PauthAbiTag.empty()) {
528-
in.aarch64PauthAbiTag = std::make_unique<AArch64PauthAbiTag>();
529-
add(*in.aarch64PauthAbiTag);
530-
}
527+
// TODO: alternative PAuth ELF marking way
528+
// To be implemented after the following PRs are merged:
529+
// - https://github.com/ARM-software/abi-aa/pull/240
530+
// - https://github.com/llvm/llvm-project/pull/72714
531+
//
532+
// if (!ctx.aarch64PauthAbiTag.empty()) {
533+
// in.aarch64PauthAbiTag = std::make_unique<AArch64PauthAbiTag>();
534+
// add(*in.aarch64PauthAbiTag);
535+
// }
531536

532537
// .note.GNU-stack is always added when we are creating a re-linkable
533538
// object file. Other linkers are using the presence of this marker
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# REQUIRES: aarch64
2+
3+
# RUN: rm -rf %t && split-file %s %t && cd %t
4+
5+
# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu abi-tag1.s -o tag11.o
6+
# RUN: cp tag11.o tag12.o
7+
# RUN: ld.lld -shared tag11.o tag12.o -o tagok.so
8+
# RUN: llvm-readelf -n tagok.so | FileCheck --check-prefix OK %s
9+
10+
# OK: Properties: AArch64 PAuth ABI tag: platform 0x2a, version 0x1
11+
12+
# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu abi-tag2.s -o tag2.o
13+
# RUN: not ld.lld tag11.o tag12.o tag2.o -o /dev/null 2>&1 | FileCheck --check-prefix ERR1 %s
14+
15+
# ERR1: error: incompatible values of AArch64 PAuth compatibility info found
16+
# ERR1: {{.*}}: 0x2A000000000000000{{1|2}}00000000000000
17+
# ERR1: {{.*}}: 0x2A000000000000000{{1|2}}00000000000000
18+
19+
# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu abi-tag-short.s -o short.o
20+
# RUN: not ld.lld short.o -o /dev/null 2>&1 | FileCheck --check-prefix ERR2 %s
21+
22+
# ERR2: error: short.o:(.note.gnu.property+0x0): size of GNU_PROPERTY_AARCH64_FEATURE_PAUTH property must be 16
23+
24+
# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu abi-tag-multiple.s -o multiple.o
25+
# RUN: not ld.lld multiple.o -o /dev/null 2>&1 | FileCheck --check-prefix ERR3 %s
26+
# ERR3: error: multiple.o:(.note.gnu.property+0x0): multiple GNU_PROPERTY_AARCH64_FEATURE_PAUTH properties are not allowed
27+
28+
# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu no-info.s -o noinfo1.o
29+
# RUN: cp noinfo1.o noinfo2.o
30+
# RUN: not ld.lld -z pauth-report=error tag11.o noinfo1.o noinfo2.o -o /dev/null 2>&1 | FileCheck --check-prefix ERR4 %s
31+
# RUN: ld.lld -z pauth-report=warning tag11.o noinfo1.o noinfo2.o -o /dev/null 2>&1 | FileCheck --check-prefix WARN %s
32+
# RUN: ld.lld -z pauth-report=none tag11.o noinfo1.o noinfo2.o -o /dev/null 2>&1 | FileCheck --check-prefix NONE %s
33+
34+
# ERR4: error: {{.*}}noinfo1.o has no AArch64 PAuth compatibility info while {{.*}}tag11.o has one; either all or no input files must have it
35+
# 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
36+
# WARN: warning: {{.*}}noinfo1.o has no AArch64 PAuth compatibility info while {{.*}}tag11.o has one; either all or no input files must have it
37+
# 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
38+
# NONE-NOT: {{.*}} has no AArch64 PAuth compatibility info while {{.*}} has one; either all or no input files must have it
39+
40+
#--- abi-tag-short.s
41+
42+
# Version is 4 bytes instead of 8 bytes, must emit an error
43+
44+
.section ".note.gnu.property", "a"
45+
.long 4
46+
.long 20
47+
.long 5
48+
.asciz "GNU"
49+
.long 0xc0000001
50+
.long 12
51+
.quad 2
52+
.long 31
53+
54+
#--- abi-tag-multiple.s
55+
56+
.section ".note.gnu.property", "a"
57+
.long 4
58+
.long 48
59+
.long 5
60+
.asciz "GNU"
61+
.long 0xc0000001
62+
.long 16
63+
.quad 42 // platform
64+
.quad 1 // version
65+
.long 0xc0000001
66+
.long 16
67+
.quad 42 // platform
68+
.quad 1 // version
69+
70+
#--- abi-tag1.s
71+
72+
.section ".note.gnu.property", "a"
73+
.long 4
74+
.long 24
75+
.long 5
76+
.asciz "GNU"
77+
.long 0xc0000001
78+
.long 16
79+
.quad 42 // platform
80+
.quad 1 // version
81+
82+
#--- abi-tag2.s
83+
84+
.section ".note.gnu.property", "a"
85+
.long 4
86+
.long 24
87+
.long 5
88+
.asciz "GNU"
89+
.long 0xc0000001
90+
.long 16
91+
.quad 42 // platform
92+
.quad 2 // version
93+
94+
#--- no-info.s
95+
96+
.section ".test", "a"

0 commit comments

Comments
 (0)