Skip to content

Commit

Permalink
Support GNU_PROPERTY_AARCH64_FEATURE_PAUTH
Browse files Browse the repository at this point in the history
  • Loading branch information
kovdan01 committed Feb 7, 2024
1 parent 908da16 commit f39ea06
Show file tree
Hide file tree
Showing 19 changed files with 291 additions and 65 deletions.
14 changes: 14 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1075,6 +1075,20 @@ void CodeGenModule::Release() {
if (!LangOpts.isSignReturnAddressWithAKey())
getModule().addModuleFlag(llvm::Module::Min,
"sign-return-address-with-bkey", 1);

if (getTriple().isOSBinFormatELF()) {
uint64_t PAuthABIVersion =
(LangOpts.PointerAuthCalls << 0) |
(LangOpts.PointerAuthReturns << 1) |
(LangOpts.PointerAuthVTPtrAddressDiscrimination << 2) |
(LangOpts.PointerAuthVTPtrTypeDiscrimination << 3) |
(LangOpts.PointerAuthInitFini << 4);
if (PAuthABIVersion != 0) {
getModule().addModuleFlag(llvm::Module::Error, "pauthabi-platform", 2);
getModule().addModuleFlag(llvm::Module::Error, "pauthabi-version",
PAuthABIVersion);
}
}
}

if (!CodeGenOpts.MemoryProfileOutput.empty()) {
Expand Down
3 changes: 2 additions & 1 deletion lld/ELF/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/GlobPattern.h"
#include "llvm/Support/PrettyStackTrace.h"
#include <array>
#include <atomic>
#include <memory>
#include <optional>
Expand Down Expand Up @@ -492,7 +493,7 @@ struct Ctx {

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

SmallVector<uint8_t, 0> aarch64PauthAbiTag;
std::optional<std::array<uint8_t, 16>> aarch64PauthAbiTag;
};

LLVM_LIBRARY_VISIBILITY extern Ctx ctx;
Expand Down
20 changes: 10 additions & 10 deletions lld/ELF/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2662,17 +2662,17 @@ static void getAarch64PauthInfo() {

auto NonEmptyIt = std::find_if(
ctx.objectFiles.begin(), ctx.objectFiles.end(),
[](const ELFFileBase *f) { return !f->aarch64PauthAbiTag.empty(); });
[](const ELFFileBase *f) { return f->aarch64PauthAbiTag.has_value(); });
if (NonEmptyIt == ctx.objectFiles.end())
return;

ctx.aarch64PauthAbiTag = (*NonEmptyIt)->aarch64PauthAbiTag;
StringRef F1 = (*NonEmptyIt)->getName();
for (ELFFileBase *F : ArrayRef(ctx.objectFiles)) {
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()) {
std::optional<std::array<uint8_t, 16>> D1 = ctx.aarch64PauthAbiTag;
std::optional<std::array<uint8_t, 16>> D2 = F->aarch64PauthAbiTag;
if (D1.has_value() != D2.has_value()) {
auto Helper = [](StringRef Report, const Twine &Msg) {
if (Report == "warning")
warn(Msg);
Expand All @@ -2681,19 +2681,19 @@ static void getAarch64PauthInfo() {
};

Helper(config->zPauthReport,
(D1.empty() ? F1.str() : F2.str()) +
(D1.has_value() ? F2.str() : F1.str()) +
" has no AArch64 PAuth compatibility info while " +
(D1.empty() ? F2.str() : F1.str()) +
(D1.has_value() ? F1.str() : F2.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()))
if (!D1.has_value() && !D2.has_value() &&
!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())));
F1 + ": 0x" + toHex(ArrayRef(D1->data(), D1->size())) + "\n" + F2 +
": 0x" + toHex(ArrayRef(D2->data(), D2->size())));
}
}

Expand Down
40 changes: 33 additions & 7 deletions lld/ELF/InputFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
#include "llvm/Support/TarWriter.h"
#include "llvm/Support/raw_ostream.h"

#include <tuple>

