-
Notifications
You must be signed in to change notification settings - Fork 12.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[lld][AArch64][ELF][PAC] Support AUTH relocations and AUTH ELF marking #72714
Changes from 1 commit
c493d78
589c645
a021f15
d341159
b791da9
b95dcf8
594f8a0
d793c0c
acb1730
de73eae
321a6a9
aff3a96
4e53afa
ec1c632
2136ad1
9ea8c5c
e3a64d2
865c983
1448a4a
203f61e
1b8c52b
e9b5337
aec9155
b215b0d
90dbcf4
d32b8e3
978ed06
ec04d8c
728c933
4409838
128831c
83eddae
08c749d
e4a37a6
05cf361
eb02b6a
f459527
2846e76
45fcbb9
0eb272a
964e9fe
2e8ec83
99df511
b6e0e28
cbc380a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -65,6 +65,7 @@ | |
#include "llvm/Support/TargetSelect.h" | ||
#include "llvm/Support/TimeProfiler.h" | ||
#include "llvm/Support/raw_ostream.h" | ||
#include <algorithm> | ||
#include <cstdlib> | ||
#include <tuple> | ||
#include <utility> | ||
|
@@ -459,6 +460,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"); | ||
MaskRay marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
if (config->emachine != EM_386 && config->emachine != EM_X86_64 && | ||
|
@@ -558,6 +561,7 @@ constexpr const char *knownZFlags[] = { | |
"nognustack", | ||
"nokeep-text-section-prefix", | ||
"nopack-relative-relocs", | ||
"nopack-relative-auth-relocs", | ||
"norelro", | ||
"noseparate-code", | ||
"nostart-stop-gc", | ||
|
@@ -566,6 +570,7 @@ constexpr const char *knownZFlags[] = { | |
"origin", | ||
"pac-plt", | ||
"pack-relative-relocs", | ||
"pack-relative-auth-relocs", | ||
MaskRay marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"rel", | ||
"rela", | ||
"relro", | ||
|
@@ -583,7 +588,7 @@ constexpr const char *knownZFlags[] = { | |
static bool isKnownZFlag(StringRef s) { | ||
return llvm::is_contained(knownZFlags, s) || | ||
s.starts_with("common-page-size=") || s.starts_with("bti-report=") || | ||
s.starts_with("cet-report=") || | ||
s.starts_with("cet-report=") || s.starts_with("pauth-report=") || | ||
s.starts_with("dead-reloc-in-nonalloc=") || | ||
s.starts_with("max-page-size=") || s.starts_with("stack-size=") || | ||
s.starts_with("start-stop-visibility="); | ||
|
@@ -1514,7 +1519,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('='); | ||
|
@@ -1671,6 +1677,9 @@ static void readConfigs(opt::InputArgList &args) { | |
getPackDynRelocs(args); | ||
} | ||
|
||
config->relrPackAuthDynRelocs = getZFlag( | ||
args, "pack-relative-auth-relocs", "nopack-relative-auth-relocs", false); | ||
|
||
if (auto *arg = args.getLastArg(OPT_symbol_ordering_file)){ | ||
if (args.hasArg(OPT_call_graph_ordering_file)) | ||
error("--symbol-ordering-file and --call-graph-order-file " | ||
|
@@ -2639,6 +2648,47 @@ static uint32_t getAndFeatures() { | |
return ret; | ||
} | ||
|
||
static void getAarch64PauthInfo() { | ||
if (ctx.objectFiles.empty()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. delete redundant empty check There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Deleted, thanks, see aff3a96 |
||
return; | ||
|
||
auto NonEmptyIt = std::find_if( | ||
ctx.objectFiles.begin(), ctx.objectFiles.end(), | ||
[](const ELFFileBase *f) { return !f->aarch64PauthAbiTag.empty(); }); | ||
if (NonEmptyIt == ctx.objectFiles.end()) | ||
MaskRay marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return; | ||
|
||
ctx.aarch64PauthAbiTag = (*NonEmptyIt)->aarch64PauthAbiTag; | ||
StringRef F1 = (*NonEmptyIt)->getName(); | ||
for (ELFFileBase *F : ArrayRef(ctx.objectFiles)) { | ||
MaskRay marked this conversation as resolved.
Show resolved
Hide resolved
|
||
StringRef F2 = F->getName(); | ||
const SmallVector<uint8_t, 0> &D1 = ctx.aarch64PauthAbiTag; | ||
const SmallVector<uint8_t, 0> &D2 = F->aarch64PauthAbiTag; | ||
if (D1.empty() != D2.empty()) { | ||
auto Helper = [](StringRef Report, const Twine &Msg) { | ||
if (Report == "warning") | ||
warn(Msg); | ||
else if (Report == "error") | ||
error(Msg); | ||
}; | ||
|
||
Helper(config->zPauthReport, | ||
(D1.empty() ? F1.str() : F2.str()) + | ||
" has no AArch64 PAuth compatibility info while " + | ||
(D1.empty() ? F2.str() : F1.str()) + | ||
" has one; either all or no input files must have it"); | ||
} | ||
|
||
if (!D1.empty() && !D2.empty() && | ||
!std::equal(D1.begin(), D1.end(), D2.begin(), D2.end())) | ||
errorOrWarn( | ||
"incompatible values of AArch64 PAuth compatibility info found" | ||
"\n" + | ||
F1 + ": 0x" + toHex(ArrayRef(D1.data(), D1.size())) + "\n" + F2 + | ||
": 0x" + toHex(ArrayRef(D2.data(), D2.size()))); | ||
} | ||
} | ||
|
||
static void initSectionsAndLocalSyms(ELFFileBase *file, bool ignoreComdats) { | ||
switch (file->ekind) { | ||
case ELF32LEKind: | ||
|
@@ -2976,6 +3026,9 @@ void LinkerDriver::link(opt::InputArgList &args) { | |
// contain a hint to tweak linker's and loader's behaviors. | ||
config->andFeatures = getAndFeatures(); | ||
|
||
if (config->emachine == EM_AARCH64) | ||
getAarch64PauthInfo(); | ||
MaskRay marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// The Target instance handles target-specific stuff, such as applying | ||
// relocations or writing a PLT section. It also contains target-dependent | ||
// values such as a default image base address. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -962,6 +962,44 @@ template <class ELFT> static uint32_t readAndFeatures(const InputSection &sec) { | |
return featuresSet; | ||
} | ||
|
||
// Extract compatibility info for aarch64 pointer authentication from the | ||
// .note.AARCH64-PAUTH-ABI-tag section and write it to the corresponding ObjFile | ||
// field. See the following ABI documentation: | ||
// https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst#elf-marking | ||
template <class ELFT> | ||
static void readAArch64PauthAbiTag(const InputSection &sec, ObjFile<ELFT> &f) { | ||
using Elf_Nhdr = typename ELFT::Nhdr; | ||
using Elf_Note = typename ELFT::Note; | ||
ArrayRef<uint8_t> data = sec.content(); | ||
auto reportError = [&](const Twine &msg) { | ||
errorOrWarn(toString(sec.file) + ":(" + sec.name + "): " + msg); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unneeded helper. Just use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed, thanks, see 4e53afa |
||
}; | ||
|
||
auto *nhdr = reinterpret_cast<const Elf_Nhdr *>(data.data()); | ||
if (data.size() < sizeof(Elf_Nhdr) || | ||
data.size() < nhdr->getSize(sec.addralign)) { | ||
reportError("section is too short"); | ||
return; | ||
} | ||
|
||
Elf_Note note(*nhdr); | ||
if (nhdr->n_type != NT_ARM_TYPE_PAUTH_ABI_TAG) | ||
reportError("invalid type field value " + Twine(nhdr->n_type) + " (" + | ||
Twine(NT_ARM_TYPE_PAUTH_ABI_TAG) + " expected)"); | ||
if (note.getName() != "ARM") | ||
reportError("invalid name field value " + note.getName() + | ||
" (ARM expected)"); | ||
|
||
ArrayRef<uint8_t> desc = note.getDesc(sec.addralign); | ||
if (desc.size() < 16) { | ||
reportError("too short AArch64 PAuth compatibility info " | ||
MaskRay marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"(at least 16 bytes expected)"); | ||
return; | ||
} | ||
|
||
f.aarch64PauthAbiTag = SmallVector<uint8_t, 0>(iterator_range(desc)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Change There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed both in |
||
} | ||
|
||
template <class ELFT> | ||
InputSectionBase *ObjFile<ELFT>::getRelocTarget(uint32_t idx, | ||
const Elf_Shdr &sec, | ||
|
@@ -1020,6 +1058,12 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(uint32_t idx, | |
return &InputSection::discarded; | ||
} | ||
|
||
if (config->emachine == EM_AARCH64 && | ||
name == ".note.AARCH64-PAUTH-ABI-tag") { | ||
readAArch64PauthAbiTag<ELFT>(InputSection(*this, sec, name), *this); | ||
return &InputSection::discarded; | ||
} | ||
|
||
// Split stacks is a feature to support a discontiguous stack, | ||
// commonly used in the programming language Go. For the details, | ||
// see https://gcc.gnu.org/wiki/SplitStacks. An object file compiled | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -331,6 +331,29 @@ void GnuPropertySection::writeTo(uint8_t *buf) { | |
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. unneeded blank line There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed, thanks, see 2e8ec83 |
||
size_t GnuPropertySection::getSize() const { return config->is64 ? 32 : 28; } | ||
|
||
AArch64PauthAbiTag::AArch64PauthAbiTag() | ||
: SyntheticSection(llvm::ELF::SHF_ALLOC, llvm::ELF::SHT_NOTE, | ||
config->wordsize, ".note.AARCH64-PAUTH-ABI-tag") {} | ||
|
||
bool AArch64PauthAbiTag::isNeeded() const { | ||
return !ctx.aarch64PauthAbiTag.empty(); | ||
} | ||
|
||
void AArch64PauthAbiTag::writeTo(uint8_t *buf) { | ||
const SmallVector<uint8_t, 0> &data = ctx.aarch64PauthAbiTag; | ||
write32(buf, 4); // Name size | ||
write32(buf + 4, data.size()); // Content size | ||
write32(buf + 8, NT_ARM_TYPE_PAUTH_ABI_TAG); // Type | ||
memcpy(buf + 12, "ARM", 4); // Name string | ||
memcpy(buf + 16, data.data(), data.size()); | ||
memset(buf + 16 + data.size(), 0, getSize() - 16 - data.size()); // Padding | ||
} | ||
|
||
size_t AArch64PauthAbiTag::getSize() const { | ||
return alignToPowerOf2(16 + ctx.aarch64PauthAbiTag.size(), | ||
config->is64 ? 8 : 4); | ||
} | ||
|
||
BuildIdSection::BuildIdSection() | ||
: SyntheticSection(SHF_ALLOC, SHT_NOTE, 4, ".note.gnu.build-id"), | ||
hashSize(getHashSize()) {} | ||
|
@@ -1406,6 +1429,12 @@ DynamicSection<ELFT>::computeContents() { | |
addInt(config->useAndroidRelrTags ? DT_ANDROID_RELRENT : DT_RELRENT, | ||
sizeof(Elf_Relr)); | ||
} | ||
if (part.relrAuthDyn && part.relrAuthDyn->getParent() && | ||
!part.relrAuthDyn->relocs.empty()) { | ||
addInSec(DT_AARCH64_AUTH_RELR, *part.relrAuthDyn); | ||
addInt(DT_AARCH64_AUTH_RELRSZ, part.relrAuthDyn->getParent()->size); | ||
addInt(DT_AARCH64_AUTH_RELRENT, sizeof(Elf_Relr)); | ||
} | ||
// .rel[a].plt section usually consists of two parts, containing plt and | ||
// iplt relocations. It is possible to have only iplt relocations in the | ||
// 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) { | |
} | ||
} | ||
|
||
RelrBaseSection::RelrBaseSection(unsigned concurrency) | ||
: SyntheticSection(SHF_ALLOC, | ||
config->useAndroidRelrTags ? SHT_ANDROID_RELR : SHT_RELR, | ||
config->wordsize, ".relr.dyn"), | ||
RelrBaseSection::RelrBaseSection(unsigned concurrency, bool isAArch64Auth) | ||
: SyntheticSection( | ||
SHF_ALLOC, | ||
isAArch64Auth | ||
? SHT_AARCH64_AUTH_RELR | ||
: (config->useAndroidRelrTags ? SHT_ANDROID_RELR : SHT_RELR), | ||
config->wordsize, isAArch64Auth ? ".relr.auth.dyn" : ".relr.dyn"), | ||
relocsVec(concurrency) {} | ||
|
||
void RelrBaseSection::mergeRels() { | ||
|
@@ -1988,8 +2020,8 @@ bool AndroidPackedRelocationSection<ELFT>::updateAllocSize() { | |
} | ||
|
||
template <class ELFT> | ||
RelrSection<ELFT>::RelrSection(unsigned concurrency) | ||
: RelrBaseSection(concurrency) { | ||
RelrSection<ELFT>::RelrSection(unsigned concurrency, bool isAArch64Auth) | ||
: RelrBaseSection(concurrency, isAArch64Auth) { | ||
this->entsize = config->wordsize; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -144,6 +144,16 @@ class GnuPropertySection final : public SyntheticSection { | |
size_t getSize() const override; | ||
}; | ||
|
||
// .note.AARCH64-PAUTH-ABI-tag section. See | ||
// https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst#elf-marking | ||
class AArch64PauthAbiTag final : public SyntheticSection { | ||
public: | ||
AArch64PauthAbiTag(); | ||
void writeTo(uint8_t *buf) override; | ||
size_t getSize() const override; | ||
bool isNeeded() const override; | ||
}; | ||
|
||
// .note.gnu.build-id section. | ||
class BuildIdSection : public SyntheticSection { | ||
// First 16 bytes are a header. | ||
|
@@ -543,7 +553,8 @@ class RelocationBaseSection : public SyntheticSection { | |
static bool classof(const SectionBase *d) { | ||
return SyntheticSection::classof(d) && | ||
(d->type == llvm::ELF::SHT_RELA || d->type == llvm::ELF::SHT_REL || | ||
d->type == llvm::ELF::SHT_RELR); | ||
d->type == llvm::ELF::SHT_RELR || | ||
d->type == llvm::ELF::SHT_AARCH64_AUTH_RELR); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added, thanks, see de73eae |
||
} | ||
int32_t dynamicTag, sizeDynamicTag; | ||
SmallVector<DynamicReloc, 0> relocs; | ||
|
@@ -599,7 +610,7 @@ struct RelativeReloc { | |
|
||
class RelrBaseSection : public SyntheticSection { | ||
public: | ||
RelrBaseSection(unsigned concurrency); | ||
RelrBaseSection(unsigned concurrency, bool isAArch64Auth = false); | ||
void mergeRels(); | ||
bool isNeeded() const override { | ||
return !relocs.empty() || | ||
|
@@ -617,7 +628,7 @@ template <class ELFT> class RelrSection final : public RelrBaseSection { | |
using Elf_Relr = typename ELFT::Relr; | ||
|
||
public: | ||
RelrSection(unsigned concurrency); | ||
RelrSection(unsigned concurrency, bool isAArch64Auth = false); | ||
|
||
bool updateAllocSize() override; | ||
size_t getSize() const override { return relrRelocs.size() * this->entsize; } | ||
|
@@ -1319,6 +1330,7 @@ struct Partition { | |
std::unique_ptr<PackageMetadataNote> packageMetadataNote; | ||
std::unique_ptr<RelocationBaseSection> relaDyn; | ||
std::unique_ptr<RelrBaseSection> relrDyn; | ||
std::unique_ptr<RelrBaseSection> relrAuthDyn; | ||
std::unique_ptr<VersionDefinitionSection> verDef; | ||
std::unique_ptr<SyntheticSection> verNeed; | ||
std::unique_ptr<VersionTableSection> verSym; | ||
|
@@ -1363,6 +1375,7 @@ struct InStruct { | |
std::unique_ptr<StringTableSection> strTab; | ||
std::unique_ptr<SymbolTableBaseSection> symTab; | ||
std::unique_ptr<SymtabShndxSection> symTabShndx; | ||
std::unique_ptr<AArch64PauthAbiTag> aarch64PauthAbiTag; | ||
|
||
void reset(); | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
checkIntUInt
can be anassert(isInt<32>(val))
thanks toisInt<32>(sym.getVA(addend))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I just realize that this is related to isStaticLinkTimeConstant. See the main comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I just realized
-no-pie
was not handled properly, thanks for bringing attention to this. I've applied the commit 865c983 from your branch, thanks.P.S. Code here becomes changed after e9b5337