Skip to content

[lld][AArch64][Build Attributes] Add support for AArch64 Build Attributes #142637

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

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions lld/ELF/Arch/AArch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1044,8 +1044,7 @@ AArch64BtiPac::AArch64BtiPac(Ctx &ctx) : AArch64(ctx) {
// instructions.

if (ctx.arg.zPacPlt) {
if (llvm::any_of(ctx.aarch64PauthAbiCoreInfo,
[](uint8_t c) { return c != 0; }))
if (ctx.aarch64PauthAbiCoreInfo && ctx.aarch64PauthAbiCoreInfo->isValid())
pacEntryKind = PEK_Auth;
else
pacEntryKind = PEK_AuthHint;
Expand Down
19 changes: 18 additions & 1 deletion lld/ELF/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,23 @@ enum class GcsPolicy { Implicit, Never, Always };
// For some options that resemble -z bti-report={none,warning,error}
enum class ReportPolicy { None, Warning, Error };

// Describes the signing schema for a file using the PAuth ABI extension.
// Two files are considered compatible when both `platform` and `version` match.
// The pair (0, 0) is reserved to indicate incompatibility with the PAuth ABI.
struct AArch64PauthAbiCoreInfo {
uint64_t platform;
uint64_t version;
// Returns true if the core info is not the reserved (0, 0) value.
bool isValid() const { return platform || version; }
static constexpr size_t size() { return sizeof(platform) + sizeof(version); }
bool operator==(const AArch64PauthAbiCoreInfo &other) const {
return platform == other.platform && version == other.version;
}
bool operator!=(const AArch64PauthAbiCoreInfo &other) const {
return !(*this == other);
}
};

struct SymbolVersion {
llvm::StringRef name;
bool isExternCpp;
Expand Down Expand Up @@ -695,7 +712,7 @@ struct Ctx : CommonLinkerContext {

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

ArrayRef<uint8_t> aarch64PauthAbiCoreInfo;
std::optional<AArch64PauthAbiCoreInfo> aarch64PauthAbiCoreInfo;
};

// The first two elements of versionDefinitions represent VER_NDX_LOCAL and
Expand Down
27 changes: 17 additions & 10 deletions lld/ELF/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2841,15 +2841,15 @@ static void readSecurityNotes(Ctx &ctx) {
StringRef referenceFileName;
if (ctx.arg.emachine == EM_AARCH64) {
auto it = llvm::find_if(ctx.objectFiles, [](const ELFFileBase *f) {
return !f->aarch64PauthAbiCoreInfo.empty();
return f->aarch64PauthAbiCoreInfo.has_value();
});
if (it != ctx.objectFiles.end()) {
ctx.aarch64PauthAbiCoreInfo = (*it)->aarch64PauthAbiCoreInfo;
referenceFileName = (*it)->getName();
}
}
bool hasValidPauthAbiCoreInfo = llvm::any_of(
ctx.aarch64PauthAbiCoreInfo, [](uint8_t c) { return c != 0; });
bool hasValidPauthAbiCoreInfo =
ctx.aarch64PauthAbiCoreInfo && ctx.aarch64PauthAbiCoreInfo->isValid();

auto report = [&](ReportPolicy policy) -> ELFSyncStream {
return {ctx, toDiagLevel(policy)};
Expand Down Expand Up @@ -2909,10 +2909,10 @@ static void readSecurityNotes(Ctx &ctx) {
}
ctx.arg.andFeatures &= features;

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

if (f->aarch64PauthAbiCoreInfo.empty()) {
if (!f->aarch64PauthAbiCoreInfo) {
report(ctx.arg.zPauthReport)
<< f
<< ": -z pauth-report: file does not have AArch64 "
Expand All @@ -2922,11 +2922,18 @@ static void readSecurityNotes(Ctx &ctx) {
}

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

// Force enable Shadow Stack.
Expand Down
116 changes: 105 additions & 11 deletions lld/ELF/InputFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/LTO/LTO.h"
#include "llvm/Object/IRObjectFile.h"
#include "llvm/Support/AArch64AttributeParser.h"
#include "llvm/Support/ARMAttributeParser.h"
#include "llvm/Support/ARMBuildAttributes.h"
#include "llvm/Support/Endian.h"
Expand Down Expand Up @@ -539,6 +540,51 @@ uint32_t ObjFile<ELFT>::getSectionIndex(const Elf_Sym &sym) const {
this);
}

template <class ELFT>
static void
handleAArch64BAAndGnuProperties(ObjFile<ELFT> *file, Ctx &ctx, bool hasGP,
const AArch64BuildAttrSubsections &baInfo,
const GnuPropertiesInfo &gpInfo) {
if (hasGP) {
// Check for data mismatch
if (gpInfo.pauthAbiCoreInfo) {
if (baInfo.Pauth.TagPlatform != gpInfo.pauthAbiCoreInfo->platform ||
baInfo.Pauth.TagSchema != gpInfo.pauthAbiCoreInfo->version)
Err(ctx)
<< file
<< " Pauth Data mismatch: file contains both GNU properties and "
"AArch64 build attributes sections with different Pauth data";
}
if (baInfo.AndFeatures != gpInfo.andFeatures)
Err(ctx) << file
<< " Features Data mismatch: file contains both GNU "
"properties and AArch64 build attributes sections with "
"different And Features data";
} else {
// Write missing data
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a bit in the BuildAttributes spec to handle this case. Starting at https://github.com/ARM-software/abi-aa/pull/230/files#diff-9563b0c9eccaf2fd9c6dd704c6ef00fbe7b531657e220c0324523391da355d1cR1030 and also https://github.com/ARM-software/abi-aa/pull/230/files#diff-9563b0c9eccaf2fd9c6dd704c6ef00fbe7b531657e220c0324523391da355d1cR1053

Essentially a build attributes of TagPlatform 0, TagSchema 1 maps to an explicit GNU Properties section of 0, 0.

I think the simplest way of handling this would be to change TagPlatform 0, TagSchema 1 (which would pass the if test) to an aarch64PauthAbiCoreInfo of (0, 0).

Would likely need the comment updating. Something like:

Build Attributes default to a value of 0 when not present. A (TagPlatform, TagSchema) of (0, 0) maps to no PAuth property present. A (TagPlatform, TagSchema) of (0, 1) maps to an explicit PAuth property of platform = 0, version = 0.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handled

// We can only know when Pauth is missing.
// Unlike AArch64 Build Attributes, GNU properties does not give a way to
// distinguish between no-value given to value of '0' given.
if (baInfo.Pauth.TagPlatform || baInfo.Pauth.TagSchema) {
// According to the BuildAttributes specification Build Attributes
// default to a value of 0 when not present. A (TagPlatform, TagSchema) of
// (0, 0) maps to 'no PAuth property present'. A (TagPlatform, TagSchema)
// of (0, 1) maps to an explicit PAuth property of platform = 0, version =
// 0 ('Invalid').
if (baInfo.Pauth.TagPlatform == 0 && baInfo.Pauth.TagSchema == 1) {
file->aarch64PauthAbiCoreInfo = {0, 0};
}
file->aarch64PauthAbiCoreInfo = {baInfo.Pauth.TagPlatform,
baInfo.Pauth.TagSchema};
}
file->andFeatures = baInfo.AndFeatures;
}
}

template <typename ELFT>
static GnuPropertiesInfo readGnuProperty(Ctx &, const InputSection &,
ObjFile<ELFT> &);

template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
object::ELFFile<ELFT> obj = this->getObj();
// Read a section table. justSymbols is usually false.
Expand All @@ -554,8 +600,31 @@ template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
StringRef shstrtab = CHECK2(obj.getSectionStringTable(objSections), this);
uint64_t size = objSections.size();
sections.resize(size);

// For handling AArch64 Build attributes and GNU properties
AArch64BuildAttrSubsections aarch64BAsubSections;
GnuPropertiesInfo gnuProperty;
bool hasAArch64BuildAttributes = false;
bool hasGNUProperties = false;

for (size_t i = 0; i != size; ++i) {
const Elf_Shdr &sec = objSections[i];
// Object files that use processor features such as Intel Control-Flow
// Enforcement (CET) or AArch64 Branch Target Identification BTI, use a
// .note.gnu.property section containing a bitfield of feature bits like the
// GNU_PROPERTY_X86_FEATURE_1_IBT flag. Read a bitmap containing the flag.
if (check(obj.getSectionName(sec, shstrtab)) == ".note.gnu.property") {
gnuProperty = readGnuProperty(
ctx,
InputSection(*this, sec, check(obj.getSectionName(sec, shstrtab))),
*this);
hasGNUProperties = true;
// Since we merge bitmaps from multiple object files to create a new
// .note.gnu.property containing a single AND'ed bitmap, we discard an
// input file's .note.gnu.property section.
sections[i] = &InputSection::discarded;
}

if (LLVM_LIKELY(sec.sh_type == SHT_PROGBITS))
continue;
if (LLVM_LIKELY(sec.sh_type == SHT_GROUP)) {
Expand Down Expand Up @@ -639,13 +708,27 @@ template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
}
break;
case EM_AARCH64:
// FIXME: BuildAttributes have been implemented in llvm, but not yet in
// lld. Remove the section so that it does not accumulate in the output
// file. When support is implemented we expect not to output a build
// attributes section in files of type ET_EXEC or ET_SHARED, but ld -r
// ouptut will need a single merged attributes section.
if (sec.sh_type == SHT_AARCH64_ATTRIBUTES)
// At this stage AArch64 Build Attributes does not replace GNU Properties.
// When both exists, their values must match.
// When both exists and contain different attributes, they complement each
// other. Currently attributes are represented in the linked object file
// as GNU properties, which are already supported by the Linux kernel and
// the dynamic loader. In the future, when relocatable linking (`-r` flag)
// is performed, a single merged AArch64 Build Attributes section will be
// emitted.
if (sec.sh_type == SHT_AARCH64_ATTRIBUTES) {
ArrayRef<uint8_t> contents = check(obj.getSectionContents(sec));
AArch64AttributeParser attributes;
StringRef name = check(obj.getSectionName(sec, shstrtab));
InputSection isec(*this, sec, name);
if (Error e = attributes.parse(contents, ELFT::Endianness)) {
Warn(ctx) << &isec << ": " << std::move(e);
} else {
aarch64BAsubSections = extractBuildAttributesSubsections(attributes);
hasAArch64BuildAttributes = true;
}
sections[i] = &InputSection::discarded;
}
// Producing a static binary with MTE globals is not currently supported,
// remove all SHT_AARCH64_MEMTAG_GLOBALS_STATIC sections as they're unused
// medatada, and we don't want them to end up in the output file for
Expand All @@ -657,6 +740,14 @@ template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
}
}

if (hasAArch64BuildAttributes) {
// Handle AArch64 Build Attributes and GNU properties:
// - Err on mismatched values.
// - Store missing values as GNU properties.
handleAArch64BAAndGnuProperties<ELFT>(this, ctx, hasGNUProperties,
aarch64BAsubSections, gnuProperty);
}

// Read a symbol table.
initializeSymbols(obj);
}
Expand Down Expand Up @@ -950,7 +1041,7 @@ static void parseGnuPropertyNote(Ctx &ctx, ELFFileBase &f,
} else if (ctx.arg.emachine == EM_AARCH64 &&
type == GNU_PROPERTY_AARCH64_FEATURE_PAUTH) {
ArrayRef<uint8_t> contents = data ? *data : desc;
if (!f.aarch64PauthAbiCoreInfo.empty()) {
if (f.aarch64PauthAbiCoreInfo) {
return void(
err(contents.data())
<< "multiple GNU_PROPERTY_AARCH64_FEATURE_PAUTH entries are "
Expand All @@ -961,7 +1052,9 @@ static void parseGnuPropertyNote(Ctx &ctx, ELFFileBase &f,
"is invalid: expected 16 bytes, but got "
<< size);
}
f.aarch64PauthAbiCoreInfo = desc;
f.aarch64PauthAbiCoreInfo = {
support::endian::read64<ELFT::Endianness>(&desc[0]),
support::endian::read64<ELFT::Endianness>(&desc[8])};
}

// Padding is present in the note descriptor, if necessary.
Expand All @@ -974,8 +1067,8 @@ static void parseGnuPropertyNote(Ctx &ctx, ELFFileBase &f,
// hardware-assisted call flow control;
// - AArch64 PAuth ABI core info (16 bytes).
template <class ELFT>
static void readGnuProperty(Ctx &ctx, const InputSection &sec,
ObjFile<ELFT> &f) {
static GnuPropertiesInfo readGnuProperty(Ctx &ctx, const InputSection &sec,
ObjFile<ELFT> &f) {
using Elf_Nhdr = typename ELFT::Nhdr;
using Elf_Note = typename ELFT::Note;

Expand All @@ -991,7 +1084,7 @@ static void readGnuProperty(Ctx &ctx, const InputSection &sec,
auto *nhdr = reinterpret_cast<const Elf_Nhdr *>(data.data());
if (data.size() < sizeof(Elf_Nhdr) ||
data.size() < nhdr->getSize(sec.addralign))
return void(err(data.data()) << "data is too short");
return (err(data.data()) << "data is too short", GnuPropertiesInfo{});

Elf_Note note(*nhdr);
if (nhdr->n_type != NT_GNU_PROPERTY_TYPE_0 || note.getName() != "GNU") {
Expand All @@ -1011,6 +1104,7 @@ static void readGnuProperty(Ctx &ctx, const InputSection &sec,
// Go to next NOTE record to look for more FEATURE_1_AND descriptions.
data = data.slice(nhdr->getSize(sec.addralign));
}
return GnuPropertiesInfo{f.andFeatures, f.aarch64PauthAbiCoreInfo};
}

template <class ELFT>
Expand Down
7 changes: 6 additions & 1 deletion lld/ELF/InputFiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,12 @@ class ELFFileBase : public InputFile {
StringRef sourceFile;
uint32_t andFeatures = 0;
bool hasCommonSyms = false;
ArrayRef<uint8_t> aarch64PauthAbiCoreInfo;
std::optional<AArch64PauthAbiCoreInfo> aarch64PauthAbiCoreInfo;
};

struct GnuPropertiesInfo {
uint32_t andFeatures = 0;
std::optional<AArch64PauthAbiCoreInfo> pauthAbiCoreInfo;
};

// .o file.
Expand Down
14 changes: 7 additions & 7 deletions lld/ELF/SyntheticSections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -344,20 +344,20 @@ void GnuPropertySection::writeTo(uint8_t *buf) {
offset += 16;
}

if (!ctx.aarch64PauthAbiCoreInfo.empty()) {
if (ctx.aarch64PauthAbiCoreInfo) {
write32(ctx, buf + offset + 0, GNU_PROPERTY_AARCH64_FEATURE_PAUTH);
write32(ctx, buf + offset + 4, ctx.aarch64PauthAbiCoreInfo.size());
memcpy(buf + offset + 8, ctx.aarch64PauthAbiCoreInfo.data(),
ctx.aarch64PauthAbiCoreInfo.size());
write32(ctx, buf + offset + 4, AArch64PauthAbiCoreInfo::size());
write64(ctx, buf + offset + 8, ctx.aarch64PauthAbiCoreInfo->platform);
write64(ctx, buf + offset + 16, ctx.aarch64PauthAbiCoreInfo->version);
}
}

size_t GnuPropertySection::getSize() const {
uint32_t contentSize = 0;
if (ctx.arg.andFeatures != 0)
contentSize += ctx.arg.is64 ? 16 : 12;
if (!ctx.aarch64PauthAbiCoreInfo.empty())
contentSize += 4 + 4 + ctx.aarch64PauthAbiCoreInfo.size();
if (ctx.aarch64PauthAbiCoreInfo)
contentSize += 4 + 4 + AArch64PauthAbiCoreInfo::size();
assert(contentSize != 0);
return contentSize + 16;
}
Expand Down Expand Up @@ -4959,7 +4959,7 @@ template <class ELFT> void elf::createSyntheticSections(Ctx &ctx) {
ctx.in.iplt = std::make_unique<IpltSection>(ctx);
add(*ctx.in.iplt);

if (ctx.arg.andFeatures || !ctx.aarch64PauthAbiCoreInfo.empty()) {
if (ctx.arg.andFeatures || ctx.aarch64PauthAbiCoreInfo) {
ctx.in.gnuProperty = std::make_unique<GnuPropertySection>(ctx);
add(*ctx.in.gnuProperty);
}
Expand Down
10 changes: 10 additions & 0 deletions lld/test/ELF/Inputs/aarch64-func3-pac-replace.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Declare file properties exclusively with aarch64 build attributes.

.aeabi_subsection aeabi_feature_and_bits, optional, uleb128
.aeabi_attribute Tag_Feature_PAC, 1

.text
.globl func3
.type func3,@function
func3:
ret
13 changes: 13 additions & 0 deletions lld/test/ELF/Inputs/aarch64-pac1-replace.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// This file replace gnu properties with aarch64 build attributes.

.aeabi_subsection aeabi_feature_and_bits, optional, uleb128
.aeabi_attribute Tag_Feature_PAC, 1

.text
.globl func2
.type func2,@function
func2:
.globl func3
.type func3, @function
bl func3
ret
Loading