using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
Expand Down Expand Up @@ -878,11 +880,14 @@ void ObjFile<ELFT>::initializeSections(bool ignoreComdats,
// 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) {
template <class ELFT>
static std::pair<uint32_t, std::optional<std::array<uint8_t, 16>>>
readGnuProperty(const InputSection &sec) {
using Elf_Nhdr = typename ELFT::Nhdr;
using Elf_Note = typename ELFT::Note;

uint32_t featuresSet = 0;
std::optional<std::array<uint8_t, 16>> aarch64PauthAbiTag;
ArrayRef<uint8_t> data = sec.content();
auto reportFatal = [&](const uint8_t *place, const char *msg) {
fatal(toString(sec.file) + ":(" + sec.name + "+0x" +
Expand Down Expand Up @@ -924,6 +929,19 @@ template <class ELFT> static uint32_t readAndFeatures(const InputSection &sec) {
if (size < 4)
reportFatal(place, "FEATURE_1_AND entry is too short");
featuresSet |= read32<ELFT::TargetEndianness>(desc.data());
} else if (config->emachine == EM_AARCH64 &&
type == GNU_PROPERTY_AARCH64_FEATURE_PAUTH) {
if (aarch64PauthAbiTag != std::nullopt)
reportFatal(data.data(),
"multiple GNU_PROPERTY_AARCH64_FEATURE_PAUTH properties "
"are not allowed");
if (size != 16)
reportFatal(
data.data(),
"size of GNU_PROPERTY_AARCH64_FEATURE_PAUTH property must be 16");
aarch64PauthAbiTag = std::array<uint8_t, 16>{};
memcpy(aarch64PauthAbiTag->data(), desc.data(),
aarch64PauthAbiTag->size());
}

// Padding is present in the note descriptor, if necessary.
Expand All @@ -934,7 +952,7 @@ template <class ELFT> static uint32_t readAndFeatures(const InputSection &sec) {
data = data.slice(nhdr->getSize(sec.addralign));
}

return featuresSet;
return {featuresSet, aarch64PauthAbiTag};
}

// Extract compatibility info for aarch64 pointer authentication from the
Expand Down Expand Up @@ -966,13 +984,15 @@ static void readAArch64PauthAbiTag(const InputSection &sec, ObjFile<ELFT> &f) {
" (ARM expected)");

ArrayRef<uint8_t> desc = note.getDesc(sec.addralign);
if (desc.size() < 16) {
reportError("too short AArch64 PAuth compatibility info "
"(at least 16 bytes expected)");
if (desc.size() != 16) {
reportError("invalid AArch64 PAuth compatibility info length "
"(exactly 16 bytes expected)");
return;
}

f.aarch64PauthAbiTag = SmallVector<uint8_t, 0>(iterator_range(desc));
f.aarch64PauthAbiTag = std::array<uint8_t, 16>{};
memcpy(f.aarch64PauthAbiTag->data(), desc.data(),
f.aarch64PauthAbiTag->size());
}

template <class ELFT>
Expand Down Expand Up @@ -1029,12 +1049,18 @@ 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));
std::tie(this->andFeatures, this->aarch64PauthAbiTag) =
readGnuProperty<ELFT>(InputSection(*this, sec, name));
return &InputSection::discarded;
}

if (config->emachine == EM_AARCH64 &&
name == ".note.AARCH64-PAUTH-ABI-tag") {
// TODO: proper handling of both ways of ELF marking in one file
// To be implemented after the following PRs are merged:
// - https://github.com/ARM-software/abi-aa/pull/240
// - https://github.com/llvm/llvm-project/pull/72714
assert(this->aarch64PauthAbiTag == std::nullopt);
readAArch64PauthAbiTag<ELFT>(InputSection(*this, sec, name), *this);
return &InputSection::discarded;
}
Expand Down
2 changes: 1 addition & 1 deletion lld/ELF/InputFiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ class ELFFileBase : public InputFile {
public:
uint32_t andFeatures = 0;
bool hasCommonSyms = false;
SmallVector<uint8_t, 0> aarch64PauthAbiTag;
std::optional<std::array<uint8_t, 16>> aarch64PauthAbiTag;
};

