diff --git a/lld/ELF/Arch/X86.cpp b/lld/ELF/Arch/X86.cpp --- a/lld/ELF/Arch/X86.cpp +++ b/lld/ELF/Arch/X86.cpp @@ -34,6 +34,7 @@ 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 writeIBTPlt(uint8_t *Buf, size_t NumEntries) const override; void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, @@ -172,9 +173,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 (In.IBTPlt) + // 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 +227,18 @@ 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 (In.IBTPlt) { + 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); + 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 +261,22 @@ write32le(Buf + 12, -getPltEntryOffset(Index) - 16); } +void X86::writeIBTPlt(uint8_t *Buf, size_t NumEntries) const { + 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 + }; + + for (size_t I = 0; I != NumEntries; ++I) { + memcpy(Buf, Inst, sizeof(Inst)); + write32le(Buf + 5, I * sizeof(object::ELF32LE::Rel)); + write32le(Buf + 10, -getPltEntryOffset(I) - 14); + Buf += sizeof(Inst); + } +} + int64_t X86::getImplicitAddend(const uint8_t *Buf, RelType Type) const { switch (Type) { case R_386_8: diff --git a/lld/ELF/Arch/X86_64.cpp b/lld/ELF/Arch/X86_64.cpp --- a/lld/ELF/Arch/X86_64.cpp +++ b/lld/ELF/Arch/X86_64.cpp @@ -34,6 +34,7 @@ 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 writeIBTPlt(uint8_t *Buf, size_t NumEntries) const override; void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, @@ -141,8 +142,15 @@ template void X86_64::writeGotPlt(uint8_t *Buf, const Symbol &S) const { - // See comments in X86::writeGotPlt. - write64le(Buf, S.getPltVA() + 6); + if (In.IBTPlt) + // 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,10 +170,22 @@ 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 (In.IBTPlt) { + 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)); + write32le(Buf + 6, GotPltEntryAddr - PltEntryAddr - 10); + return; + } + const uint8_t Inst[] = { 0xff, 0x25, 0, 0, 0, 0, // jmpq *got(%rip) - 0x68, 0, 0, 0, 0, // pushq - 0xe9, 0, 0, 0, 0, // jmpq plt[0] + 0x68, 0, 0, 0, 0, // pushq + 0xe9, 0, 0, 0, 0, // jmpq plt[0] }; memcpy(Buf, Inst, sizeof(Inst)); @@ -174,6 +194,23 @@ write32le(Buf + 12, -getPltEntryOffset(Index) - 16); } +template +void X86_64::writeIBTPlt(uint8_t *Buf, size_t NumEntries) const { + 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 + }; + + for (size_t I = 0; I < NumEntries; ++I) { + memcpy(Buf, Inst, sizeof(Inst)); + write32le(Buf + 5, I * sizeof(object::ELF64LE::Rela)); + write32le(Buf + 10, -getPltEntryOffset(I) - 14); + Buf += sizeof(Inst); + } +} + 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) diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -82,6 +82,7 @@ // Most fields are initialized by the driver. struct Configuration { uint8_t OSABI = 0; + uint32_t X86Features = 0; llvm::CachePruningPolicy ThinLTOCachePolicy; llvm::StringMap SectionStartMap; llvm::StringRef Chroot; @@ -145,6 +146,7 @@ bool ExportDynamic; bool FixCortexA53Errata843419; bool FormatBinary = false; + bool ForceCET; bool GcSections; bool GdbIndex; bool GnuHash = false; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -326,6 +326,9 @@ if (Config->SingleRoRx && !Script->HasSectionsCommand) error("-execute-only and -no-rosegment cannot be used together"); } + + if (Config->ZRetpolineplt && Config->ForceCET) + error("--force-cet may not be used with -z retpolineplt"); } static const char *getReproduceOption(opt::InputArgList &Args) { @@ -798,6 +801,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); @@ -1443,6 +1447,28 @@ Symtab->wrap(W.Sym, W.Real, W.Wrap); } +// In order to enable CET (x86's hardware-assited control flow enforcement), +// each source files are compiled as such by passing --xxx to the compiler [what +// is the compiler flag to enable CET?]. Object files compiled with the flag +// contain feature flags indicating that they are compatible with CET. We enable +// the feature only when all object files are compatible with CET. +// +// This function returns a merged feature flags. If 0, we cannot enable CET. +template static uint32_t getX86Features() { + if (Config->EMachine != EM_386 && Config->EMachine != EM_X86_64) + return 0; + + uint32_t Ret = -1; + for (InputFile *F : ObjectFiles) { + uint32_t Features = cast>(F)->X86Features; + if (!Features && Config->ForceCET) + error(toString(F) + + ": --force-cet: file is not compatible with CET"); + Ret &= Features; + } + return Ret; +} + static const char *LibcallRoutineNames[] = { #define HANDLE_LIBCALL(code, name) name, #include "llvm/IR/RuntimeLibcalls.def" @@ -1620,6 +1646,7 @@ }); Config->EFlags = Target->calcEFlags(); + Config->X86Features = getX86Features(); if (Config->EMachine == EM_ARM) { // FIXME: These warnings can be removed when lld only uses these features diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -215,6 +215,8 @@ // R_MIPS_GPREL16 / R_MIPS_GPREL32 relocations. uint32_t MipsGp0 = 0; + uint32_t X86Features = 0; + // Name of source file obtained from STT_FILE symbol value, // or empty string if there is no such symbol in object file // symbol table. diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -589,6 +589,65 @@ } } +// If a source file is compiled with x86 hardware-assisted call flow control +// enabled, the generated object file contains feature flags indicating that +// fact. This function reads the feature flags and returns it. +// +// Essentially we want to read a single 32-bit value in this function, but this +// function is rather compilcated because the value is buried deep inside a +// .note.gnu.property section. +// +// The section consists of one or more NOTE records. Each NOTE record consists +// 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 readX86Features(ObjFile *Obj, ArrayRef Data) { + typedef typename ELFT::Nhdr Elf_Nhdr; + typedef typename ELFT::Note Elf_Note; + + while (!Data.empty()) { + // Read one NOTE record. + if (Data.size() < sizeof(Elf_Nhdr)) + fatal(toString(Obj) + ": .note.gnu.property: section too short"); + + auto *Nhdr = reinterpret_cast(Data.data()); + if (Data.size() < Nhdr->getSize()) + fatal(toString(Obj) + ": .note.gnu.property: section too short"); + + Elf_Note Note(*Nhdr); + if (Nhdr->n_type != NT_GNU_PROPERTY_TYPE_0 || Note.getName() != "GNU") { + Data = Data.slice(Nhdr->getSize()); + continue; + } + + // Read a body of a NOTE record, which consists of type-length-value fields. + ArrayRef Desc = Note.getDesc(); + while (!Desc.empty()) { + if (Desc.size() < 8) + fatal(toString(Obj) + ": .note.gnu.property: section too short"); + + uint32_t Type = *reinterpret_cast(Desc.data()); + uint32_t Size = *reinterpret_cast(Desc.data() + 4) * 4; + if (Size == 0) + break; + + if (Type == GNU_PROPERTY_X86_FEATURE_1_AND) { + // We found the field. + return *reinterpret_cast(Desc.data() + 8); + } + + Desc = Desc.slice(Size); + } + + // Go to next NOTE record if a note section didn't contain + // X86_FEATURES_1_AND description. + Data = Data.slice(Nhdr->getSize()); + } + + return 0; +} + template InputSectionBase *ObjFile::getRelocTarget(const Elf_Shdr &Sec) { uint32_t Idx = Sec.sh_info; @@ -719,6 +778,19 @@ if (Name == ".note.GNU-stack") return &InputSection::Discarded; + // If an object file is compatible with Intel Control-Flow Enforcement + // Technology (CET), it has a .note.gnu.property section containing the + // GNU_PROPERTY_X86_FEATURE_1_IBT flag. Read a bitmap containing the flag. + // + // 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. + if (Name == ".note.gnu.property") { + ArrayRef Contents = check(this->getObj().getSectionContents(&Sec)); + this->X86Features = readX86Features(this, Contents); + return &InputSection::Discarded; + } + // Split stacks is a feature to support a discontiguous stack, // commonly used in the programming language Go. For the details, // see https://gcc.gnu.org/wiki/SplitStacks. An object file compiled diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -167,6 +167,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 x86 Control-Flow Enforcement Technology">; + defm format: Eq<"format", "Change the input format of the inputs following this option">, MetaVarName<"[default,elf,binary]">; diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h --- a/lld/ELF/SyntheticSections.h +++ b/lld/ELF/SyntheticSections.h @@ -142,9 +142,16 @@ size_t getSize() const override { return 0; } }; +class GnuPropertySection : public SyntheticSection { +public: + GnuPropertySection(); + void writeTo(uint8_t *Buf) override; + size_t getSize() const override { return 32; } +}; + // .note.gnu.build-id section. class BuildIdSection : public SyntheticSection { - // First 16 bytes are a header. + // First 16 bytes are a note header. static const unsigned HeaderSize = 16; public: @@ -658,14 +665,23 @@ bool empty() const override { return Entries.empty(); } void addSymbols(); template void addEntry(Symbol &Sym); + ArrayRef getEntries() { return Entries; } - size_t HeaderSize; + size_t HeaderSize = 0; private: std::vector Entries; bool IsIplt; }; +// This is x86-only. +class IBTPltSection : public SyntheticSection { +public: + IBTPltSection(); + void writeTo(uint8_t *Buf) override; + size_t getSize() const override; +}; + class GdbIndexSection final : public SyntheticSection { public: struct AddressEntry { @@ -1009,6 +1025,7 @@ SyntheticSection *Dynamic; StringTableSection *DynStrTab; SymbolTableBaseSection *DynSymTab; + GnuPropertySection *GnuProperty; GnuHashTableSection *GnuHashTab; HashTableSection *HashTab; GotSection *Got; @@ -1019,6 +1036,7 @@ MipsRldMapSection *MipsRldMap; PltSection *Plt; PltSection *Iplt; + IBTPltSection *IBTPlt; RelocationBaseSection *RelaDyn; RelrBaseSection *RelrDyn; RelocationBaseSection *RelaPlt; diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -291,6 +291,33 @@ } } +// This class represents a linker-synthesized .note.gnu.property section. +// +// If x86, each object file may contain feature flags indicating what features +// that object file is compatible with. The flags are stored in a +// .note.gnu.property section. +// +// lld reads the sections from input files and merges them by computing AND of +// the flags. The result is written as a new .note.gnu.property section. +// +// If the flag is zero (which indicates that the intersection of the feature +// sets is empty, or some input files didn't have .note.gnu.property sections), +// we don't create this section. +GnuPropertySection::GnuPropertySection() + : SyntheticSection(llvm::ELF::SHF_ALLOC, llvm::ELF::SHT_NOTE, 4, + ".note.gnu.property") {} + +void GnuPropertySection::writeTo(uint8_t *Buf) { + write32(Buf, 4); // Name size + write32(Buf + 4, 16); // Content size + write32(Buf + 8, NT_GNU_PROPERTY_TYPE_0); // Type + memcpy(Buf + 12, "GNU", 4); // Name string + write32(Buf + 16, GNU_PROPERTY_X86_FEATURE_1_AND); // Feature type + write32(Buf + 20, 4); // Feature size + write32(Buf + 24, Config->X86Features); // Feature flags + write32(Buf + 28, 0); // Padding +} + BuildIdSection::BuildIdSection() : SyntheticSection(SHF_ALLOC, SHT_NOTE, 4, ".note.gnu.build-id"), HashSize(getHashSize()) {} @@ -2312,10 +2339,22 @@ // On PowerPC64 the lazy symbol resolvers go into the `global linkage table` // in the .glink section, rather then the typical .plt section. PltSection::PltSection(bool IsIplt) - : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 16, - Config->EMachine == EM_PPC64 ? ".glink" : ".plt"), - HeaderSize(!IsIplt || Config->ZRetpolineplt ? Target->PltHeaderSize : 0), + : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 16, ""), IsIplt(IsIplt) { + bool IsX86Ibt = (Config->X86Features & GNU_PROPERTY_X86_FEATURE_1_IBT); + + if (Config->EMachine == EM_PPC64) + Name = ".glink"; + else if (IsX86Ibt) + Name = ".splt"; + else + Name = ".plt"; + + if (Config->ZRetpolineplt || (!IsIplt && !IsX86Ibt)) + HeaderSize = Target->PltHeaderSize; + else + HeaderSize = 0; + // The PLT needs to be writable on SPARC as the dynamic linker will // modify the instructions in the PLT entries. if (Config->EMachine == EM_SPARCV9) @@ -2339,7 +2378,7 @@ unsigned RelOff = RelSec->Entsize * I + PltOff; uint64_t Got = B->getGotPltVA(); uint64_t Plt = this->getVA() + Off; - Target->writePlt(Buf + Off, Got, Plt, B->PltIndex, RelOff); + Target->writePlt(Buf + Off, Got, Plt, I, RelOff); Off += Target->PltEntrySize; } } @@ -2367,6 +2406,74 @@ } } +// This is an x86-only extra PLT section and used only when a security +// enhancement feature called CET is enabled. In this comment, I'll explain what +// the feature is and why we have two PLT sections if CET is enabled. +// +// So, what does CET do? CET introduces a new restriction to indirect jump +// instructions. CET works this way. Assume that CET is enabled. Then, if you +// execute an indirect jump instruction, the processor verifies that a special +// "landing pad" instruction (which is actually a repurposed NOP instruction and +// now called "endbr32" or "endbr64") is at the jump target. If the jump target +// does not start with that instruction, the processor raises an exception +// instead of continue executing code. +// +// If CET is enabled, the compiler emits endbr to all locations where indirect +// jumps may jump to. +// +// This mechanism makes it extremely hard to transfer the control to a middle of +// a function that is not supporsed to be a indirect jump target, preventing +// certain types of attacks such as ROP or JOP. +// +// Note that the processors in the market as of early 2019 don't actually +// support the feature. The only spec is available at the moment. +// +// Now, I'll explain why we have this extra PLT section for CET. +// +// Since you can indirectly jump to a PLT entry, we have to make PLT entries +// start with endbr. The problem is there's no extra space for endbr (which is 4 +// byte long), as the PLT entry is only 16 byte long and all bytes are already +// used. +// +// In order to deal with the issue, we split a PLT entry into two PLT entries. +// Remember that each PLT entry contains code to jump to an address read from +// .got.plt AND code to resolve a dynamic symbol lazily. With the 2-PLT scheme, +// the former code is written to .splt, and the latter code is written to .plt. +// +// Lazy symbol resolution in the 2-PLT scheme works in the usual way, except +// that the regular .plt is now called .splt and .plt is repurposed to contain +// only code for lazy symbol resolution. +// +// In other words, this is how the 2-PLT scheme works. Application code is +// supposed to jump to .splt to call an external function. Each .splt entry +// contains code to read an address from a corresponding .got.plt entry and +// jump to that address. Addresses in .got.plt initially point to .plt, so +// when an application calls an external function for the first time, the +// control is transferred to a function to resolve a symbol name from external +// shared object files. That function then rewrites an .got.plt entry with a +// resolved address, so that the subsequent function calls directly jump to a +// desired location from .splt. +// +// There is an open question as to whether the 2-PLT scheme was desirable or +// not. We could have simply extended the PLT entry size to 32-bytes to +// accommodate endbr, and that scheme would have been much simpler than the +// 2-PLT scheme. One reason to split PLT was, by doing that, we could keep hot +// code (.splt) from cold code (.plt). But as far as I know no one proved the +// optimization actually makes a difference. +// +// That said, the 2-PLT scheme is a part of the ABI, debuggers and other tools +// depend on it, so we implement the ABI. +IBTPltSection::IBTPltSection() + : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 16, ".plt") {} + +void IBTPltSection::writeTo(uint8_t *Buf) { + Target->writeIBTPlt(Buf, In.Plt->getEntries().size()); +} + +size_t IBTPltSection::getSize() const { + return In.Plt->getEntries().size() * Target->PltEntrySize; +} + // The string hash function for .gdb_index. static uint32_t computeGdbHash(StringRef S) { uint32_t H = 0; diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -42,6 +42,7 @@ virtual void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const {} + virtual void writeIBTPlt(uint8_t *Buf, size_t NumEntries) const {} virtual void addPltHeaderSymbols(InputSection &IS) const {} virtual void addPltSymbols(InputSection &IS, uint64_t Off) const {} diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -410,6 +410,12 @@ In.Iplt = make(true); Add(In.Iplt); + if (Config->X86Features & GNU_PROPERTY_X86_FEATURE_1_IBT) + Add(make()); + + if (Config->X86Features) + 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 // section to control the executable-ness of the stack area, but that diff --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1 --- a/lld/docs/ld.lld.1 +++ b/lld/docs/ld.lld.1 @@ -182,6 +182,9 @@ field to the specified value. .It Fl -fini Ns = Ns Ar symbol Specify a finalizer function. +.It Fl -force-cet +Intel Control-Flow Enforcement Technology is enforced. An error is +reported if there is an input object file not compatible with CET. .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 diff --git a/lld/test/ELF/Inputs/x86-64-cet1.s b/lld/test/ELF/Inputs/x86-64-cet1.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/Inputs/x86-64-cet1.s @@ -0,0 +1,16 @@ +.section ".note.gnu.property", "a" +.long 4 +.long 16 +.long 5 +.asciz "GNU" + +.long 0xc0000002 +.long 4 +.long 3 +.long 0 + +.text +.globl func2 +.type func2,@function +func2: + ret diff --git a/lld/test/ELF/Inputs/x86-64-cet2.s b/lld/test/ELF/Inputs/x86-64-cet2.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/Inputs/x86-64-cet2.s @@ -0,0 +1,21 @@ +.section ".note.gnu.property", "a" +.long 4 +.long 32 +.long 5 +.asciz "GNU" + +.long 0xc0000000 +.long 4 +.long 0 +.long 0 + +.long 0xc0000002 +.long 4 +.long 3 +.long 0 + +.text +.globl func2 +.type func2,@function +func2: + ret diff --git a/lld/test/ELF/Inputs/x86-64-cet3.s b/lld/test/ELF/Inputs/x86-64-cet3.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/Inputs/x86-64-cet3.s @@ -0,0 +1,5 @@ +.text +.globl func2 +.type func2,@function +func2: + ret diff --git a/lld/test/ELF/Inputs/x86-64-cet4.s b/lld/test/ELF/Inputs/x86-64-cet4.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/Inputs/x86-64-cet4.s @@ -0,0 +1,16 @@ +.section ".note.gnu.property", "a" +.long 4 +.long 16 +.long 5 +.asciz "GNU" + +.long 0xc0000002 +.long 4 +.long 0xfffffffd +.long 0 + +.text +.globl func2 +.type func2,@function +func2: + ret diff --git a/lld/test/ELF/i386-cet.s b/lld/test/ELF/i386-cet.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/i386-cet.s @@ -0,0 +1,52 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %p/Inputs/x86-64-cet1.s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %p/Inputs/x86-64-cet2.s -o %t2.o +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %p/Inputs/x86-64-cet3.s -o %t3.o +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %p/Inputs/x86-64-cet4.s -o %t4.o + +# RUN: ld.lld -e func1 %t.o %t1.o -o %t +# RUN: llvm-readelf -n %t | FileCheck -check-prefix=CET -match-full-lines %s + +# RUN: ld.lld -e func1 %t.o %t2.o -o %t +# RUN: llvm-readelf -n %t | FileCheck -check-prefix=CET -match-full-lines %s + +# CET: Properties: x86 feature: IBT, SHSTK + +# RUN: ld.lld -e func1 %t.o %t3.o -o %t +# RUN: llvm-readelf -S %t | FileCheck -check-prefix=NOCET %s + +# NOCET: Section Headers +# NOCET-NOT: .note.gnu.property + +# RUN: ld.lld -e func1 %t.o %t4.o -o %t +# RUN: llvm-readelf -n %t | FileCheck -check-prefix=NOSHSTK -match-full-lines %s + +# Check .note.gnu.protery without property SHSTK. +# NOSHSTK: Properties: x86 feature: IBT + +# RUN: ld.lld -shared %t1.o -o %t1.so +# RUN: ld.lld -e func1 %t.o %t1.so -o %t +# RUN: llvm-readelf -n %t | FileCheck -check-prefix=CET -match-full-lines %s +# RUN: llvm-objdump -d %t | FileCheck -check-prefix=SPLT %s + +# Check if .note.gnu.property's name and splt is generated successfully. +# SPLT: splt + +.section ".note.gnu.property", "a" +.long 4 +.long 0x10 +.long 0x5 +.asciz "GNU" + +.long 0xc0000002 +.long 4 +.long 3 +.long 0 + +.text +.globl func1 +.type func1,@function +func1: + call func2 + ret diff --git a/lld/test/ELF/x86-64-cet.s b/lld/test/ELF/x86-64-cet.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/x86-64-cet.s @@ -0,0 +1,52 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/x86-64-cet1.s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/x86-64-cet2.s -o %t2.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/x86-64-cet3.s -o %t3.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/x86-64-cet4.s -o %t4.o + +# RUN: ld.lld -e func1 %t.o %t1.o -o %t +# RUN: llvm-readelf -n %t | FileCheck -check-prefix=CET -match-full-lines %s + +# RUN: ld.lld -e func1 %t.o %t2.o -o %t +# RUN: llvm-readelf -n %t | FileCheck -check-prefix=CET -match-full-lines %s + +# CET: Properties: x86 feature: IBT, SHSTK + +# RUN: ld.lld -e func1 %t.o %t3.o -o %t +# RUN: llvm-readelf -S %t | FileCheck -check-prefix=NOCET %s + +# NOCET: Section Headers +# NOCET-NOT: .note.gnu.property + +# RUN: ld.lld -e func1 %t.o %t4.o -o %t +# RUN: llvm-readelf -n %t | FileCheck -check-prefix=NOSHSTK -match-full-lines %s + +# Check .note.gnu.protery without property SHSTK. +# NOSHSTK: Properties: x86 feature: IBT + +# RUN: ld.lld -shared %t1.o -o %t1.so +# RUN: ld.lld -e func1 %t.o %t1.so -o %t +# RUN: llvm-readelf -n %t | FileCheck -check-prefix=CET -match-full-lines %s +# RUN: llvm-objdump -d %t | FileCheck -check-prefix=SPLT %s + +# Check if .note.gnu.property's name and splt is generated successfully. +# SPLT: splt + +.section ".note.gnu.property", "a" +.long 4 +.long 0x10 +.long 0x5 +.asciz "GNU" + +.long 0xc0000002 +.long 4 +.long 3 +.long 0 + +.text +.globl func1 +.type func1,@function +func1: + call func2 + ret diff --git a/llvm/include/llvm/Object/ELFTypes.h b/llvm/include/llvm/Object/ELFTypes.h --- a/llvm/include/llvm/Object/ELFTypes.h +++ b/llvm/include/llvm/Object/ELFTypes.h @@ -592,9 +592,9 @@ template friend class Elf_Note_Iterator_Impl; +public: Elf_Note_Impl(const Elf_Nhdr_Impl &Nhdr) : Nhdr(Nhdr) {} -public: /// Get the note's name, excluding the terminating null byte. StringRef getName() const { if (!Nhdr.n_namesz)