diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index d8ecf681f6d6..f901c1d7f313 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -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()) { diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 873438033a65..ae5aaa1cc356 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -31,6 +31,8 @@ #include "llvm/Support/TarWriter.h" #include "llvm/Support/raw_ostream.h" +#include + using namespace llvm; using namespace llvm::ELF; using namespace llvm::object; @@ -878,11 +880,14 @@ void ObjFile::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 static uint32_t readAndFeatures(const InputSection &sec) { +template +static std::pair> +readGnuProperty(const InputSection &sec) { using Elf_Nhdr = typename ELFT::Nhdr; using Elf_Note = typename ELFT::Note; uint32_t featuresSet = 0; + SmallVector aarch64PauthAbiTag; ArrayRef data = sec.content(); auto reportFatal = [&](const uint8_t *place, const char *msg) { fatal(toString(sec.file) + ":(" + sec.name + "+0x" + @@ -926,6 +931,16 @@ template static uint32_t readAndFeatures(const InputSection &sec) { featuresSet |= read32(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)); } @@ -934,7 +949,7 @@ template 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 @@ -1029,12 +1044,15 @@ InputSectionBase *ObjFile::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(InputSection(*this, sec, name)); + std::tie(this->andFeatures, this->aarch64PauthAbiTag) = + readGnuProperty(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(InputSection(*this, sec, name), *this); return &InputSection::discarded; } diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index 51a017d8fb6c..73854fa5f59f 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -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, diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index ed68da5d442e..d242e2c9ad1c 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -521,13 +521,14 @@ template void elf::createSyntheticSections() { in.iplt = std::make_unique(); add(*in.iplt); - if (config->andFeatures) + if (config->andFeatures || !ctx.aarch64PauthAbiTag.empty()) add(*make()); - if (!ctx.aarch64PauthAbiTag.empty()) { - in.aarch64PauthAbiTag = std::make_unique(); - add(*in.aarch64PauthAbiTag); - } + // TODO: alternative PAuth ELF marking way + // if (!ctx.aarch64PauthAbiTag.empty()) { + // in.aarch64PauthAbiTag = std::make_unique(); + // 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 diff --git a/lld/test/ELF/aarch64-feature-pauth.s b/lld/test/ELF/aarch64-feature-pauth.s index 0520b2f28631..5b3e43aa5360 100644 --- a/lld/test/ELF/aarch64-feature-pauth.s +++ b/lld/test/ELF/aarch64-feature-pauth.s @@ -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 diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp index e8a0d05206c4..670643cbea33 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp @@ -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"); } @@ -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; diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp index 2da971dff895..7d886adc2a9d 100644 --- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -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; @@ -1472,6 +1510,7 @@ size_t ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl §ion_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; @@ -1554,6 +1593,16 @@ size_t ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl §ion_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, diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h index bc8e34981a9d..d6a2d425f88f 100644 --- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h @@ -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); diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h index 326c737afa1f..6680acad8594 100644 --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -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, diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp index 152105c20b11..ff02f1e7e641 100644 --- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -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); @@ -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( + M.getModuleFlag("pauthabi-platform"))) + PAuthABIPlatform = PAP->getZExtValue(); + uint64_t PAuthABIVersion = -1; + if (const auto *PAV = mdconst::extract_or_null( + 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(OutStreamer->getTargetStreamer()); - TS->emitNoteSection(Flags); + TS->emitNoteSection(Flags, PAuthABIPlatform, PAuthABIVersion); } void AArch64AsmPrinter::emitFunctionHeaderComment() { diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.cpp index e1d6dd7a056b..0b13f5868e60 100644 --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.cpp +++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.cpp @@ -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(); @@ -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); diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h index b3bce9960772..716ca1b3286c 100644 --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h +++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h @@ -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);