// .o file.
Expand Down
52 changes: 35 additions & 17 deletions lld/ELF/SyntheticSections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,33 +314,56 @@ 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.aarch64PauthAbiTag != std::nullopt) {
write32(buf + offset + 0, GNU_PROPERTY_AARCH64_FEATURE_PAUTH);
write32(buf + offset + 4, 8 * 2);
memcpy(buf + offset + 8, ctx.aarch64PauthAbiTag->data(),
ctx.aarch64PauthAbiTag->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.aarch64PauthAbiTag != std::nullopt) {
assert(config->emachine == EM_AARCH64);
contentSize += 4 + 4 + 8 * 2;
}
assert(contentSize != 0);
return contentSize + 16;
}

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();
return ctx.aarch64PauthAbiTag != std::nullopt;
}

void AArch64PauthAbiTag::writeTo(uint8_t *buf) {
const SmallVector<uint8_t, 0> &data = ctx.aarch64PauthAbiTag;
std::array<uint8_t, 16> data = ctx.aarch64PauthAbiTag.value();
write32(buf, 4); // Name size
write32(buf + 4, data.size()); // Content size
write32(buf + 8, NT_ARM_TYPE_PAUTH_ABI_TAG); // Type
Expand All @@ -349,11 +372,6 @@ void AArch64PauthAbiTag::writeTo(uint8_t *buf) {
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()) {}
Expand Down
2 changes: 1 addition & 1 deletion lld/ELF/SyntheticSections.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ class AArch64PauthAbiTag final : public SyntheticSection {
public:
AArch64PauthAbiTag();
void writeTo(uint8_t *buf) override;
size_t getSize() const override;
size_t getSize() const override { return 32; };
bool isNeeded() const override;
};

Expand Down
14 changes: 9 additions & 5 deletions lld/ELF/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -521,13 +521,17 @@ template <class ELFT> void elf::createSyntheticSections() {
in.iplt = std::make_unique<IpltSection>();
add(*in.iplt);

if (config->andFeatures)
if (config->andFeatures || ctx.aarch64PauthAbiTag.has_value())
add(*make<GnuPropertySection>());

if (!ctx.aarch64PauthAbiTag.empty()) {
in.aarch64PauthAbiTag = std::make_unique<AArch64PauthAbiTag>();
add(*in.aarch64PauthAbiTag);
}
// TODO: alternative PAuth ELF marking way
// To be implemented after the following PRs are merged:
// - https://github.com/ARM-software/abi-aa/pull/240
// - https://github.com/llvm/llvm-project/pull/72714
// if (!ctx.aarch64PauthAbiTag.empty()) {
// in.aarch64PauthAbiTag = std::make_unique<AArch64PauthAbiTag>();
// add(*in.aarch64PauthAbiTag);
// }

// .note.GNU-stack is always added when we are creating a re-linkable
// object file. Other linkers are using the presence of this marker
Expand Down
1 change: 1 addition & 0 deletions lld/test/ELF/aarch64-feature-pauth.s
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# REQUIRES: aarch64
# TODO: test PAuth ELF marking via GNU property section

# RUN: rm -rf %t && split-file %s %t && cd %t

Expand Down
10 changes: 10 additions & 0 deletions lldb/include/lldb/Symbol/ObjectFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "llvm/Support/Threading.h"
#include "llvm/Support/VersionTuple.h"
#include <optional>
#include <utility>

namespace lldb_private {

Expand Down Expand Up @@ -331,6 +332,15 @@ class ObjectFile : public std::enable_shared_from_this<ObjectFile>,
///
virtual void RelocateSection(lldb_private::Section *section);

/// Parse ELF's AArch64 PAuth ABI from GNU property section. If it's missing
/// or the object file is not an ELF, return std::nullopt. Define this as a
/// virtual function in a base class since direct usage of ObjectFileELF in
/// ClangExpressionParser requires linking against the ObjectFileELF plugin.
virtual std::optional<std::pair<uint64_t, uint64_t>>
ParseGNUPropertyAArch64PAuthABI() {
return std::nullopt;
}

/// Appends a Symbol for the specified so_addr to the symbol table.
///
/// If verify_unique is false, the symbol table is not searched to determine
Expand Down
2 changes: 2 additions & 0 deletions lldb/include/lldb/Target/LanguageRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,8 @@ class LanguageRuntime : public Runtime, public PluginInterface {
}

LanguageRuntime(Process *process);

std::pair<uint64_t, uint64_t> m_aarch64_pauth_abi_tag;
};

} // namespace lldb_private
Expand Down
Loading

0 comments on commit f39ea06

Please sign in to comment.