Skip to content

Commit c493d78

Browse files
kovdan01pcc
andcommitted
[lld][AArch64][ELF][PAC] Support AUTH relocations and AUTH ELF marking
This patch adds lld support for: - Dynamic R_AARCH64_AUTH_* relocations (including RELR compressed AUTH relocations) as described here: https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst#auth-variant-dynamic-relocations - .note.AARCH64-PAUTH-ABI-tag section as defined here https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst#elf-marking Co-authored-by: Peter Collingbourne <[email protected]>
1 parent 8410ee4 commit c493d78

11 files changed

+445
-11
lines changed

lld/ELF/Arch/AArch64.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
112112
case R_AARCH64_MOVW_UABS_G2:
113113
case R_AARCH64_MOVW_UABS_G2_NC:
114114
case R_AARCH64_MOVW_UABS_G3:
115+
case R_AARCH64_AUTH_ABS64:
115116
return R_ABS;
116117
case R_AARCH64_TLSDESC_ADR_PAGE21:
117118
return R_AARCH64_TLSDESC_PAGE;
@@ -395,6 +396,10 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel,
395396
case R_AARCH64_PREL64:
396397
write64(loc, val);
397398
break;
399+
case R_AARCH64_AUTH_ABS64:
400+
checkIntUInt(loc, val, 32, rel);
401+
write32(loc, val);
402+
break;
398403
case R_AARCH64_ADD_ABS_LO12_NC:
399404
or32AArch64Imm(loc, val);
400405
break;

