Skip to content

Commit

Permalink
[WIP] Support GNU_PROPERTY_AARCH64_FEATURE_PAUTH
Browse files Browse the repository at this point in the history
  • Loading branch information
kovdan01 committed Feb 5, 2024
1 parent 908da16 commit f85a1a3
Show file tree
Hide file tree
Showing 12 changed files with 184 additions and 33 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
24 changes: 21 additions & 3 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, SmallVector<uint8_t, 0>>
readGnuProperty(const InputSection &sec) {
using Elf_Nhdr = typename ELFT::Nhdr;
using Elf_Note = typename ELFT::Note;

uint32_t featuresSet = 0;
SmallVector<uint8_t, 0> 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 @@ -926,6 +931,16 @@ template <class ELFT> static uint32_t readAndFeatures(const InputSection &sec) {
featuresSet |= read32<ELFT::TargetEndianness>(desc.data());
}

if (config->emachine == EM_AARCH64 &&
type == GNU_PROPERTY_AARCH64_FEATURE_PAUTH) {
// TODO: proper invalid size handling
assert(size == 16);
// TODO: proper multiple pauth tags handling
assert(aarch64PauthAbiTag.empty());
aarch64PauthAbiTag.resize(size);
memcpy(aarch64PauthAbiTag.data(), desc.data(), size);
}

// Padding is present in the note descriptor, if necessary.
desc = desc.slice(alignTo<(ELFT::Is64Bits ? 8 : 4)>(size));
}
Expand All @@ -934,7 +949,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 @@ -1029,12 +1044,15 @@ 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
assert(this->aarch64PauthAbiTag.empty());
readAArch64PauthAbiTag<ELFT>(InputSection(*this, sec, name), *this);
return &InputSection::discarded;
}
Expand Down
44 changes: 34 additions & 10 deletions lld/ELF/SyntheticSections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,22 +314,46 @@ 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.empty()) {
write32(buf + offset + 0, GNU_PROPERTY_AARCH64_FEATURE_PAUTH);
write32(buf + offset + 4, 8 * 2);
memcpy(buf + offset + 8, ctx.aarch64PauthAbiTag.data(), 16);
}
}

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.empty()) {
// TODO: proper error handling
assert(ctx.aarch64PauthAbiTag.size() == 16);
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,
Expand Down
11 changes: 6 additions & 5 deletions lld/ELF/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -521,13 +521,14 @@ template <class ELFT> void elf::createSyntheticSections() {
in.iplt = std::make_unique<IpltSection>();
add(*in.iplt);

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

if (!ctx.aarch64PauthAbiTag.empty()) {
in.aarch64PauthAbiTag = std::make_unique<AArch64PauthAbiTag>();
add(*in.aarch64PauthAbiTag);
}
// TODO: alternative PAuth ELF marking way
// 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
Original file line number Diff line number Diff line change
Expand Up @@ -472,8 +472,13 @@ ClangExpressionParser::ClangExpressionParser(

auto target_info = TargetInfo::CreateTargetInfo(
m_compiler->getDiagnostics(), m_compiler->getInvocation().TargetOpts);
bool is_pauthabi = true;
if (target_machine == llvm::Triple::aarch64 && is_pauthabi) {

bool is_elf_pauthabi = false;
if (llvm::Triple(m_compiler->getTargetOpts().Triple).isOSBinFormatELF()) {
bool is_elf_pauthabi = true; // TODO
}

if (target_machine == llvm::Triple::aarch64 && is_elf_pauthabi) {
// TODO: enable this depending on corresponding tag section in ELF
target_info->getTargetOpts().Features.push_back("+pauth");
}
Expand Down Expand Up @@ -622,8 +627,8 @@ ClangExpressionParser::ClangExpressionParser(
// additionally enabling them as expandable builtins is breaking Clang.
lang_opts.NoBuiltin = true;

// TODO: enable this depending on corresponding tag section in ELF
if (is_pauthabi) {
// TODO
if (is_elf_pauthabi) {
lang_opts.PointerAuthCalls = true;
lang_opts.PointerAuthReturns = true;
lang_opts.PointerAuthVTPtrAddressDiscrimination = true;
Expand Down
49 changes: 49 additions & 0 deletions lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1284,6 +1284,44 @@ ObjectFileELF::RefineModuleDetailsFromNote(lldb_private::DataExtractor &data,
return error;
}

void ObjectFileELF::ParseGNUPropertyPAuthABI(DataExtractor &data,
uint64_t length) {
lldb::offset_t Offset = 0;

uint32_t NameSz = data.GetU32(&Offset);
if (NameSz != 4)
return; // TODO

uint32_t ContentSz = data.GetU32(&Offset);
uint32_t SectionType = data.GetU32(&Offset);
if (SectionType != NT_GNU_PROPERTY_TYPE_0)
return; // TODO

llvm::StringRef Name = data.GetCStr(&Offset, NameSz);
if (Name != "GNU")
return; // TODO

while (Offset < length) {
uint32_t FeatureType = data.GetU32(&Offset);
uint32_t Size = data.GetU32(&Offset);
if (FeatureType != GNU_PROPERTY_AARCH64_FEATURE_PAUTH) {
Offset += Size;
continue;
}
assert(Size == 16); // TODO
uint64_t PAuthABIPlatform = data.GetU64(&Offset);
uint64_t PAuthABIVersion = data.GetU64(&Offset);
assert(PAuthABIPlatform == 2); // TODO

// TODO
bool PointerAuthCalls = PAuthABIVersion & (1 << 0);
bool PointerAuthReturns = PAuthABIVersion & (1 << 1);
bool PointerAuthVTPtrAddressDiscrimination = PAuthABIVersion & (1 << 2);
bool PointerAuthVTPtrTypeDiscrimination = PAuthABIVersion & (1 << 3);
bool PointerAuthInitFini = PAuthABIVersion & (1 << 4);
}
}

void ObjectFileELF::ParseARMAttributes(DataExtractor &data, uint64_t length,
ArchSpec &arch_spec) {
lldb::offset_t Offset = 0;
Expand Down Expand Up @@ -1472,6 +1510,7 @@ size_t ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl &section_headers,
for (SectionHeaderCollIter I = section_headers.begin();
I != section_headers.end(); ++I) {
static ConstString g_sect_name_gnu_debuglink(".gnu_debuglink");
static ConstString g_sect_name_gnu_property(".note.gnu.property");
const ELFSectionHeaderInfo &sheader = *I;
const uint64_t section_size =
sheader.sh_type == SHT_NOBITS ? 0 : sheader.sh_size;
Expand Down Expand Up @@ -1554,6 +1593,16 @@ size_t ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl &section_headers,
ParseARMAttributes(data, section_size, arch_spec);
}

if (arch_spec.GetMachine() == llvm::Triple::aarch64 &&
name == g_sect_name_gnu_property) {
DataExtractor data;

if (sheader.sh_type == SHT_NOTE && section_size != 0 &&
data.SetData(object_data, sheader.sh_offset, section_size) ==
section_size)
ParseGNUPropertyPAuthABI(data, section_size);
}

if (name == g_sect_name_gnu_debuglink) {
DataExtractor data;
if (section_size && (data.SetData(object_data, sheader.sh_offset,
Expand Down
3 changes: 3 additions & 0 deletions lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,9 @@ class ObjectFileELF : public lldb_private::ObjectFile {

lldb::SectionType GetSectionType(const ELFSectionHeaderInfo &H) const;

static void ParseGNUPropertyPAuthABI(lldb_private::DataExtractor &data,
uint64_t length);

static void ParseARMAttributes(lldb_private::DataExtractor &data,
uint64_t length,
lldb_private::ArchSpec &arch_spec);
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/BinaryFormat/ELF.h
Original file line number Diff line number Diff line change
Expand Up @@ -1673,6 +1673,7 @@ enum : unsigned {
GNU_PROPERTY_STACK_SIZE = 1,
GNU_PROPERTY_NO_COPY_ON_PROTECTED = 2,
GNU_PROPERTY_AARCH64_FEATURE_1_AND = 0xc0000000,
GNU_PROPERTY_AARCH64_FEATURE_PAUTH = 0xc0000001,
GNU_PROPERTY_X86_FEATURE_1_AND = 0xc0000002,

GNU_PROPERTY_X86_UINT32_OR_LO = 0xc0008000,
Expand Down
20 changes: 17 additions & 3 deletions llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,8 @@ void AArch64AsmPrinter::emitStartOfAsmFile(Module &M) {
S, MCConstantExpr::create(Feat00Value, MMI->getContext()));
}

// TODO: enhance naming to distinguish MachO ptrauth ABI version and ELF
// pauthabi platform and version
if (TM.getTargetTriple().isOSBinFormatMachO())
EmitPtrAuthVersion(M);

Expand All @@ -283,13 +285,25 @@ void AArch64AsmPrinter::emitStartOfAsmFile(Module &M) {
if (Sign->getZExtValue())
Flags |= ELF::GNU_PROPERTY_AARCH64_FEATURE_1_PAC;

if (Flags == 0)
return;
uint64_t PAuthABIPlatform = -1;
if (const auto *PAP = mdconst::extract_or_null<ConstantInt>(
M.getModuleFlag("pauthabi-platform")))
PAuthABIPlatform = PAP->getZExtValue();
uint64_t PAuthABIVersion = -1;
if (const auto *PAV = mdconst::extract_or_null<ConstantInt>(
M.getModuleFlag("pauthabi-version")))
PAuthABIVersion = PAV->getZExtValue();

// TODO: proper error handling instead of assertion
// We later rely on this invariant in AArch64TargetStreamer::emitNoteSection,
// but here we might get invalid user input.
assert((PAuthABIPlatform == uint64_t(-1)) ==
(PAuthABIVersion == uint64_t(-1)));

// Emit a .note.gnu.property section with the flags.
auto *TS =
static_cast<AArch64TargetStreamer *>(OutStreamer->getTargetStreamer());
TS->emitNoteSection(Flags);
TS->emitNoteSection(Flags, PAuthABIPlatform, PAuthABIVersion);
}

void AArch64AsmPrinter::emitFunctionHeaderComment() {
Expand Down
34 changes: 27 additions & 7 deletions llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,20 @@ void AArch64TargetStreamer::emitConstantPools() {
void AArch64TargetStreamer::finish() {
if (MarkBTIProperty)
emitNoteSection(ELF::GNU_PROPERTY_AARCH64_FEATURE_1_BTI);
// TODO: do we need to handle GNU_PROPERTY_AARCH64_FEATURE_PAUTH here?
}

void AArch64TargetStreamer::emitNoteSection(unsigned Flags) {
if (Flags == 0)
void AArch64TargetStreamer::emitNoteSection(unsigned Flags,
uint64_t PAuthABIPlatform,
uint64_t PAuthABIVersion) {
assert((PAuthABIPlatform == uint64_t(-1)) ==
(PAuthABIVersion == uint64_t(-1)));
uint64_t DescSz = 0;
if (Flags != 0)
DescSz += 4 * 4;
if (PAuthABIPlatform != uint64_t(-1))
DescSz += 4 + 4 + 8 * 2;
if (DescSz == 0)
return;

MCStreamer &OutStreamer = getStreamer();
Expand All @@ -80,15 +90,25 @@ void AArch64TargetStreamer::emitNoteSection(unsigned Flags) {
// Emit the note header.
OutStreamer.emitValueToAlignment(Align(8));
OutStreamer.emitIntValue(4, 4); // data size for "GNU\0"
OutStreamer.emitIntValue(4 * 4, 4); // Elf_Prop size
OutStreamer.emitIntValue(DescSz, 4); // Elf_Prop array size
OutStreamer.emitIntValue(ELF::NT_GNU_PROPERTY_TYPE_0, 4);
OutStreamer.emitBytes(StringRef("GNU", 4)); // note name

// Emit the PAC/BTI properties.
OutStreamer.emitIntValue(ELF::GNU_PROPERTY_AARCH64_FEATURE_1_AND, 4);
OutStreamer.emitIntValue(4, 4); // data size
OutStreamer.emitIntValue(Flags, 4); // data
OutStreamer.emitIntValue(0, 4); // pad
if (Flags != 0) {
OutStreamer.emitIntValue(ELF::GNU_PROPERTY_AARCH64_FEATURE_1_AND, 4);
OutStreamer.emitIntValue(4, 4); // data size
OutStreamer.emitIntValue(Flags, 4); // data
OutStreamer.emitIntValue(0, 4); // pad
}

// Emit the PAuth ABI compatibility info
if (PAuthABIPlatform != uint64_t(-1)) {
OutStreamer.emitIntValue(ELF::GNU_PROPERTY_AARCH64_FEATURE_PAUTH, 4);
OutStreamer.emitIntValue(8 * 2, 4); // data size
OutStreamer.emitIntValue(PAuthABIPlatform, 8);
OutStreamer.emitIntValue(PAuthABIVersion, 8);
}

OutStreamer.endSection(Nt);
OutStreamer.switchSection(Cur);
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ class AArch64TargetStreamer : public MCTargetStreamer {
void emitCurrentConstantPool();

/// Callback used to implement the .note.gnu.property section.
void emitNoteSection(unsigned Flags);
void emitNoteSection(unsigned Flags, uint64_t PAuthABIPlatform = -1,
uint64_t PAuthABIVersion = -1);

/// Callback used to implement the .inst directive.
virtual void emitInst(uint32_t Inst);
Expand Down

0 comments on commit f85a1a3

Please sign in to comment.