Index: ELF/Arch/X86.cpp =================================================================== --- ELF/Arch/X86.cpp +++ ELF/Arch/X86.cpp @@ -34,6 +34,8 @@ void writePltHeader(uint8_t *Buf) const override; void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; + void writeSPlt(uint8_t *Buf, uint64_t GotPltEntryAddr, + uint64_t PltEntryAddr) const override; void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, @@ -172,9 +174,15 @@ } void X86::writeGotPlt(uint8_t *Buf, const Symbol &S) const { - // Entries in .got.plt initially points back to the corresponding - // PLT entries with a fixed offset to skip the first instruction. - write32le(Buf, S.getPltVA() + 6); + if (Config->SPltSectionEnable) + // If the PLT is IBT-enabled, the entries in .got.plt initially point to + // the entries in the first PLT not the second PLT. + write32le(Buf, S.getPltVA()); + else + // Otherwise the entries in .got.plt initially point back to the + // corresponding PLT entries with a fixed offset to skip the first + // instruction. + write32le(Buf, S.getPltVA() + 6); } void X86::writeIgotPlt(uint8_t *Buf, const Symbol &S) const { @@ -220,6 +228,21 @@ void X86::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const { + // In the IBT-enabled PLT, the instruction sequence is different. + if (Config->SPltSectionEnable) { + const uint8_t Inst[] = { + 0xf3, 0x0f, 0x1e, 0xfb, // endbr32 + 0x68, 0, 0, 0, 0, // pushl $reloc_offset + 0xe9, 0, 0, 0, 0, // jmpq .PLT0@PC + 0x66, 0x90, // nop + }; + memcpy(Buf, Inst, sizeof(Inst)); + + write32le(Buf + 5, RelOff); + write32le(Buf + 10, -getPltEntryOffset(Index) - 14); + return; + } + const uint8_t Inst[] = { 0xff, 0x00, 0, 0, 0, 0, // jmp *foo_in_GOT or jmp *foo@GOT(%ebx) 0x68, 0, 0, 0, 0, // pushl $reloc_offset @@ -242,6 +265,18 @@ write32le(Buf + 12, -getPltEntryOffset(Index) - 16); } +void X86::writeSPlt(uint8_t *Buf, uint64_t GotPltEntryAddr, + uint64_t PltEntryAddr) const { + const uint8_t Inst[] = { + 0xf3, 0x0f, 0x1e, 0xfb, // endbr32 + 0xff, 0x25, 0, 0, 0, 0, // jmpq *got(%rip) + 0x66, 0x0f, 0x1f, 0x44, 0, 0, // nop + }; + memcpy(Buf, Inst, sizeof(Inst)); + + write32le(Buf + 6, GotPltEntryAddr - PltEntryAddr - 10); +} + int64_t X86::getImplicitAddend(const uint8_t *Buf, RelType Type) const { switch (Type) { case R_386_8: Index: ELF/Arch/X86_64.cpp =================================================================== --- ELF/Arch/X86_64.cpp +++ ELF/Arch/X86_64.cpp @@ -34,6 +34,8 @@ void writePltHeader(uint8_t *Buf) const override; void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; + void writeSPlt(uint8_t *Buf, uint64_t GotPltEntryAddr, + uint64_t PltEntryAddr) const override; void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, @@ -141,8 +143,15 @@ template void X86_64::writeGotPlt(uint8_t *Buf, const Symbol &S) const { - // See comments in X86::writeGotPlt. - write64le(Buf, S.getPltVA() + 6); + if (Config->SPltSectionEnable) + // If the PLT is IBT-enabled, the entries in .got.plt also initially point to + // the entries in the first PLT not the second PLT. + write64le(Buf, S.getPltVA()); + else + // Otherwise the entries in .got.plt initially point back to the + // corresponding PLT entries with a fixed offset to skip the first + // instruction. + write64le(Buf, S.getPltVA() + 6); } template void X86_64::writePltHeader(uint8_t *Buf) const { @@ -162,6 +171,21 @@ void X86_64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const { + // In the IBT-enabled PLT, the instruction sequence is different. + if (Config->SPltSectionEnable) { + const uint8_t Inst[] = { + 0xf3, 0x0f, 0x1e, 0xfa, // endbr64 + 0x68, 0, 0, 0, 0, // pushq + 0xe9, 0, 0, 0, 0, // jmpq plt[0] + 0x66, 0x90, // nop + }; + memcpy(Buf, Inst, sizeof(Inst)); + write32le(Buf + 5, Index); + // Distance between nop and PLT's entry + write32le(Buf + 10, -getPltEntryOffset(Index) - 14); + return; + } + const uint8_t Inst[] = { 0xff, 0x25, 0, 0, 0, 0, // jmpq *got(%rip) 0x68, 0, 0, 0, 0, // pushq @@ -174,6 +198,19 @@ write32le(Buf + 12, -getPltEntryOffset(Index) - 16); } +template +void X86_64::writeSPlt(uint8_t *Buf, uint64_t GotPltEntryAddr, + uint64_t PltEntryAddr) const { + const uint8_t Inst[] = { + 0xf3, 0x0f, 0x1e, 0xfa, // endbr64 + 0xff, 0x25, 0, 0, 0, 0, // jmpq *got(%rip) + 0x66, 0x0f, 0x1f, 0x44, 0, 0, // nop + }; + memcpy(Buf, Inst, sizeof(Inst)); + // Distance between nop and GOT PLT's entry + write32le(Buf + 6, GotPltEntryAddr - PltEntryAddr - 10); +} + template RelType X86_64::getDynRel(RelType Type) const { if (Type == R_X86_64_64 || Type == R_X86_64_PC64 || Type == R_X86_64_SIZE32 || Type == R_X86_64_SIZE64) Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -128,6 +128,7 @@ bool ARMHasMovtMovw = false; bool ARMJ1J2BranchEncoding = false; bool AsNeeded = false; + bool AutoCET; bool Bsymbolic; bool BsymbolicFunctions; bool CallGraphProfileSort; @@ -145,6 +146,7 @@ bool ExportDynamic; bool FixCortexA53Errata843419; bool FormatBinary = false; + bool ForceCET; bool GcSections; bool GdbIndex; bool GnuHash = false; @@ -187,6 +189,7 @@ bool WarnMissingEntry; bool WarnSymbolOrdering; bool WriteAddends; + bool SPltSectionEnable = false; bool ZCombreloc; bool ZCopyreloc; bool ZExecstack; @@ -217,6 +220,11 @@ ELFKind EKind = ELFNoneKind; uint16_t DefaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL; uint16_t EMachine = llvm::ELF::EM_NONE; + + // GNU_PROPERTY_X86_FEATURE_1_AND: Its pr_data field contains a 4-byte + // integer. A bit in the output pr_data field is set only if it is set + // in all relocatable input pr_data fields. + uint32_t X86Feature1AND= 0x0; llvm::Optional ImageBase; uint64_t MaxPageSize; uint64_t MipsGotSize; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -326,6 +326,10 @@ if (Config->SingleRoRx && !Script->HasSectionsCommand) error("-execute-only and -no-rosegment cannot be used together"); } + + if (Config->ZRetpolineplt + && (Config->AutoCET || Config->ForceCET)) + error("--auto-cet/--force-cet may not be used with -z retpolineplt"); } static const char *getReproduceOption(opt::InputArgList &Args) { @@ -768,6 +772,7 @@ Args.hasFlag(OPT_allow_shlib_undefined, OPT_no_allow_shlib_undefined, Args.hasArg(OPT_shared)); Config->AuxiliaryList = args::getStrings(Args, OPT_auxiliary); + Config->AutoCET = Args.hasArg(OPT_auto_cet); Config->Bsymbolic = Args.hasArg(OPT_Bsymbolic); Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions); Config->CheckSections = @@ -798,6 +803,7 @@ Config->FilterList = args::getStrings(Args, OPT_filter); Config->Fini = Args.getLastArgValue(OPT_fini, "_fini"); Config->FixCortexA53Errata843419 = Args.hasArg(OPT_fix_cortex_a53_843419); + Config->ForceCET = Args.hasArg(OPT_force_cet); Config->GcSections = Args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false); Config->GnuUnique = Args.hasFlag(OPT_gnu_unique, OPT_no_gnu_unique, true); Config->GdbIndex = Args.hasFlag(OPT_gdb_index, OPT_no_gdb_index, false); @@ -1619,6 +1625,14 @@ return S->Name.startswith(".debug") || S->Name.startswith(".zdebug"); }); + // Merge aggregate metadata sections like .note.gnu.property that should be + // found across all relocated files and reproduced once. But Until now, the + // emitAggregateMetadata just deal with the X86Feature1AND info, and only X86_64, i386 + // support the X86Feature1AND. + if ((Config->EMachine == EM_X86_64 || Config->EMachine == EM_386) + && (Config->AutoCET || Config->ForceCET)) + mergeAggregateMetadata(); + Config->EFlags = Target->calcEFlags(); if (Config->EMachine == EM_ARM) { Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -710,10 +710,20 @@ return Dest - P; } case R_PLT: - return Sym.getPltVA() + A; + // When the CET Branch protection is enabled, the second PLT + // should be call to. + if (Config->SPltSectionEnable) + return Sym.getSPltVA() + A; + else + return Sym.getPltVA() + A; case R_PLT_PC: case R_PPC_CALL_PLT: - return Sym.getPltVA() + A - P; + // When the CET Branch protection is enabled, the second PLT + // should be call to. + if (Config->SPltSectionEnable) + return Sym.getSPltVA() + A - P; + else + return Sym.getPltVA() + A - P; case R_PPC_CALL: { uint64_t SymVA = Sym.getVA(A); // If we have an undefined weak symbol, we might get here with a symbol Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -18,6 +18,8 @@ defm auxiliary: Eq<"auxiliary", "Set DT_AUXILIARY field to the specified name">; +def auto_cet: F<"auto-cet">, HelpText<"Auto enable CET if all inputfile CETed">; + def Bsymbolic: F<"Bsymbolic">, HelpText<"Bind defined symbols locally">; def Bsymbolic_functions: F<"Bsymbolic-functions">, @@ -167,6 +169,9 @@ def fix_cortex_a53_843419: F<"fix-cortex-a53-843419">, HelpText<"Apply fixes for AArch64 Cortex-A53 erratum 843419">; +def force_cet: F<"force-cet">, + HelpText<"Force enable CET, and report errors if not all inputfile CETed">; + defm format: Eq<"format", "Change the input format of the inputs following this option">, MetaVarName<"[default,elf,binary]">; Index: ELF/Relocations.cpp =================================================================== --- ELF/Relocations.cpp +++ ELF/Relocations.cpp @@ -783,9 +783,12 @@ } template -static void addPltEntry(PltSection *Plt, GotPltSection *GotPlt, - RelocationBaseSection *Rel, RelType Type, Symbol &Sym) { +static void addPltEntry(PltSection *Plt, SPltSection *Splt, + GotPltSection *GotPlt, RelocationBaseSection *Rel, + RelType Type, Symbol &Sym) { Plt->addEntry(Sym); + if (Config->SPltSectionEnable) + Splt->addEntry(Sym); GotPlt->addEntry(Sym); Rel->addReloc( {Type, GotPlt, Sym.getGotPltOffset(), !Sym.IsPreemptible, &Sym, 0}); @@ -977,7 +980,7 @@ "' cannot be preempted; recompile with -fPIE" + getLocation(Sec, Sym, Offset)); if (!Sym.isInPlt()) - addPltEntry(In.Plt, In.GotPlt, In.RelaPlt, Target->PltRel, Sym); + addPltEntry(In.Plt, In.Splt, In.GotPlt, In.RelaPlt, Target->PltRel, Sym); if (!Sym.isDefined()) replaceWithDefined(Sym, In.Plt, getPltEntryOffset(Sym.PltIndex), 0); Sym.NeedsPltAddr = true; @@ -1065,6 +1068,14 @@ Expr = fromPlt(Expr); } + // To Be Done. + // Now the IBT can't work with IFUNC feature, becasue they may conflict in + // changing the PLT. So we disable the IBT when the IFUNC enabled. + if (Sym.isGnuIFunc()) { + Config->X86Feature1AND &= ~GNU_PROPERTY_X86_FEATURE_1_IBT; + Config->SPltSectionEnable = false; + } + // This relocation does not require got entry, but it is relative to got and // needs it to be created. Here we request for that. if (oneof(In.Plt, In.GotPlt, In.RelaPlt, Target->PltRel, Sym); + addPltEntry(In.Plt, In.Splt, In.GotPlt, In.RelaPlt, + Target->PltRel, Sym); // Create a GOT slot if a relocation needs GOT. if (needsGot(Expr)) { @@ -1163,8 +1175,8 @@ // that's really needed to create the IRELATIVE is the section and value, // so ideally we should just need to copy those. auto *DirectSym = make(cast(Sym)); - addPltEntry(In.Iplt, In.IgotPlt, In.RelaIplt, Target->IRelativeRel, - *DirectSym); + addPltEntry(In.Iplt, In.Splt, In.IgotPlt, In.RelaIplt, + Target->IRelativeRel, *DirectSym); Sym.PltIndex = DirectSym->PltIndex; } if (Expr == R_ABS && Addend == 0 && (Sec.Flags & SHF_WRITE)) { Index: ELF/Symbols.h =================================================================== --- ELF/Symbols.h +++ ELF/Symbols.h @@ -164,6 +164,7 @@ uint64_t getGotPltOffset() const; uint64_t getGotPltVA() const; uint64_t getPltVA() const; + uint64_t getSPltVA() const; uint64_t getPPC64LongBranchTableVA() const; uint64_t getPPC64LongBranchOffset() const; uint64_t getSize() const; Index: ELF/Symbols.cpp =================================================================== --- ELF/Symbols.cpp +++ ELF/Symbols.cpp @@ -172,6 +172,10 @@ return OutVA; } +uint64_t Symbol::getSPltVA() const { + return In.Splt->getVA() + PltIndex * Target->PltEntrySize; +} + uint64_t Symbol::getPPC64LongBranchTableVA() const { assert(PPC64BranchltIndex != 0xffff); return In.PPC64LongBranchTarget->getVA() + Index: ELF/SyntheticSections.h =================================================================== --- ELF/SyntheticSections.h +++ ELF/SyntheticSections.h @@ -142,6 +142,25 @@ size_t getSize() const override { return 0; } }; +// .note.gnu.property section. +class GnuPropertySection : public SyntheticSection { +public: + GnuPropertySection(); + void writeTo(uint8_t *Buf) override; + size_t getSize() const override; + + // To Be Done + // Now we only supported X86_FEATURE_1_AND in .note.gnu.property, so we fix + // the DescSize to one Description size, it may be more than one Description + // when we try to fix other feature types in .note.gnu.property. + void setDescSize() { DescSize = Config->Is64 ? 16 : 12; } + void setFeature() { Feature1AND = Config->X86Feature1AND; } + +private: + size_t DescSize; + uint32_t Feature1AND; +}; + // .note.gnu.build-id section. class BuildIdSection : public SyntheticSection { // First 16 bytes are a header. @@ -650,7 +669,8 @@ // The PltSection is used for both the Plt and Iplt. The former usually has a // header as its first entry that is used at run-time to resolve lazy binding. // The latter is used for GNU Ifunc symbols, that will be subject to a -// Target->IRelativeRel. +// Target->IRelativeRel. It is also used for the primary part of the IBT-enabled +// PLT, also containing the lazy binding header. class PltSection : public SyntheticSection { public: PltSection(bool IsIplt); @@ -668,6 +688,21 @@ bool IsIplt; }; +// The SPltSection is used in IBT-enabled PLTs to represent the second table +// that is referenced by the GOT. +class SPltSection : public SyntheticSection { +public: + SPltSection(); + void writeTo(uint8_t *Buf) override; + size_t getSize() const override; + bool empty() const override { + return !Config->SPltSectionEnable || Entries.empty(); } + + template void addEntry(Symbol &Sym); +private: + std::vector> Entries; +}; + class GdbIndexSection final : public SyntheticSection { public: struct AddressEntry { @@ -994,6 +1029,7 @@ InputSection *createInterpSection(); MergeInputSection *createCommentSection(); +template void mergeAggregateMetadata(); template void splitSections(); void mergeSections(); @@ -1011,6 +1047,7 @@ SyntheticSection *Dynamic; StringTableSection *DynStrTab; SymbolTableBaseSection *DynSymTab; + GnuPropertySection *GnuProperty; GnuHashTableSection *GnuHashTab; HashTableSection *HashTab; GotSection *Got; @@ -1021,6 +1058,7 @@ MipsRldMapSection *MipsRldMap; PltSection *Plt; PltSection *Iplt; + SPltSection *Splt; RelocationBaseSection *RelaDyn; RelrBaseSection *RelrDyn; RelocationBaseSection *RelaPlt; Index: ELF/SyntheticSections.cpp =================================================================== --- ELF/SyntheticSections.cpp +++ ELF/SyntheticSections.cpp @@ -291,9 +291,39 @@ } } +void GnuPropertySection::writeTo(uint8_t *Buf) { + write32(Buf, 4); // Name size + write32(Buf + 4, DescSize); // Content size + write32(Buf + 8, NT_GNU_PROPERTY_TYPE_0); // Type + memcpy(Buf + 12, "GNU", 4); // Name string + + // To Be Done + // Now we only supported X86_FEATURE_1_AND in .note.gnu.property, so we fix + // the Feature type to GNU_PROPERTY_X86_FEATURE_1_AND, it should be changed + // when we try to fix other feature types in .note.gnu.property. + uint8_t *DescBuf = Buf + 16; + write32(DescBuf, GNU_PROPERTY_X86_FEATURE_1_AND); // Feature type + write32(DescBuf + 4, 4); // Feature size + // We need remove this if code when we supported IFUNC+CET. + if (!Config->SPltSectionEnable) + Feature1AND &= ~GNU_PROPERTY_X86_FEATURE_1_IBT; + write32(DescBuf + 8, Feature1AND); // Feature flags +} + +size_t GnuPropertySection::getSize() const { + return 16 + DescSize; +} + +GnuPropertySection::GnuPropertySection() + : SyntheticSection(llvm::ELF::SHF_ALLOC, llvm::ELF::SHT_NOTE, + Config->Is64 ? 8 : 4, ".note.gnu.property") { + setDescSize(); + setFeature(); +} + BuildIdSection::BuildIdSection() - : SyntheticSection(SHF_ALLOC, SHT_NOTE, 4, ".note.gnu.build-id"), - HashSize(getHashSize()) {} + : SyntheticSection(SHF_ALLOC, SHT_NOTE, 4, ".note.gnu.build-id"), + HashSize(getHashSize()) {} void BuildIdSection::writeTo(uint8_t *Buf) { write32(Buf, 4); // Name size @@ -2376,6 +2406,32 @@ return IsIplt ? In.Plt->getSize() : 0; } +// The SPLT is only used by X86_64. +SPltSection::SPltSection() + : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 16, ".splt") {} + +void SPltSection::writeTo(uint8_t *Buf) { + size_t Off = 0; + for (auto &I : Entries) { + const Symbol *B = I.first; + uint64_t Got = B->getGotPltVA(); + uint64_t Plt = this->getVA() + Off; + Target->writeSPlt(Buf + Off, Got, Plt); + Off += Target->PltEntrySize; // The Splt size is the same with Plt size + } +} + +template void SPltSection::addEntry(Symbol &Sym) { + RelocationBaseSection *PltRelocSection = In.RelaPlt; + unsigned RelOff = + static_cast *>(PltRelocSection)->getRelocOffset(); + Entries.push_back(std::make_pair(&Sym, RelOff)); +} + +size_t SPltSection::getSize() const { + return Entries.size() * Target->PltEntrySize; +} + // The string hash function for .gdb_index. static uint32_t computeGdbHash(StringRef S) { uint32_t H = 0; @@ -2983,6 +3039,149 @@ return make(Name, Type, Flags, Alignment); } +// Check if there is X86Feature1AND 'desc' in a .note.gnu.property section. +// And we can not make sure the X86Feature1AND info such as CET flags (IBT, +// SHSTK) is existed in the section, it should be checked later. +// +// Data struct of .note.gnu.property section (X86Feature1AND section) in ABI: +// System V Application Binary Interface Linux Extensions Version 0.1 Edited +// by H.J.Lu1 September 18,2018. Prease refer +// https://github.com/hjl-tools/linux-abi/wiki/linux-abi-draft.pdf +// Field Length Contents _ +// n_namsz 4 4 | +// n_descsz 4 The note descriptor size | We called these data 'head' +// n_type 4 NT_GNU_PROPERTY_TYPE_0 | +// n_name 4 GNU _| +// n_desc n_descsz The program property array +// +// Data struct in n_desc: +// typedef struct { _ +// ElfWord pr_type; //A 4-byte integer | +// ElfWord pr_datasz; //A 4-byte integer | We called these data 'desc' +// unsigned char pr_data[PR_DATASZ]; | +// unsigned char pr_padding[PR_PADDING]; _| +// } +// +// pr_type The type of program property. A 4-byte integer in the format of +// the target processor. +// pr_datasz The size of the pr_data. A 4-byte integer in the format of the +// target processor. +// pr_data The program property descriptor which is aligned to 4 bytes in +// 32-bit objects and 8 bytes in 64-bit objects. +// pr_padding The padding. If necessary, it aligns the array element to 8 or +// 4-byte alignment (depending on whether the target is a 64-biti +// or 32-bit object). +// PR_DATASZ The value in the pr_datasz. A constant. +// PR_PADDING The size of the pr_padding. A constant. +// +// We call the upper data 'head' and the following data 'desc' in the code. +// There are 2 formats for the merged note.gnu.property sections: +// A) one head with one desc. B) one head with mutil descs. +// +// Form A: Form B: +// __________________ ________________________ +// | | | | | | | +// | head | desc | | head | desc1 | desc2 | +// |_______|________| |______|_______|_______| +// +// Function return: +// Return true if the .note.gnu.property section contains X86Feature1ANDi, +// else return false; + +template +static bool findGNUPropertyX86Feature1AND(InputSectionBase *S, + unsigned &Features) { + #define FEATRUE_OFFSET 2 + using ElfWordT = typename ELFT::Word; + + struct GnuPropertyHeadType { + ElfWordT Namesz; + ElfWordT Descsz; + ElfWordT NType; + ElfWordT Name; + }; + struct GnuPropertyType { + GnuPropertyHeadType Head; + ElfWordT Desc[1]; + }; + auto Data = S->getDataAs(); + const GnuPropertyType *GnuProperty = (const GnuPropertyType*)(Data.data()); + size_t Descsz = Config->Is64 ? 16 : 12; + + if (GnuProperty->Head.NType != NT_GNU_PROPERTY_TYPE_0) + return false; + for (size_t i = 0; i < GnuProperty->Head.Descsz / sizeof (ElfWordT); + i += Descsz / sizeof (ElfWordT)) { + if (GnuProperty->Desc[i] == GNU_PROPERTY_X86_FEATURE_1_AND) { + Features = GnuProperty->Desc[i + FEATRUE_OFFSET]; + return true; + } + } + return false; +} + +// Iterate over metadata sections that should be aggregated and processed for +// the final result. But till now, we just deal with X86Feature1AND info here. +// In most cases the X86Feature1AND info was written in .note.gnu.propery +// section with one 'desc' but it can be also written in 'desc' combined with +// other 'desc' in the .note.gnu.propery section. Here we first find all the +// X86Feature1AND info section, then delete them, and finally rebuild the +// X86Feature1AND section in the Writer if necessary. +template +void elf::mergeAggregateMetadata() { + decltype (InputSections) X86F1ANDSections; + bool IsAllFileF1AND = true; + Config->SPltSectionEnable = true; + Config->X86Feature1AND = 0xFFFFFFFF; + for (InputFile *F : ObjectFiles) { + uint32_t Features = 0x0; + auto I = llvm::find_if(InputSections, [&](InputSectionBase *S) { + // X86Feature1AND info must be setten in .note.gnu.property sections. + if (S->Name != ".note.gnu.property" || S->File != F + || S->Type != SHT_NOTE) + return false; + X86F1ANDSections.push_back(S); + bool Found = findGNUPropertyX86Feature1AND(S, Features); + return Found; + }); + if (I == InputSections.end()) { + // There is no X86Feature1AND .note.gnu.property section in this file. Set + // all CFProtection properties to false and discard other such sections. + IsAllFileF1AND = false; + if (Config->ForceCET) + error(toString(F) + "must enable CET when using --force-cet"); + } else { + Config->X86Feature1AND &= Features; + } + } + Config->SPltSectionEnable = Config->X86Feature1AND + & GNU_PROPERTY_X86_FEATURE_1_IBT; + if (!IsAllFileF1AND) { + Config->SPltSectionEnable = false; + Config->X86Feature1AND = 0x0; + } + + // The resulting .note.gnu.property section will be created by the Writer + // if necessary. + // ******************************* To Be Done ******************************** + // We rudely erase the .note.gnu.property sections in the input files. Because + // till now we just implemented GNU_PROPERTY_X86_FEATURE_1_AND property, the + // other unhandled properties in the sections may be discard too. It seems not + // reasonable. But till now it's reasonable, because the .note.gnu.property + // is a special section, all the properties in this section need to be checked + // and handled. If they are simply passed to output file without being handled + // there may be a bug or exception in running the output file. For example, + // the GNU_PROPERTY_X86_ISA_1_USED also need all input file contain its flags, + // it can't be simply passed to the output file without checking other files. + // So we erase these informations before we implement the function of them. + llvm::erase_if(InputSections, [=, &X86F1ANDSections](InputSectionBase *S) { + if (llvm::find (X86F1ANDSections, S) != X86F1ANDSections.end()) + return true; + else + return false; + }); +} + template void elf::splitSections() { // splitIntoPieces needs to be called on each MergeInputSection // before calling finalizeContents(). @@ -3174,6 +3373,11 @@ template GdbIndexSection *GdbIndexSection::create(); template GdbIndexSection *GdbIndexSection::create(); +template void elf::mergeAggregateMetadata(); +template void elf::mergeAggregateMetadata(); +template void elf::mergeAggregateMetadata(); +template void elf::mergeAggregateMetadata(); + template void elf::splitSections(); template void elf::splitSections(); template void elf::splitSections(); @@ -3189,6 +3393,11 @@ template void PltSection::addEntry(Symbol &Sym); template void PltSection::addEntry(Symbol &Sym); +template void SPltSection::addEntry(Symbol &Sym); +template void SPltSection::addEntry(Symbol &Sym); +template void SPltSection::addEntry(Symbol &Sym); +template void SPltSection::addEntry(Symbol &Sym); + template class elf::MipsAbiFlagsSection; template class elf::MipsAbiFlagsSection; template class elf::MipsAbiFlagsSection; Index: ELF/Target.h =================================================================== --- ELF/Target.h +++ ELF/Target.h @@ -42,6 +42,8 @@ virtual void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const {} + virtual void writeSPlt(uint8_t *Buf, uint64_t GotEntryAddr, + uint64_t PltEntryAddr) const {} virtual void addPltHeaderSymbols(InputSection &IS) const {} virtual void addPltSymbols(InputSection &IS, uint64_t Off) const {} @@ -205,6 +207,10 @@ return Target->PltHeaderSize + Target->PltEntrySize * Idx; } +inline unsigned getSPltEntryOffset(unsigned Idx) { + return Target->PltEntrySize * Idx; +} + // Make sure that V can be represented as an N bit signed integer. inline void checkInt(uint8_t *Loc, int64_t V, int N, RelType Type) { if (V != llvm::SignExtend64(V, N)) Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -84,6 +84,13 @@ uint64_t FileSize; uint64_t SectionHeaderOff; + + // The Writer will deal with the InputSection's data array which is just a + // point in class ArrayRef, so the caller should initialize the data array + // or there may be an overwite to data array if it's initialized in + // InputSection's method locally. + using ElfWordT = typename ELFT::Word; + SmallVector DataNote; }; } // anonymous namespace @@ -409,6 +416,13 @@ Add(In.Plt); In.Iplt = make(true); Add(In.Iplt); + if (Config->SPltSectionEnable) { + In.Splt = make(); + Add(In.Splt); + } + + if (Config->X86Feature1AND) + Add(make()); // .note.GNU-stack is always added when we are creating a re-linkable // object file. Other linkers are using the presence of this marker @@ -1788,6 +1802,7 @@ finalizeSynthetic(In.RelaIplt); finalizeSynthetic(In.RelaPlt); finalizeSynthetic(In.Plt); + finalizeSynthetic(In.Splt); finalizeSynthetic(In.Iplt); finalizeSynthetic(In.EhFrameHdr); finalizeSynthetic(In.VerSym); Index: docs/ld.lld.1 =================================================================== --- docs/ld.lld.1 +++ docs/ld.lld.1 @@ -59,6 +59,8 @@ Set the .Dv DT_AUXILIARY field to the specified name. +.It Fl -auto-cet +Auto enable CET if all inputfiles with CET flags. .It Fl -Bdynamic , Fl -dy Link against shared libraries. .It Fl -Bstatic , Fl -static , Fl -dn @@ -182,6 +184,8 @@ field to the specified value. .It Fl -fini Ns = Ns Ar symbol Specify a finalizer function. +.It Fl -force-cet +Force enable CET, and report errors if not all inputfiles with CET flags. .It Fl -format Ns = Ns Ar input-format , Fl b Ar input-format Specify the format of the inputs following this option. .Ar input-format Index: test/ELF/Inputs/i386-feature-1-and0.s =================================================================== --- /dev/null +++ test/ELF/Inputs/i386-feature-1-and0.s @@ -0,0 +1,5 @@ + .text + .globl func2 + .type func2,@function +func2: + ret Index: test/ELF/Inputs/i386-feature-1-and1.s =================================================================== --- /dev/null +++ test/ELF/Inputs/i386-feature-1-and1.s @@ -0,0 +1,28 @@ + .section ".note.gnu.property", "a" + + .p2align 2 + + .long 1f - 0f + .long 4f -1f + .long 0x5 +0: + .asciz "GNU" +1: + + .p2align 2 + + .long 0xc0000002 + .long 3f - 2f +2: + .long 0x00000003 +3: + + .p2align 2 + +4: + + .text + .globl func2 + .type func2,@function +func2: + ret Index: test/ELF/Inputs/i386-feature-1-and1x.s =================================================================== --- /dev/null +++ test/ELF/Inputs/i386-feature-1-and1x.s @@ -0,0 +1,28 @@ + .section ".note.gnu.property", "a" + + .p2align 2 + + .long 1f - 0f + .long 4f -1f + .long 0x5 +0: + .asciz "GNU" +1: + + .p2align 2 + + .long 0xc0000002 + .long 3f - 2f +2: + .long 0xfffffffd +3: + + .p2align 2 + +4: + + .text + .globl func2 + .type func2,@function +func2: + ret Index: test/ELF/Inputs/i386-feature-1-and2.s =================================================================== --- /dev/null +++ test/ELF/Inputs/i386-feature-1-and2.s @@ -0,0 +1,37 @@ + .section ".note.gnu.property", "a" + + .p2align 2 + + .long 1f - 0f + .long 7f -1f + .long 0x5 +0: + .asciz "GNU" +1: + + .p2align 2 + + .long 0xc0000000 + .long 3f - 2f +2: + .long 0x00000000 +3: + + .p2align 2 + +4: + .long 0xc0000002 + .long 6f - 5f +5: + .long 0x00000003 +6: + + .p2align 2 + +7: + + .text + .globl func2 + .type func2,@function +func2: + ret Index: test/ELF/Inputs/i386-feature-1-and2s.s =================================================================== --- /dev/null +++ test/ELF/Inputs/i386-feature-1-and2s.s @@ -0,0 +1,29 @@ + .section ".note.gnu.property", "a" + + .p2align 2 + + .long 1f - 0f + .long 4f -1f + .long 0x5 +0: + .asciz "GNU" +1: + + .p2align 2 + + .long 0xc0000002 + .long 3f - 2f +2: + .long 0x00000002 +3: + + .p2align 2 + +4: + + + .text + .globl func2 + .type func2,@function +func2: + ret Index: test/ELF/Inputs/x86-64-feature-1-and0.s =================================================================== --- /dev/null +++ test/ELF/Inputs/x86-64-feature-1-and0.s @@ -0,0 +1,5 @@ + .text + .globl func2 + .type func2,@function +func2: + retq Index: test/ELF/Inputs/x86-64-feature-1-and1.s =================================================================== --- /dev/null +++ test/ELF/Inputs/x86-64-feature-1-and1.s @@ -0,0 +1,27 @@ + .section ".note.gnu.property", "a" + + .p2align 3 + + .long 1f - 0f + .long 4f -1f + .long 0x5 +0: + .asciz "GNU" +1: + + .p2align 3 + + .long 0xc0000002 + .long 3f - 2f +2: + .long 0x00000003 +3: + + .p2align 3 + +4: + .text + .globl func2 + .type func2,@function +func2: + retq Index: test/ELF/Inputs/x86-64-feature-1-and1x.s =================================================================== --- /dev/null +++ test/ELF/Inputs/x86-64-feature-1-and1x.s @@ -0,0 +1,27 @@ + .section ".note.gnu.property", "a" + + .p2align 3 + + .long 1f - 0f + .long 4f -1f + .long 0x5 +0: + .asciz "GNU" +1: + + .p2align 3 + + .long 0xc0000002 + .long 3f - 2f +2: + .long 0xfffffffd +3: + + .p2align 3 + +4: + .text + .globl func2 + .type func2,@function +func2: + retq Index: test/ELF/Inputs/x86-64-feature-1-and2.s =================================================================== --- /dev/null +++ test/ELF/Inputs/x86-64-feature-1-and2.s @@ -0,0 +1,32 @@ + .section ".note.gnu.property", "a" + + .p2align 3 + + .long 1f - 0f + .long 7f -1f + .long 0x5 +0: + .asciz "GNU" +1: + .p2align 3 + .long 0xc0000000 + .long 3f - 2f +2: + .long 0x00000001 +3: + .p2align 3 + +4: + .long 0xc0000002 + .long 6f - 5f +5: + .long 0x00000003 +6: + .p2align 3 + +7: + .text + .globl func2 + .type func2,@function +func2: + retq Index: test/ELF/Inputs/x86-64-feature-1-and2s.s =================================================================== --- /dev/null +++ test/ELF/Inputs/x86-64-feature-1-and2s.s @@ -0,0 +1,25 @@ + .section ".note.gnu.property", "a" + + .p2align 3 + + .long 1f - 0f + .long 4f -1f + .long 0x5 +0: + .asciz "GNU" +1: + .p2align 3 + .long 0xc0000002 + .long 3f - 2f +2: + .long 0x00000002 +3: + .p2align 3 + +4: + + .text + .globl func2 + .type func2,@function +func2: + retq Index: test/ELF/i386-feature-1-and.s =================================================================== --- /dev/null +++ test/ELF/i386-feature-1-and.s @@ -0,0 +1,89 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %p/Inputs/i386-feature-1-and1.s -o %t2.o +# RUN: ld.lld -e func1 --auto-cet %t1.o %t2.o -o %t +# RUN: llvm-readelf -n %t | FileCheck --check-prefix=NGP1 %s + +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %p/Inputs/i386-feature-1-and0.s -o %t2.o +# RUN: ld.lld -e func1 --auto-cet %t1.o %t2.o -o %t +# RUN: llvm-readelf -S %t | FileCheck --check-prefix=SEC0 %s + +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %p/Inputs/i386-feature-1-and1x.s -o %t2.o +# RUN: ld.lld -e func1 --auto-cet %t1.o %t2.o -o %t +# RUN: llvm-readelf -n %t | FileCheck --check-prefix=NGP1X %s + +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %p/Inputs/i386-feature-1-and2.s -o %t2.o +# RUN: ld.lld -e func1 --auto-cet %t1.o %t2.o -o %t +# RUN: llvm-readelf -n %t | FileCheck --check-prefix=NGP1 %s + +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %p/Inputs/i386-feature-1-and2s.s -o %t2.o +# RUN: ld.lld -e func1 --auto-cet %t1.o %t2.o -o %t +# RUN: llvm-readelf -n %t | FileCheck --check-prefix=NGP2S %s + +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %p/Inputs/i386-feature-1-and1.s -o %t2.o +# RUN: ld.lld -shared %t2.o -o %t2.so +# RUN: ld.lld -e func1 --auto-cet %t1.o %t2.so -o %t +# RUN: llvm-readelf -n %t | FileCheck --check-prefix=NGPSPLT %s +# RUN: llvm-readelf -S %t | FileCheck --check-prefix=SECSPLT %s +# RUN: llvm-objdump -d %t | FileCheck --check-prefix=DMPSPLT %s + +# Check if CET info generated. +# SEC1: .note.gnu.property +# NGP1: NT_GNU_PROPERTY_TYPE_0 +# NGP1: IBT +# NGP1: SHSTK + +# Check no .note.gnu.property +# SEC0: Section Headers +# SEC0-NOT: .note.gnu.property + +# Check .note.gnu.property without SHSTK. +# NGP1X: NT_GNU_PROPERTY_TYPE_0 +# NGP1X: IBT +# NGP1X-NOT: SHSTK + +# NGP2S: NT_GNU_PROPERTY_TYPE_0 +# NGP2S-NOT: IBT +# NGP2S: SHSTK + +# Check .note.gnu.property's name and splt is generated. +# SECSPLT: .note.gnu.property +# NGPSPLT: NT_GNU_PROPERTY_TYPE_0 +# NGPSPLT: IBT +# NGPSPLT: SHSTK +# DMPSPLT: splt + + .section ".note.gnu.property", "a" + + .p2align 2 + + .long 1f - 0f + .long 4f -1f + .long 0x5 +0: + .asciz "GNU" +1: + + .p2align 2 + + .long 0xc0000002 + .long 3f - 2f +2: + .long 0x00000003 +3: + + .p2align 2 + +4: + + .text + .globl func1 + .type func1,@function +func1: + call func2 + ret Index: test/ELF/x86-64-feature-1-and.s =================================================================== --- /dev/null +++ test/ELF/x86-64-feature-1-and.s @@ -0,0 +1,109 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/x86-64-feature-1-and1.s -o %t2.o +# RUN: ld.lld -e func1 --auto-cet %t1.o %t2.o -o %t +# RUN: llvm-readelf -n %t | FileCheck --check-prefix=NGP1 %s + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/x86-64-feature-1-and0.s -o %t2.o +# RUN: ld.lld -e func1 --auto-cet %t1.o %t2.o -o %t +# RUN: llvm-readelf -S %t | FileCheck --check-prefix=SEC0 %s + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/x86-64-feature-1-and1x.s -o %t2.o +# RUN: ld.lld -e func1 --auto-cet %t1.o %t2.o -o %t +# RUN: llvm-readelf -n %t | FileCheck --check-prefix=NGP1X %s + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/x86-64-feature-1-and2.s -o %t2.o +# RUN: ld.lld -e func1 --auto-cet %t1.o %t2.o -o %t +# RUN: llvm-readelf -n %t | FileCheck --check-prefix=NGP1 %s + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/x86-64-feature-1-and2s.s -o %t2.o +# RUN: ld.lld -e func1 --auto-cet %t1.o %t2.o -o %t +# RUN: llvm-readelf -n %t | FileCheck --check-prefix=NGP2S %s + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/x86-64-feature-1-and1.s -o %t2.o +# RUN: ld.lld -shared %t2.o -o %t2.so +# RUN: ld.lld -e func1 --auto-cet %t1.o %t2.so -o %t +# RUN: llvm-readelf -n %t | FileCheck --check-prefix=NGPSPLT %s +# RUN: llvm-readelf -S %t | FileCheck --check-prefix=SECSPLT %s +# RUN: llvm-objdump -d %t | FileCheck --check-prefix=DMPSPLT %s + +# Check if CET info is correct. +# NGP1: NT_GNU_PROPERTY_TYPE_0 +# NGP1: IBT +# NGP1: SHSTK + +# SEC0: Section Headers +# SEC0-NOT: .note.gnu.property + +# Check .note.gnu.protery without property SHSTK. +# NGP1X: NT_GNU_PROPERTY_TYPE_0 +# NGP1X: IBT +# NGP1X-NOT: SHSTK + +# NGP2S: NT_GNU_PROPERTY_TYPE_0 +# NGP2S-NOT: IBT +# NGP2S: SHSTK + +# Check if .note.gnu.property's name and splt is generated successfully. +# SECSPLT: .note.gnu.property +# NGPSPLT: NT_GNU_PROPERTY_TYPE_0 +# NGPSPLT: IBT +# NGPSPLT: SHSTK +# DMPSPLT: Disassembly of section .text: +# DMPSPLT-NEXT: func1: +# DMPSPLT-NEXT: 201000: e8 2b 00 00 00 callq 43 +# DMPSPLT-NEXT: 201005: e8 26 00 00 00 callq 38 +# DMPSPLT-NEXT: 20100a: c3 retq +# DMPSPLT: Disassembly of section .plt: +# DMPSPLT-NEXT: .plt: +# DMPSPLT-NEXT: 201010: {{.*}} pushq 8178(%rip) +# DMPSPLT-NEXT: 201016: {{.*}} jmpq *8180(%rip) +# DMPSPLT-NEXT: 20101c: {{.*}} nopl (%rax) +# DMPSPLT-NEXT: 201020: {{.*}} endbr64 +# DMPSPLT-NEXT: 201024: {{.*}} pushq $0 +# DMPSPLT-NEXT: 201029: {{.*}} jmp -30 <.plt> +# DMPSPLT-NEXT: 20102e: {{.*}} nop +# DMPSPLT: Disassembly of section .splt: +# DMPSPLT-NEXT: .splt: +# DMPSPLT-NEXT: 201030: {{.*}} endbr64 +# DMPSPLT-NEXT: 201034: {{.*}} jmpq *8158(%rip) +# DMPSPLT-NEXT: 20103a: {{.*}} nopw (%rax,%rax) + + .section ".note.gnu.property", "a" + + .p2align 3 + + .long 1f - 0f + .long 4f -1f + .long 0x5 +0: + .asciz "GNU" +1: + + .p2align 3 + + + + .long 0xc0000002 + .long 3f - 2f +2: + .long 0x00000003 +3: + + .p2align 3 + + + +4: + .text + .globl func1 + .type func1,@function +func1: + callq func2 + callq func2@plt + retq