lld/ELF/Config.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ struct Config {
187187
llvm::StringRef cmseOutputLib;
188188
StringRef zBtiReport = "none";
189189
StringRef zCetReport = "none";
190+
StringRef zPauthReport = "none";
190191
llvm::StringRef ltoBasicBlockSections;
191192
std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
192193
llvm::StringRef thinLTOPrefixReplaceOld;
@@ -275,6 +276,7 @@ struct Config {
275276
bool relocatable;
276277
bool relrGlibc = false;
277278
bool relrPackDynRelocs = false;
279+
bool relrPackAuthDynRelocs = false;
278280
llvm::DenseSet<llvm::StringRef> saveTempsArgs;
279281
llvm::SmallVector<std::pair<llvm::GlobPattern, uint32_t>, 0> shuffleSections;
280282
bool singleRoRx;
@@ -492,6 +494,8 @@ struct Ctx {
492494
void reset();
493495

494496
llvm::raw_fd_ostream openAuxiliaryFile(llvm::StringRef, std::error_code &);
497+
498+
SmallVector<uint8_t, 0> aarch64PauthAbiTag;
495499
};
496500

497501
LLVM_LIBRARY_VISIBILITY extern Ctx ctx;

lld/ELF/Driver.cpp

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
#include "llvm/Support/TargetSelect.h"
6666
#include "llvm/Support/TimeProfiler.h"
6767
#include "llvm/Support/raw_ostream.h"
68+
#include <algorithm>
6869
#include <cstdlib>
6970
#include <tuple>
7071
#include <utility>
@@ -459,6 +460,8 @@ static void checkOptions() {
459460
error("-z force-bti only supported on AArch64");
460461
if (config->zBtiReport != "none")
461462
error("-z bti-report only supported on AArch64");
463+
if (config->zPauthReport != "none")
464+
error("-z pauth-report only supported on AArch64");
462465
}
463466

464467
if (config->emachine != EM_386 && config->emachine != EM_X86_64 &&
@@ -558,6 +561,7 @@ constexpr const char *knownZFlags[] = {
558561
"nognustack",
559562
"nokeep-text-section-prefix",
560563
"nopack-relative-relocs",
564+
"nopack-relative-auth-relocs",
561565
"norelro",
562566
"noseparate-code",
563567
"nostart-stop-gc",
@@ -566,6 +570,7 @@ constexpr const char *knownZFlags[] = {
566570
"origin",
567571
"pac-plt",
568572
"pack-relative-relocs",
573+
"pack-relative-auth-relocs",
569574
"rel",
570575
"rela",
571576
"relro",
@@ -583,7 +588,7 @@ constexpr const char *knownZFlags[] = {
583588
static bool isKnownZFlag(StringRef s) {
584589
return llvm::is_contained(knownZFlags, s) ||
585590
s.starts_with("common-page-size=") || s.starts_with("bti-report=") ||
586-
s.starts_with("cet-report=") ||
591+
s.starts_with("cet-report=") || s.starts_with("pauth-report=") ||
587592
s.starts_with("dead-reloc-in-nonalloc=") ||
588593
s.starts_with("max-page-size=") || s.starts_with("stack-size=") ||
589594
s.starts_with("start-stop-visibility=");
@@ -1514,7 +1519,8 @@ static void readConfigs(opt::InputArgList &args) {
15141519
}
15151520

15161521
auto reports = {std::make_pair("bti-report", &config->zBtiReport),
1517-
std::make_pair("cet-report", &config->zCetReport)};
1522+
std::make_pair("cet-report", &config->zCetReport),
1523+
std::make_pair("pauth-report", &config->zPauthReport)};
15181524
for (opt::Arg *arg : args.filtered(OPT_z)) {
15191525
std::pair<StringRef, StringRef> option =
15201526
StringRef(arg->getValue()).split('=');
@@ -1671,6 +1677,9 @@ static void readConfigs(opt::InputArgList &args) {
16711677
getPackDynRelocs(args);
16721678
}
16731679

1680+
config->relrPackAuthDynRelocs = getZFlag(
1681+
args, "pack-relative-auth-relocs", "nopack-relative-auth-relocs", false);
1682+
16741683
if (auto *arg = args.getLastArg(OPT_symbol_ordering_file)){
16751684
if (args.hasArg(OPT_call_graph_ordering_file))
16761685
error("--symbol-ordering-file and --call-graph-order-file "
@@ -2639,6 +2648,47 @@ static uint32_t getAndFeatures() {
26392648
return ret;
26402649
}
26412650

2651+
static void getAarch64PauthInfo() {
2652+
if (ctx.objectFiles.empty())
2653+
return;
2654+
2655+
auto NonEmptyIt = std::find_if(
2656+
ctx.objectFiles.begin(), ctx.objectFiles.end(),
2657+
[](const ELFFileBase *f) { return !f->aarch64PauthAbiTag.empty(); });
2658+
if (NonEmptyIt == ctx.objectFiles.end())
2659+
return;
2660+
2661+
ctx.aarch64PauthAbiTag = (*NonEmptyIt)->aarch64PauthAbiTag;
2662+
StringRef F1 = (*NonEmptyIt)->getName();
2663+
for (ELFFileBase *F : ArrayRef(ctx.objectFiles)) {
2664+
StringRef F2 = F->getName();
2665+
const SmallVector<uint8_t, 0> &D1 = ctx.aarch64PauthAbiTag;
2666+
const SmallVector<uint8_t, 0> &D2 = F->aarch64PauthAbiTag;
2667+
if (D1.empty() != D2.empty()) {
2668+
auto Helper = [](StringRef Report, const Twine &Msg) {
2669+
if (Report == "warning")
2670+
warn(Msg);
2671+
else if (Report == "error")
2672+
error(Msg);
2673+
};
2674+
2675+
Helper(config->zPauthReport,
2676+
(D1.empty() ? F1.str() : F2.str()) +
2677+
" has no AArch64 PAuth compatibility info while " +
2678+
(D1.empty() ? F2.str() : F1.str()) +
2679+
" has one; either all or no input files must have it");
2680+
}
2681+
2682+
if (!D1.empty() && !D2.empty() &&
2683+
!std::equal(D1.begin(), D1.end(), D2.begin(), D2.end()))
2684+
errorOrWarn(
2685+
"incompatible values of AArch64 PAuth compatibility info found"
2686+
"\n" +
2687+
F1 + ": 0x" + toHex(ArrayRef(D1.data(), D1.size())) + "\n" + F2 +
2688+
": 0x" + toHex(ArrayRef(D2.data(), D2.size())));
2689+
}
2690+
}
2691+
26422692
static void initSectionsAndLocalSyms(ELFFileBase *file, bool ignoreComdats) {
26432693
switch (file->ekind) {
26442694
case ELF32LEKind:
@@ -2976,6 +3026,9 @@ void LinkerDriver::link(opt::InputArgList &args) {
29763026
// contain a hint to tweak linker's and loader's behaviors.
29773027
config->andFeatures = getAndFeatures();
29783028

3029+
if (config->emachine == EM_AARCH64)
3030+
getAarch64PauthInfo();
3031+
29793032
// The Target instance handles target-specific stuff, such as applying
29803033
// relocations or writing a PLT section. It also contains target-dependent
29813034
// values such as a default image base address.

lld/ELF/InputFiles.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,44 @@ template <class ELFT> static uint32_t readAndFeatures(const InputSection &sec) {
962962
return featuresSet;
963963
}
964964

965+
// Extract compatibility info for aarch64 pointer authentication from the
966+
// .note.AARCH64-PAUTH-ABI-tag section and write it to the corresponding ObjFile
967+
// field. See the following ABI documentation:
968+
// https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst#elf-marking
969+
template <class ELFT>
970+
static void readAArch64PauthAbiTag(const InputSection &sec, ObjFile<ELFT> &f) {
971+
using Elf_Nhdr = typename ELFT::Nhdr;
972+
using Elf_Note = typename ELFT::Note;
973+
ArrayRef<uint8_t> data = sec.content();
974+
auto reportError = [&](const Twine &msg) {
975+
errorOrWarn(toString(sec.file) + ":(" + sec.name + "): " + msg);
976+
};
977+
978+
auto *nhdr = reinterpret_cast<const Elf_Nhdr *>(data.data());
979+
if (data.size() < sizeof(Elf_Nhdr) ||
980+
data.size() < nhdr->getSize(sec.addralign)) {
981+
reportError("section is too short");
982+
return;
983+
}
984+
985+
Elf_Note note(*nhdr);
986+
if (nhdr->n_type != NT_ARM_TYPE_PAUTH_ABI_TAG)
987+
reportError("invalid type field value " + Twine(nhdr->n_type) + " (" +
988+
Twine(NT_ARM_TYPE_PAUTH_ABI_TAG) + " expected)");
989+
if (note.getName() != "ARM")
990+
reportError("invalid name field value " + note.getName() +
991+
" (ARM expected)");
992+
993+
ArrayRef<uint8_t> desc = note.getDesc(sec.addralign);
994+
if (desc.size() < 16) {
995+
reportError("too short AArch64 PAuth compatibility info "
996+
"(at least 16 bytes expected)");
997+
return;
998+
}
999+
1000+
f.aarch64PauthAbiTag = SmallVector<uint8_t, 0>(iterator_range(desc));
1001+
}
1002+
9651003
template <class ELFT>
9661004
InputSectionBase *ObjFile<ELFT>::getRelocTarget(uint32_t idx,
9671005
const Elf_Shdr &sec,
@@ -1020,6 +1058,12 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(uint32_t idx,
10201058
return &InputSection::discarded;
10211059
}
10221060

1061+
if (config->emachine == EM_AARCH64 &&
1062+
name == ".note.AARCH64-PAUTH-ABI-tag") {
1063+
readAArch64PauthAbiTag<ELFT>(InputSection(*this, sec, name), *this);
1064+
return &InputSection::discarded;
1065+
}
1066+
10231067
// Split stacks is a feature to support a discontiguous stack,
10241068
// commonly used in the programming language Go. For the details,
10251069
// see https://gcc.gnu.org/wiki/SplitStacks. An object file compiled

lld/ELF/InputFiles.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ class ELFFileBase : public InputFile {
218218
public:
219219
uint32_t andFeatures = 0;
220220
bool hasCommonSyms = false;
221+
SmallVector<uint8_t, 0> aarch64PauthAbiTag;
221222
};
222223

223224
// .o file.

lld/ELF/Relocations.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1444,6 +1444,32 @@ template <class ELFT, class RelTy> void RelocationScanner::scanOne(RelTy *&i) {
14441444
}
14451445
}
14461446

1447+
if (config->emachine == EM_AARCH64 && type == R_AARCH64_AUTH_ABS64) {
1448+
// Assume relocations from relocatable objects are RELA.
1449+
assert(RelTy::IsRela);
1450+
std::lock_guard<std::mutex> lock(relocMutex);
1451+
// For a preemptible symbol, we can't use a relative relocation. For an
1452+
// undefined symbol, we can't compute offset at link-time and use a relative
1453+
// relocation. Use a symbolic relocation instead.
1454+
Partition &part = sec->getPartition();
1455+
if (sym.isPreemptible || sym.isUndefined()) {
1456+
part.relaDyn->addSymbolReloc(type, *sec, offset, sym, addend, type);
1457+
} else if (part.relrAuthDyn && sec->addralign >= 2 && offset % 2 == 0 &&
1458+
isInt<32>(sym.getVA(addend))) {
1459+
// Implicit addend is below 32-bits so we can use the compressed
1460+
// relative relocation section. The R_AARCH64_AUTH_RELATIVE
1461+
// has a smaller addend fielf as bits [63:32] encode the signing-schema.
1462+
sec->addReloc({expr, type, offset, addend, &sym});
1463+
part.relrAuthDyn->relocsVec[parallel::getThreadIndex()].push_back(
1464+
{sec, offset});
1465+
} else {
1466+
part.relaDyn->addReloc({R_AARCH64_AUTH_RELATIVE, sec, offset,
1467+
DynamicReloc::AddendOnlyWithTargetVA, sym, addend,
1468+
R_ABS});
1469+
}
1470+
return;
1471+
}
1472+
14471473
// If the relocation does not emit a GOT or GOTPLT entry but its computation
14481474
// uses their addresses, we need GOT or GOTPLT to be created.
14491475
//

lld/ELF/SyntheticSections.cpp

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,29 @@ void GnuPropertySection::writeTo(uint8_t *buf) {
331331

332332
size_t GnuPropertySection::getSize() const { return config->is64 ? 32 : 28; }
333333

334+
AArch64PauthAbiTag::AArch64PauthAbiTag()
335+
: SyntheticSection(llvm::ELF::SHF_ALLOC, llvm::ELF::SHT_NOTE,
336+
config->wordsize, ".note.AARCH64-PAUTH-ABI-tag") {}
337+
338+
bool AArch64PauthAbiTag::isNeeded() const {
339+
return !ctx.aarch64PauthAbiTag.empty();
340+
}
341+
342+
void AArch64PauthAbiTag::writeTo(uint8_t *buf) {
343+
const SmallVector<uint8_t, 0> &data = ctx.aarch64PauthAbiTag;
344+
write32(buf, 4); // Name size
345+
write32(buf + 4, data.size()); // Content size
346+
write32(buf + 8, NT_ARM_TYPE_PAUTH_ABI_TAG); // Type
347+
memcpy(buf + 12, "ARM", 4); // Name string
348+
memcpy(buf + 16, data.data(), data.size());
349+
memset(buf + 16 + data.size(), 0, getSize() - 16 - data.size()); // Padding
350+
}
351+
352+
size_t AArch64PauthAbiTag::getSize() const {
353+
return alignToPowerOf2(16 + ctx.aarch64PauthAbiTag.size(),
354+
config->is64 ? 8 : 4);
355+
}
356+
334357
BuildIdSection::BuildIdSection()
335358
: SyntheticSection(SHF_ALLOC, SHT_NOTE, 4, ".note.gnu.build-id"),
336359
hashSize(getHashSize()) {}
@@ -1406,6 +1429,12 @@ DynamicSection<ELFT>::computeContents() {
14061429
addInt(config->useAndroidRelrTags ? DT_ANDROID_RELRENT : DT_RELRENT,
14071430
sizeof(Elf_Relr));
14081431
}
1432+
if (part.relrAuthDyn && part.relrAuthDyn->getParent() &&
1433+
!part.relrAuthDyn->relocs.empty()) {
1434+
addInSec(DT_AARCH64_AUTH_RELR, *part.relrAuthDyn);
1435+
addInt(DT_AARCH64_AUTH_RELRSZ, part.relrAuthDyn->getParent()->size);
1436+
addInt(DT_AARCH64_AUTH_RELRENT, sizeof(Elf_Relr));
1437+
}
14091438
// .rel[a].plt section usually consists of two parts, containing plt and
14101439
// iplt relocations. It is possible to have only iplt relocations in the
14111440
// output. In that case relaPlt is empty and have zero offset, the same offset
@@ -1717,10 +1746,13 @@ template <class ELFT> void RelocationSection<ELFT>::writeTo(uint8_t *buf) {
17171746
}
17181747
}
17191748

1720-
RelrBaseSection::RelrBaseSection(unsigned concurrency)
1721-
: SyntheticSection(SHF_ALLOC,
1722-
config->useAndroidRelrTags ? SHT_ANDROID_RELR : SHT_RELR,
1723-
config->wordsize, ".relr.dyn"),
1749+
RelrBaseSection::RelrBaseSection(unsigned concurrency, bool isAArch64Auth)
1750+
: SyntheticSection(
1751+
SHF_ALLOC,
1752+
isAArch64Auth
1753+
? SHT_AARCH64_AUTH_RELR
1754+
: (config->useAndroidRelrTags ? SHT_ANDROID_RELR : SHT_RELR),
1755+
config->wordsize, isAArch64Auth ? ".relr.auth.dyn" : ".relr.dyn"),
17241756
relocsVec(concurrency) {}
17251757

17261758
void RelrBaseSection::mergeRels() {
@@ -1988,8 +2020,8 @@ bool AndroidPackedRelocationSection<ELFT>::updateAllocSize() {
19882020
}
19892021

19902022
template <class ELFT>
1991-
RelrSection<ELFT>::RelrSection(unsigned concurrency)
1992-
: RelrBaseSection(concurrency) {
2023+
RelrSection<ELFT>::RelrSection(unsigned concurrency, bool isAArch64Auth)
2024+
: RelrBaseSection(concurrency, isAArch64Auth) {
19932025
this->entsize = config->wordsize;
19942026
}
19952027

lld/ELF/SyntheticSections.h

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,16 @@ class GnuPropertySection final : public SyntheticSection {
144144
size_t getSize() const override;
145145
};
146146

147+
// .note.AARCH64-PAUTH-ABI-tag section. See
148+
// https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst#elf-marking
149+
class AArch64PauthAbiTag final : public SyntheticSection {
150+
public:
151+
AArch64PauthAbiTag();
152+
void writeTo(uint8_t *buf) override;
153+
size_t getSize() const override;
154+
bool isNeeded() const override;
155+
};
156+
147157
// .note.gnu.build-id section.
148158
class BuildIdSection : public SyntheticSection {
149159
// First 16 bytes are a header.
@@ -543,7 +553,8 @@ class RelocationBaseSection : public SyntheticSection {
543553
static bool classof(const SectionBase *d) {
544554
return SyntheticSection::classof(d) &&
545555
(d->type == llvm::ELF::SHT_RELA || d->type == llvm::ELF::SHT_REL ||
546-
d->type == llvm::ELF::SHT_RELR);
556+
d->type == llvm::ELF::SHT_RELR ||
557+
d->type == llvm::ELF::SHT_AARCH64_AUTH_RELR);
547558
}
548559
int32_t dynamicTag, sizeDynamicTag;
549560
SmallVector<DynamicReloc, 0> relocs;
@@ -599,7 +610,7 @@ struct RelativeReloc {
599610

600611
class RelrBaseSection : public SyntheticSection {
601612
public:
602-
RelrBaseSection(unsigned concurrency);
613+
RelrBaseSection(unsigned concurrency, bool isAArch64Auth = false);
603614
void mergeRels();
604615
bool isNeeded() const override {
605616
return !relocs.empty() ||
@@ -617,7 +628,7 @@ template <class ELFT> class RelrSection final : public RelrBaseSection {
617628
using Elf_Relr = typename ELFT::Relr;
618629

619630
public:
620-
RelrSection(unsigned concurrency);
631+
RelrSection(unsigned concurrency, bool isAArch64Auth = false);
621632

622633
bool updateAllocSize() override;
623634
size_t getSize() const override { return relrRelocs.size() * this->entsize; }
@@ -1319,6 +1330,7 @@ struct Partition {
13191330
std::unique_ptr<PackageMetadataNote> packageMetadataNote;
13201331
std::unique_ptr<RelocationBaseSection> relaDyn;
13211332
std::unique_ptr<RelrBaseSection> relrDyn;
1333+
std::unique_ptr<RelrBaseSection> relrAuthDyn;
13221334
std::unique_ptr<VersionDefinitionSection> verDef;
13231335
std::unique_ptr<SyntheticSection> verNeed;
13241336
std::unique_ptr<VersionTableSection> verSym;
@@ -1363,6 +1375,7 @@ struct InStruct {
13631375
std::unique_ptr<StringTableSection> strTab;
13641376
std::unique_ptr<SymbolTableBaseSection> symTab;
13651377
std::unique_ptr<SymtabShndxSection> symTabShndx;
1378+
std::unique_ptr<AArch64PauthAbiTag> aarch64PauthAbiTag;
13661379

13671380
void reset();
13681381
};

0 commit comments

Comments
 (0)