Skip to content

Commit cca9115

Browse files
kovdan01pccMaskRay
authored
[lld][AArch64][ELF][PAC] Support AUTH relocations and AUTH ELF marking (#72714)
This patch adds lld support for: - Dynamic R_AARCH64_AUTH_* relocations (without 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 Depends on #72713 and #85231 --------- Co-authored-by: Peter Collingbourne <[email protected]> Co-authored-by: Fangrui Song <[email protected]>
1 parent 7a8cf95 commit cca9115

16 files changed

+403
-56
lines changed

lld/ELF/Arch/AArch64.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
113113
case R_AARCH64_MOVW_UABS_G2_NC:
114114
case R_AARCH64_MOVW_UABS_G3:
115115
return R_ABS;
116+
case R_AARCH64_AUTH_ABS64:
117+
return R_AARCH64_AUTH;
116118
case R_AARCH64_TLSDESC_ADR_PAGE21:
117119
return R_AARCH64_TLSDESC_PAGE;
118120
case R_AARCH64_TLSDESC_LD64_LO12:
@@ -204,7 +206,7 @@ bool AArch64::usesOnlyLowPageBits(RelType type) const {
204206
}
205207

206208
RelType AArch64::getDynRel(RelType type) const {
207-
if (type == R_AARCH64_ABS64)
209+
if (type == R_AARCH64_ABS64 || type == R_AARCH64_AUTH_ABS64)
208210
return type;
209211
return R_AARCH64_NONE;
210212
}

lld/ELF/Config.h

Lines changed: 3 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
bool ltoBBAddrMap;
191192
llvm::StringRef ltoBasicBlockSections;
192193
std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
@@ -499,6 +500,8 @@ struct Ctx {
499500
void reset();
500501

501502
llvm::raw_fd_ostream openAuxiliaryFile(llvm::StringRef, std::error_code &);
503+
504+
ArrayRef<uint8_t> aarch64PauthAbiCoreInfo;
502505
};
503506

504507
LLVM_LIBRARY_VISIBILITY extern Ctx ctx;

lld/ELF/Driver.cpp

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include "lld/Common/Strings.h"
4747
#include "lld/Common/TargetOptionsCommandFlags.h"
4848
#include "lld/Common/Version.h"
49+
#include "llvm/ADT/STLExtras.h"
4950
#include "llvm/ADT/SetVector.h"
5051
#include "llvm/ADT/StringExtras.h"
5152
#include "llvm/ADT/StringSwitch.h"
@@ -461,6 +462,8 @@ static void checkOptions() {
461462
error("-z force-bti only supported on AArch64");
462463
if (config->zBtiReport != "none")
463464
error("-z bti-report only supported on AArch64");
465+
if (config->zPauthReport != "none")
466+
error("-z pauth-report only supported on AArch64");
464467
}
465468

466469
if (config->emachine != EM_386 && config->emachine != EM_X86_64 &&
@@ -1501,7 +1504,8 @@ static void readConfigs(opt::InputArgList &args) {
15011504
}
15021505

15031506
auto reports = {std::make_pair("bti-report", &config->zBtiReport),
1504-
std::make_pair("cet-report", &config->zCetReport)};
1507+
std::make_pair("cet-report", &config->zCetReport),
1508+
std::make_pair("pauth-report", &config->zPauthReport)};
15051509
for (opt::Arg *arg : args.filtered(OPT_z)) {
15061510
std::pair<StringRef, StringRef> option =
15071511
StringRef(arg->getValue()).split('=');
@@ -2599,14 +2603,17 @@ static void redirectSymbols(ArrayRef<WrappedSymbol> wrapped) {
25992603
symtab.wrap(w.sym, w.real, w.wrap);
26002604
}
26012605

2606+
static void reportMissingFeature(StringRef config, const Twine &report) {
2607+
if (config == "error")
2608+
error(report);
2609+
else if (config == "warning")
2610+
warn(report);
2611+
}
2612+
26022613
static void checkAndReportMissingFeature(StringRef config, uint32_t features,
26032614
uint32_t mask, const Twine &report) {
2604-
if (!(features & mask)) {
2605-
if (config == "error")
2606-
error(report);
2607-
else if (config == "warning")
2608-
warn(report);
2609-
}
2615+
if (!(features & mask))
2616+
reportMissingFeature(config, report);
26102617
}
26112618

26122619
// To enable CET (x86's hardware-assisted control flow enforcement), each
@@ -2617,12 +2624,28 @@ static void checkAndReportMissingFeature(StringRef config, uint32_t features,
26172624
//
26182625
// This is also the case with AARCH64's BTI and PAC which use the similar
26192626
// GNU_PROPERTY_AARCH64_FEATURE_1_AND mechanism.
2620-
static uint32_t getAndFeatures() {
2627+
//
2628+
// For AArch64 PAuth-enabled object files, the core info of all of them must
2629+
// match. Missing info for some object files with matching info for remaining
2630+
// ones can be allowed (see -z pauth-report).
2631+
static void readSecurityNotes() {
26212632
if (config->emachine != EM_386 && config->emachine != EM_X86_64 &&
26222633
config->emachine != EM_AARCH64)
2623-
return 0;
2634+
return;
2635+
2636+
config->andFeatures = -1;
2637+
2638+
StringRef referenceFileName;
2639+
if (config->emachine == EM_AARCH64) {
2640+
auto it = llvm::find_if(ctx.objectFiles, [](const ELFFileBase *f) {
2641+
return !f->aarch64PauthAbiCoreInfo.empty();
2642+
});
2643+
if (it != ctx.objectFiles.end()) {
2644+
ctx.aarch64PauthAbiCoreInfo = (*it)->aarch64PauthAbiCoreInfo;
2645+
referenceFileName = (*it)->getName();
2646+
}
2647+
}
26242648

2625-
uint32_t ret = -1;
26262649
for (ELFFileBase *f : ctx.objectFiles) {
26272650
uint32_t features = f->andFeatures;
26282651

@@ -2658,14 +2681,31 @@ static uint32_t getAndFeatures() {
26582681
"GNU_PROPERTY_AARCH64_FEATURE_1_PAC property");
26592682
features |= GNU_PROPERTY_AARCH64_FEATURE_1_PAC;
26602683
}
2661-
ret &= features;
2684+
config->andFeatures &= features;
2685+
2686+
if (ctx.aarch64PauthAbiCoreInfo.empty())
2687+
continue;
2688+
2689+
if (f->aarch64PauthAbiCoreInfo.empty()) {
2690+
reportMissingFeature(config->zPauthReport,
2691+
toString(f) +
2692+
": -z pauth-report: file does not have AArch64 "
2693+
"PAuth core info while '" +
2694+
referenceFileName + "' has one");
2695+
continue;
2696+
}
2697+
2698+
if (ctx.aarch64PauthAbiCoreInfo != f->aarch64PauthAbiCoreInfo)
2699+
errorOrWarn("incompatible values of AArch64 PAuth core info found\n>>> " +
2700+
referenceFileName + ": 0x" +
2701+
toHex(ctx.aarch64PauthAbiCoreInfo, /*LowerCase=*/true) +
2702+
"\n>>> " + toString(f) + ": 0x" +
2703+
toHex(f->aarch64PauthAbiCoreInfo, /*LowerCase=*/true));
26622704
}
26632705

26642706
// Force enable Shadow Stack.
26652707
if (config->zShstk)
2666-
ret |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
2667-
2668-
return ret;
2708+
config->andFeatures |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
26692709
}
26702710

26712711
static void initSectionsAndLocalSyms(ELFFileBase *file, bool ignoreComdats) {
@@ -2944,7 +2984,7 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
29442984

29452985
// Read .note.gnu.property sections from input object files which
29462986
// contain a hint to tweak linker's and loader's behaviors.
2947-
config->andFeatures = getAndFeatures();
2987+
readSecurityNotes();
29482988

29492989
// The Target instance handles target-specific stuff, such as applying
29502990
// relocations or writing a PLT section. It also contains target-dependent

lld/ELF/InputFiles.cpp

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -926,25 +926,18 @@ void ObjFile<ELFT>::initializeSections(bool ignoreComdats,
926926
handleSectionGroup<ELFT>(this->sections, entries);
927927
}
928928

929-
// If a source file is compiled with x86 hardware-assisted call flow control
930-
// enabled, the generated object file contains feature flags indicating that
931-
// fact. This function reads the feature flags and returns it.
932-
//
933-
// Essentially we want to read a single 32-bit value in this function, but this
934-
// function is rather complicated because the value is buried deep inside a
935-
// .note.gnu.property section.
936-
//
937-
// The section consists of one or more NOTE records. Each NOTE record consists
938-
// of zero or more type-length-value fields. We want to find a field of a
939-
// certain type. It seems a bit too much to just store a 32-bit value, perhaps
940-
// the ABI is unnecessarily complicated.
941-
template <class ELFT> static uint32_t readAndFeatures(const InputSection &sec) {
929+
// Read the following info from the .note.gnu.property section and write it to
930+
// the corresponding fields in `ObjFile`:
931+
// - Feature flags (32 bits) representing x86 or AArch64 features for
932+
// hardware-assisted call flow control;
933+
// - AArch64 PAuth ABI core info (16 bytes).
934+
template <class ELFT>
935+
void readGnuProperty(const InputSection &sec, ObjFile<ELFT> &f) {
942936
using Elf_Nhdr = typename ELFT::Nhdr;
943937
using Elf_Note = typename ELFT::Note;
944938

945-
uint32_t featuresSet = 0;
946939
ArrayRef<uint8_t> data = sec.content();
947-
auto reportFatal = [&](const uint8_t *place, const char *msg) {
940+
auto reportFatal = [&](const uint8_t *place, const Twine &msg) {
948941
fatal(toString(sec.file) + ":(" + sec.name + "+0x" +
949942
Twine::utohexstr(place - sec.content().data()) + "): " + msg);
950943
};
@@ -983,7 +976,19 @@ template <class ELFT> static uint32_t readAndFeatures(const InputSection &sec) {
983976
// accumulate the bits set.
984977
if (size < 4)
985978
reportFatal(place, "FEATURE_1_AND entry is too short");
986-
featuresSet |= read32<ELFT::Endianness>(desc.data());
979+
f.andFeatures |= read32<ELFT::Endianness>(desc.data());
980+
} else if (config->emachine == EM_AARCH64 &&
981+
type == GNU_PROPERTY_AARCH64_FEATURE_PAUTH) {
982+
if (!f.aarch64PauthAbiCoreInfo.empty()) {
983+
reportFatal(data.data(),
984+
"multiple GNU_PROPERTY_AARCH64_FEATURE_PAUTH entries are "
985+
"not supported");
986+
} else if (size != 16) {
987+
reportFatal(data.data(), "GNU_PROPERTY_AARCH64_FEATURE_PAUTH entry "
988+
"is invalid: expected 16 bytes, but got " +
989+
Twine(size));
990+
}
991+
f.aarch64PauthAbiCoreInfo = desc;
987992
}
988993

989994
// Padding is present in the note descriptor, if necessary.
@@ -993,8 +998,6 @@ template <class ELFT> static uint32_t readAndFeatures(const InputSection &sec) {
993998
// Go to next NOTE record to look for more FEATURE_1_AND descriptions.
994999
data = data.slice(nhdr->getSize(sec.addralign));
9951000
}
996-
997-
return featuresSet;
9981001
}
9991002

10001003
template <class ELFT>
@@ -1051,7 +1054,7 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(uint32_t idx,
10511054
// .note.gnu.property containing a single AND'ed bitmap, we discard an input
10521055
// file's .note.gnu.property section.
10531056
if (name == ".note.gnu.property") {
1054-
this->andFeatures = readAndFeatures<ELFT>(InputSection(*this, sec, name));
1057+
readGnuProperty<ELFT>(InputSection(*this, sec, name), *this);
10551058
return &InputSection::discarded;
10561059
}
10571060

lld/ELF/InputFiles.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ class ELFFileBase : public InputFile {
230230
public:
231231
uint32_t andFeatures = 0;
232232
bool hasCommonSyms = false;
233+
ArrayRef<uint8_t> aarch64PauthAbiCoreInfo;
233234
};
234235

235236
// .o file.

lld/ELF/InputSection.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,7 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type,
676676
case R_DTPREL:
677677
case R_RELAX_TLS_LD_TO_LE_ABS:
678678
case R_RELAX_GOT_PC_NOPIC:
679+
case R_AARCH64_AUTH:
679680
case R_RISCV_ADD:
680681
case R_RISCV_LEB128:
681682
return sym.getVA(a);

lld/ELF/Relocations.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -995,7 +995,8 @@ bool RelocationScanner::isStaticLinkTimeConstant(RelExpr e, RelType type,
995995
if (e == R_GOT || e == R_PLT)
996996
return target->usesOnlyLowPageBits(type) || !config->isPic;
997997

998-
if (sym.isPreemptible)
998+
// R_AARCH64_AUTH_ABS64 requires a dynamic relocation.
999+
if (sym.isPreemptible || e == R_AARCH64_AUTH)
9991000
return false;
10001001
if (!config->isPic)
10011002
return true;
@@ -1141,12 +1142,26 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset,
11411142
(rel == target->symbolicRel && !sym.isPreemptible)) {
11421143
addRelativeReloc<true>(*sec, offset, sym, addend, expr, type);
11431144
return;
1144-
} else if (rel != 0) {
1145+
}
1146+
if (rel != 0) {
11451147
if (config->emachine == EM_MIPS && rel == target->symbolicRel)
11461148
rel = target->relativeRel;
11471149
std::lock_guard<std::mutex> lock(relocMutex);
1148-
sec->getPartition().relaDyn->addSymbolReloc(rel, *sec, offset, sym,
1149-
addend, type);
1150+
Partition &part = sec->getPartition();
1151+
if (config->emachine == EM_AARCH64 && type == R_AARCH64_AUTH_ABS64) {
1152+
// For a preemptible symbol, we can't use a relative relocation. For an
1153+
// undefined symbol, we can't compute offset at link-time and use a
1154+
// relative relocation. Use a symbolic relocation instead.
1155+
if (sym.isPreemptible) {
1156+
part.relaDyn->addSymbolReloc(type, *sec, offset, sym, addend, type);
1157+
} else {
1158+
part.relaDyn->addReloc({R_AARCH64_AUTH_RELATIVE, sec, offset,
1159+
DynamicReloc::AddendOnlyWithTargetVA, sym,
1160+
addend, R_ABS});
1161+
}
1162+
return;
1163+
}
1164+
part.relaDyn->addSymbolReloc(rel, *sec, offset, sym, addend, type);
11501165

11511166
// MIPS ABI turns using of GOT and dynamic relocations inside out.
11521167
// While regular ABI uses dynamic relocations to fill up GOT entries
@@ -1171,7 +1186,10 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset,
11711186

11721187
// When producing an executable, we can perform copy relocations (for
11731188
// STT_OBJECT) and canonical PLT (for STT_FUNC) if sym is defined by a DSO.
1174-
if (!config->shared && sym.isShared()) {
1189+
// Copy relocations/canonical PLT entries are unsupported for
1190+
// R_AARCH64_AUTH_ABS64.
1191+
if (!config->shared && sym.isShared() &&
1192+
!(config->emachine == EM_AARCH64 && type == R_AARCH64_AUTH_ABS64)) {
11751193
if (!canDefineSymbolInExecutable(sym)) {
11761194
errorOrWarn("cannot preempt symbol: " + toString(sym) +
11771195
getLocation(*sec, sym, offset));

lld/ELF/Relocations.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ enum RelExpr {
8787
R_AARCH64_PAGE_PC,
8888
R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC,
8989
R_AARCH64_TLSDESC_PAGE,
90+
R_AARCH64_AUTH,
9091
R_ARM_PCA,
9192
R_ARM_SBREL,
9293
R_MIPS_GOTREL,

lld/ELF/SyntheticSections.cpp

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -314,22 +314,42 @@ 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+
if (config->andFeatures != 0) {
328+
write32(buf + offset + 0, featureAndType); // Feature type
329+
write32(buf + offset + 4, 4); // Feature size
330+
write32(buf + offset + 8, config->andFeatures); // Feature flags
331+
if (config->is64)
332+
write32(buf + offset + 12, 0); // Padding
333+
offset += 16;
334+
}
335+
336+
if (!ctx.aarch64PauthAbiCoreInfo.empty()) {
337+
write32(buf + offset + 0, GNU_PROPERTY_AARCH64_FEATURE_PAUTH);
338+
write32(buf + offset + 4, ctx.aarch64PauthAbiCoreInfo.size());
339+
memcpy(buf + offset + 8, ctx.aarch64PauthAbiCoreInfo.data(),
340+
ctx.aarch64PauthAbiCoreInfo.size());
341+
}
330342
}
331343

332-
size_t GnuPropertySection::getSize() const { return config->is64 ? 32 : 28; }
344+
size_t GnuPropertySection::getSize() const {
345+
uint32_t contentSize = 0;
346+
if (config->andFeatures != 0)
347+
contentSize += config->is64 ? 16 : 12;
348+
if (!ctx.aarch64PauthAbiCoreInfo.empty())
349+
contentSize += 4 + 4 + ctx.aarch64PauthAbiCoreInfo.size();
350+
assert(contentSize != 0);
351+
return contentSize + 16;
352+
}
333353

334354
BuildIdSection::BuildIdSection()
335355
: SyntheticSection(SHF_ALLOC, SHT_NOTE, 4, ".note.gnu.build-id"),

lld/ELF/Writer.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "lld/Common/CommonLinkerContext.h"
2525
#include "lld/Common/Filesystem.h"
2626
#include "lld/Common/Strings.h"
27+
#include "llvm/ADT/STLExtras.h"
2728
#include "llvm/ADT/StringMap.h"
2829
#include "llvm/Support/BLAKE3.h"
2930
#include "llvm/Support/Parallel.h"
@@ -564,7 +565,7 @@ template <class ELFT> void elf::createSyntheticSections() {
564565
in.iplt = std::make_unique<IpltSection>();
565566
add(*in.iplt);
566567

567-
if (config->andFeatures)
568+
if (config->andFeatures || !ctx.aarch64PauthAbiCoreInfo.empty())
568569
add(*make<GnuPropertySection>());
569570

570571
// .note.GNU-stack is always added when we are creating a re-linkable

lld/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ ELF Improvements
2929
* ``--compress-sections <section-glib>=[none|zlib|zstd]`` is added to compress
3030
matched output sections without the ``SHF_ALLOC`` flag.
3131
(`#84855 <https://github.com/llvm/llvm-project/pull/84855>`_)
32+
* ``GNU_PROPERTY_AARCH64_FEATURE_PAUTH`` notes, ``R_AARCH64_AUTH_ABS64`` and
33+
``R_AARCH64_AUTH_RELATIVE`` relocations are now supported.
34+
(`#72714 <https://github.com/llvm/llvm-project/pull/72714>`_)
3235

3336
Breaking changes
3437
----------------

0 commit comments

Comments
 (0)