Skip to content

Commit

Permalink
[lld][AArch64][ELF][PAC] Support AUTH relocations and AUTH ELF marking (
Browse files Browse the repository at this point in the history
#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]>
  • Loading branch information
3 people authored Apr 4, 2024
1 parent 7a8cf95 commit cca9115
Show file tree
Hide file tree
Showing 16 changed files with 403 additions and 56 deletions.
4 changes: 3 additions & 1 deletion lld/ELF/Arch/AArch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
case R_AARCH64_MOVW_UABS_G2_NC:
case R_AARCH64_MOVW_UABS_G3:
return R_ABS;
case R_AARCH64_AUTH_ABS64:
return R_AARCH64_AUTH;
case R_AARCH64_TLSDESC_ADR_PAGE21:
return R_AARCH64_TLSDESC_PAGE;
case R_AARCH64_TLSDESC_LD64_LO12:
Expand Down Expand Up @@ -204,7 +206,7 @@ bool AArch64::usesOnlyLowPageBits(RelType type) const {
}

RelType AArch64::getDynRel(RelType type) const {
if (type == R_AARCH64_ABS64)
if (type == R_AARCH64_ABS64 || type == R_AARCH64_AUTH_ABS64)
return type;
return R_AARCH64_NONE;
}
Expand Down
3 changes: 3 additions & 0 deletions lld/ELF/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ struct Config {
llvm::StringRef cmseOutputLib;
StringRef zBtiReport = "none";
StringRef zCetReport = "none";
StringRef zPauthReport = "none";
bool ltoBBAddrMap;
llvm::StringRef ltoBasicBlockSections;
std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
Expand Down Expand Up @@ -499,6 +500,8 @@ struct Ctx {
void reset();

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

ArrayRef<uint8_t> aarch64PauthAbiCoreInfo;
};

LLVM_LIBRARY_VISIBILITY extern Ctx ctx;
Expand Down
70 changes: 55 additions & 15 deletions lld/ELF/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
#include "lld/Common/Strings.h"
#include "lld/Common/TargetOptionsCommandFlags.h"
#include "lld/Common/Version.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSwitch.h"
Expand Down Expand Up @@ -461,6 +462,8 @@ static void checkOptions() {
error("-z force-bti only supported on AArch64");
if (config->zBtiReport != "none")
error("-z bti-report only supported on AArch64");
if (config->zPauthReport != "none")
error("-z pauth-report only supported on AArch64");
}

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

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

static void reportMissingFeature(StringRef config, const Twine &report) {
if (config == "error")
error(report);
else if (config == "warning")
warn(report);
}

static void checkAndReportMissingFeature(StringRef config, uint32_t features,
uint32_t mask, const Twine &report) {
if (!(features & mask)) {
if (config == "error")
error(report);
else if (config == "warning")
warn(report);
}
if (!(features & mask))
reportMissingFeature(config, report);
}

// To enable CET (x86's hardware-assisted control flow enforcement), each
Expand All @@ -2617,12 +2624,28 @@ static void checkAndReportMissingFeature(StringRef config, uint32_t features,
//
// This is also the case with AARCH64's BTI and PAC which use the similar
// GNU_PROPERTY_AARCH64_FEATURE_1_AND mechanism.
static uint32_t getAndFeatures() {
//
// For AArch64 PAuth-enabled object files, the core info of all of them must
// match. Missing info for some object files with matching info for remaining
// ones can be allowed (see -z pauth-report).
static void readSecurityNotes() {
if (config->emachine != EM_386 && config->emachine != EM_X86_64 &&
config->emachine != EM_AARCH64)
return 0;
return;

config->andFeatures = -1;

StringRef referenceFileName;
if (config->emachine == EM_AARCH64) {
auto it = llvm::find_if(ctx.objectFiles, [](const ELFFileBase *f) {
return !f->aarch64PauthAbiCoreInfo.empty();
});
if (it != ctx.objectFiles.end()) {
ctx.aarch64PauthAbiCoreInfo = (*it)->aarch64PauthAbiCoreInfo;
referenceFileName = (*it)->getName();
}
}

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

Expand Down Expand Up @@ -2658,14 +2681,31 @@ static uint32_t getAndFeatures() {
"GNU_PROPERTY_AARCH64_FEATURE_1_PAC property");
features |= GNU_PROPERTY_AARCH64_FEATURE_1_PAC;
}
ret &= features;
config->andFeatures &= features;

if (ctx.aarch64PauthAbiCoreInfo.empty())
continue;

if (f->aarch64PauthAbiCoreInfo.empty()) {
reportMissingFeature(config->zPauthReport,
toString(f) +
": -z pauth-report: file does not have AArch64 "
"PAuth core info while '" +
referenceFileName + "' has one");
continue;
}

if (ctx.aarch64PauthAbiCoreInfo != f->aarch64PauthAbiCoreInfo)
errorOrWarn("incompatible values of AArch64 PAuth core info found\n>>> " +
referenceFileName + ": 0x" +
toHex(ctx.aarch64PauthAbiCoreInfo, /*LowerCase=*/true) +
"\n>>> " + toString(f) + ": 0x" +
toHex(f->aarch64PauthAbiCoreInfo, /*LowerCase=*/true));
}

// Force enable Shadow Stack.
if (config->zShstk)
ret |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;

return ret;
config->andFeatures |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
}

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

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

// The Target instance handles target-specific stuff, such as applying
// relocations or writing a PLT section. It also contains target-dependent
Expand Down
41 changes: 22 additions & 19 deletions lld/ELF/InputFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -926,25 +926,18 @@ void ObjFile<ELFT>::initializeSections(bool ignoreComdats,
handleSectionGroup<ELFT>(this->sections, entries);
}

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

uint32_t featuresSet = 0;
ArrayRef<uint8_t> data = sec.content();
auto reportFatal = [&](const uint8_t *place, const char *msg) {
auto reportFatal = [&](const uint8_t *place, const Twine &msg) {
fatal(toString(sec.file) + ":(" + sec.name + "+0x" +
Twine::utohexstr(place - sec.content().data()) + "): " + msg);
};
Expand Down Expand Up @@ -983,7 +976,19 @@ template <class ELFT> static uint32_t readAndFeatures(const InputSection &sec) {
// accumulate the bits set.
if (size < 4)
reportFatal(place, "FEATURE_1_AND entry is too short");
featuresSet |= read32<ELFT::Endianness>(desc.data());
f.andFeatures |= read32<ELFT::Endianness>(desc.data());
} else if (config->emachine == EM_AARCH64 &&
type == GNU_PROPERTY_AARCH64_FEATURE_PAUTH) {
if (!f.aarch64PauthAbiCoreInfo.empty()) {
reportFatal(data.data(),
"multiple GNU_PROPERTY_AARCH64_FEATURE_PAUTH entries are "
"not supported");
} else if (size != 16) {
reportFatal(data.data(), "GNU_PROPERTY_AARCH64_FEATURE_PAUTH entry "
"is invalid: expected 16 bytes, but got " +
Twine(size));
}
f.aarch64PauthAbiCoreInfo = desc;
}

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

return featuresSet;
}

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

Expand Down
1 change: 1 addition & 0 deletions lld/ELF/InputFiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ class ELFFileBase : public InputFile {
public:
uint32_t andFeatures = 0;
bool hasCommonSyms = false;
ArrayRef<uint8_t> aarch64PauthAbiCoreInfo;
};

// .o file.
Expand Down
1 change: 1 addition & 0 deletions lld/ELF/InputSection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,7 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type,
case R_DTPREL:
case R_RELAX_TLS_LD_TO_LE_ABS:
case R_RELAX_GOT_PC_NOPIC:
case R_AARCH64_AUTH:
case R_RISCV_ADD:
case R_RISCV_LEB128:
return sym.getVA(a);
Expand Down
28 changes: 23 additions & 5 deletions lld/ELF/Relocations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -995,7 +995,8 @@ bool RelocationScanner::isStaticLinkTimeConstant(RelExpr e, RelType type,
if (e == R_GOT || e == R_PLT)
return target->usesOnlyLowPageBits(type) || !config->isPic;

if (sym.isPreemptible)
// R_AARCH64_AUTH_ABS64 requires a dynamic relocation.
if (sym.isPreemptible || e == R_AARCH64_AUTH)
return false;
if (!config->isPic)
return true;
Expand Down Expand Up @@ -1141,12 +1142,26 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset,
(rel == target->symbolicRel && !sym.isPreemptible)) {
addRelativeReloc<true>(*sec, offset, sym, addend, expr, type);
return;
} else if (rel != 0) {
}
if (rel != 0) {
if (config->emachine == EM_MIPS && rel == target->symbolicRel)
rel = target->relativeRel;
std::lock_guard<std::mutex> lock(relocMutex);
sec->getPartition().relaDyn->addSymbolReloc(rel, *sec, offset, sym,
addend, type);
Partition &part = sec->getPartition();
if (config->emachine == EM_AARCH64 && type == R_AARCH64_AUTH_ABS64) {
// For a preemptible symbol, we can't use a relative relocation. For an
// undefined symbol, we can't compute offset at link-time and use a
// relative relocation. Use a symbolic relocation instead.
if (sym.isPreemptible) {
part.relaDyn->addSymbolReloc(type, *sec, offset, sym, addend, type);
} else {
part.relaDyn->addReloc({R_AARCH64_AUTH_RELATIVE, sec, offset,
DynamicReloc::AddendOnlyWithTargetVA, sym,
addend, R_ABS});
}
return;
}
part.relaDyn->addSymbolReloc(rel, *sec, offset, sym, addend, type);

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

// When producing an executable, we can perform copy relocations (for
// STT_OBJECT) and canonical PLT (for STT_FUNC) if sym is defined by a DSO.
if (!config->shared && sym.isShared()) {
// Copy relocations/canonical PLT entries are unsupported for
// R_AARCH64_AUTH_ABS64.
if (!config->shared && sym.isShared() &&
!(config->emachine == EM_AARCH64 && type == R_AARCH64_AUTH_ABS64)) {
if (!canDefineSymbolInExecutable(sym)) {
errorOrWarn("cannot preempt symbol: " + toString(sym) +
getLocation(*sec, sym, offset));
Expand Down
1 change: 1 addition & 0 deletions lld/ELF/Relocations.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ enum RelExpr {
R_AARCH64_PAGE_PC,
R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC,
R_AARCH64_TLSDESC_PAGE,
R_AARCH64_AUTH,
R_ARM_PCA,
R_ARM_SBREL,
R_MIPS_GOTREL,
Expand Down
40 changes: 30 additions & 10 deletions lld/ELF/SyntheticSections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,22 +314,42 @@ GnuPropertySection::GnuPropertySection()
config->wordsize, ".note.gnu.property") {}

void GnuPropertySection::writeTo(uint8_t *buf) {
write32(buf, 4); // Name size
write32(buf + 4, getSize() - 16); // Content size
write32(buf + 8, NT_GNU_PROPERTY_TYPE_0); // Type
memcpy(buf + 12, "GNU", 4); // Name string

uint32_t featureAndType = config->emachine == EM_AARCH64
? GNU_PROPERTY_AARCH64_FEATURE_1_AND
: GNU_PROPERTY_X86_FEATURE_1_AND;

write32(buf, 4); // Name size
write32(buf + 4, config->is64 ? 16 : 12); // Content size
write32(buf + 8, NT_GNU_PROPERTY_TYPE_0); // Type
memcpy(buf + 12, "GNU", 4); // Name string
write32(buf + 16, featureAndType); // Feature type
write32(buf + 20, 4); // Feature size
write32(buf + 24, config->andFeatures); // Feature flags
if (config->is64)
write32(buf + 28, 0); // Padding
unsigned offset = 16;
if (config->andFeatures != 0) {
write32(buf + offset + 0, featureAndType); // Feature type
write32(buf + offset + 4, 4); // Feature size
write32(buf + offset + 8, config->andFeatures); // Feature flags
if (config->is64)
write32(buf + offset + 12, 0); // Padding
offset += 16;
}

if (!ctx.aarch64PauthAbiCoreInfo.empty()) {
write32(buf + offset + 0, GNU_PROPERTY_AARCH64_FEATURE_PAUTH);
write32(buf + offset + 4, ctx.aarch64PauthAbiCoreInfo.size());
memcpy(buf + offset + 8, ctx.aarch64PauthAbiCoreInfo.data(),
ctx.aarch64PauthAbiCoreInfo.size());
}
}

size_t GnuPropertySection::getSize() const { return config->is64 ? 32 : 28; }
size_t GnuPropertySection::getSize() const {
uint32_t contentSize = 0;
if (config->andFeatures != 0)
contentSize += config->is64 ? 16 : 12;
if (!ctx.aarch64PauthAbiCoreInfo.empty())
contentSize += 4 + 4 + ctx.aarch64PauthAbiCoreInfo.size();
assert(contentSize != 0);
return contentSize + 16;
}

BuildIdSection::BuildIdSection()
: SyntheticSection(SHF_ALLOC, SHT_NOTE, 4, ".note.gnu.build-id"),
Expand Down
3 changes: 2 additions & 1 deletion lld/ELF/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "lld/Common/CommonLinkerContext.h"
#include "lld/Common/Filesystem.h"
#include "lld/Common/Strings.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/BLAKE3.h"
#include "llvm/Support/Parallel.h"
Expand Down Expand Up @@ -564,7 +565,7 @@ template <class ELFT> void elf::createSyntheticSections() {
in.iplt = std::make_unique<IpltSection>();
add(*in.iplt);

if (config->andFeatures)
if (config->andFeatures || !ctx.aarch64PauthAbiCoreInfo.empty())
add(*make<GnuPropertySection>());

// .note.GNU-stack is always added when we are creating a re-linkable
Expand Down
3 changes: 3 additions & 0 deletions lld/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ ELF Improvements
* ``--compress-sections <section-glib>=[none|zlib|zstd]`` is added to compress
matched output sections without the ``SHF_ALLOC`` flag.
(`#84855 <https://github.com/llvm/llvm-project/pull/84855>`_)
* ``GNU_PROPERTY_AARCH64_FEATURE_PAUTH`` notes, ``R_AARCH64_AUTH_ABS64`` and
``R_AARCH64_AUTH_RELATIVE`` relocations are now supported.
(`#72714 <https://github.com/llvm/llvm-project/pull/72714>`_)

Breaking changes
----------------
Expand Down
Loading

0 comments on commit cca9115

Please sign in to comment.