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 @@ -409,6 +409,70 @@ memcpy(Loc - 2, Inst, sizeof(Inst)); } +// If Intel CET (Control-Flow Enforcement Technology) is enabled, +// we have to emit special PLT entries containing endbr32 instructions. +namespace { +class IntelCET : public X86 { +public: + IntelCET(); + void writeGotPlt(uint8_t *Buf, const Symbol &S) 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; + + enum { SPltHeaderSize = 16 }; +}; +} // namespace + +IntelCET::IntelCET() { PltHeaderSize = 0; } + +void IntelCET::writeGotPlt(uint8_t *Buf, const Symbol &S) const { + uint64_t VA = In.IBTPlt->getVA() + SPltHeaderSize + S.PltIndex * PltEntrySize; + write32le(Buf, VA); +} + +void IntelCET::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, + uint64_t PltEntryAddr, int32_t Index, + unsigned RelOff) const { + if (Config->Pic) { + const uint8_t Inst[] = { + 0xf3, 0x0f, 0x1e, 0xfb, // endbr32 + 0xff, 0xa3, 0, 0, 0, 0, // jmp *name@GOT(%ebx) + 0x66, 0x0f, 0x1f, 0x44, 0, 0, // nop + }; + memcpy(Buf, Inst, sizeof(Inst)); + write32le(Buf + 6, GotPltEntryAddr - In.GotPlt->getVA()); + return; + } + + const uint8_t Inst[] = { + 0xf3, 0x0f, 0x1e, 0xfb, // endbr32 + 0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT + 0x66, 0x0f, 0x1f, 0x44, 0, 0, // nop + }; + memcpy(Buf, Inst, sizeof(Inst)); + write32le(Buf + 6, GotPltEntryAddr); +} + +void IntelCET::writeIBTPlt(uint8_t *Buf, size_t NumEntries) const { + writePltHeader(Buf); + Buf += SPltHeaderSize; + + 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, -PltHeaderSize - sizeof(Inst) * I - 30); + Buf += sizeof(Inst); + } +} + namespace { class RetpolinePic : public X86 { public: @@ -550,6 +614,11 @@ return &T; } + if (Config->X86Features & GNU_PROPERTY_X86_FEATURE_1_IBT) { + static IntelCET T; + return &T; + } + static X86 T; return &T; } 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 @@ -144,7 +144,7 @@ }; memcpy(Buf, PltData, sizeof(PltData)); uint64_t GotPlt = In.GotPlt->getVA(); - uint64_t Plt = In.Plt->getVA(); + uint64_t Plt = In.IBTPlt ? In.IBTPlt->getVA() : In.Plt->getVA(); write32le(Buf + 2, GotPlt - Plt + 2); // GOTPLT+8 write32le(Buf + 8, GotPlt - Plt + 4); // GOTPLT+16 } @@ -525,6 +525,59 @@ return false; } +// If Intel CET (Control-Flow Enforcement Technology) is enabled, +// we have to emit special PLT entries containing endbr64 instructions. +namespace { +class IntelCET : public X86_64 { +public: + IntelCET(); + void writeGotPlt(uint8_t *Buf, const Symbol &S) 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; + + enum { SPltHeaderSize = 16 }; +}; +} // namespace + +IntelCET::IntelCET() { PltHeaderSize = 0; } + +void IntelCET::writeGotPlt(uint8_t *Buf, const Symbol &S) const { + uint64_t VA = In.IBTPlt->getVA() + SPltHeaderSize + S.PltIndex * PltEntrySize; + write64le(Buf, VA); +} + +void IntelCET::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, + uint64_t PltEntryAddr, int32_t Index, + unsigned RelOff) 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)); + write32le(Buf + 6, GotPltEntryAddr - PltEntryAddr - 10); +} + +void IntelCET::writeIBTPlt(uint8_t *Buf, size_t NumEntries) const { + writePltHeader(Buf); + Buf += SPltHeaderSize; + + 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); + write32le(Buf + 10, -PltHeaderSize - sizeof(Inst) * I - 30); + Buf += sizeof(Inst); + } +} + // These nonstandard PLT entries are to migtigate Spectre v2 security // vulnerability. In order to mitigate Spectre v2, we want to avoid indirect // branch instructions such as `jmp *GOTPLT(%rip)`. So, in the following PLT @@ -652,6 +705,11 @@ return &T; } + if (Config->X86Features & GNU_PROPERTY_X86_FEATURE_1_IBT) { + static IntelCET T; + return &T; + } + static X86_64 T; return &T; } 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; @@ -146,6 +147,7 @@ bool ExportDynamic; bool FixCortexA53Errata843419; bool FormatBinary = false; + bool RequireCET; 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->RequireCET) + error("--require-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->RequireCET = Args.hasArg(OPT_require_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); @@ -1445,6 +1449,27 @@ Symtab->wrap(W.Sym, W.Real, W.Wrap); } +// To enable CET (x86's hardware-assited control flow enforcement), each +// source file must be compiled with -fcf-protection. 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 the 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->RequireCET) + error(toString(F) + ": --require-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" @@ -1617,6 +1642,8 @@ }); } + Config->X86Features = getX86Features(); + // The Target instance handles target-specific stuff, such as applying // relocations or writing a PLT section. It also contains target-dependent // values such as a default image base address. 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 @@ -24,6 +24,7 @@ #include "llvm/Object/ELFObjectFile.h" #include "llvm/Support/ARMAttributeParser.h" #include "llvm/Support/ARMBuildAttributes.h" +#include "llvm/Support/Endian.h" #include "llvm/Support/Path.h" #include "llvm/Support/TarWriter.h" #include "llvm/Support/raw_ostream.h" @@ -33,6 +34,7 @@ using namespace llvm::object; using namespace llvm::sys; using namespace llvm::sys::fs; +using namespace llvm::support::endian; using namespace lld; using namespace lld::elf; @@ -589,6 +591,68 @@ } } +// 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 complicated 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) { + using Elf_Nhdr = typename ELFT::Nhdr; + using Elf_Note = typename ELFT::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 = read32le(Desc.data()); + uint32_t Size = read32le(Desc.data() + 4); + + if (Type == GNU_PROPERTY_X86_FEATURE_1_AND) { + // We found the field. + return read32le(Desc.data() + 8); + } + + // On 64-bit, a payload may be followed by a 4-byte padding to make its + // size a multiple of 8. + if (ELFT::Is64Bits) + Size = alignTo(Size, 8); + + Desc = Desc.slice(Size + 8); // +8 for Type and 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 +783,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 require_cet: F<"require-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,6 +142,13 @@ 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. @@ -662,14 +669,23 @@ bool empty() const override { return Entries.empty(); } void addSymbols(); template void addEntry(Symbol &Sym); + size_t getNumEntries() { return Entries.size(); } - 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 { @@ -1081,6 +1097,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,32 @@ } } +// This class represents a linker-synthesized .note.gnu.property section. +// +// In x86, object files may contain feature flags indicating the features that +// they have used. 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()) {} @@ -2308,10 +2334,23 @@ // 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 = ".plt.sec"; + else + Name = ".plt"; + + HeaderSize = IsIplt ? 0 : Target->PltHeaderSize; + + // A retpoline PLT always has a header even for IPLT. + if (Config->ZRetpolineplt) + HeaderSize = Target->PltHeaderSize; + // 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) @@ -2335,7 +2374,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; } } @@ -2363,6 +2402,76 @@ } } +// 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. Only the 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 +// bytes long), as the PLT entry is only 16 bytes 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 .plt.sec, 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 .plt.sec 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 .plt.sec to call an external function. Each .plt.sec +// 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 that resolves a symbol name from +// external shared object files. That function then rewrites a .got.plt entry +// with a resolved address, so that the subsequent function calls directly jump +// to a desired location from .plt.sec. +// +// 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 (.plt.sec) from cold code (.plt). But as far as I know no one proved +// that 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->getNumEntries()); +} + +size_t IBTPltSection::getSize() const { + // 16 is the header size of .plt.sec. + return 16 + In.Plt->getNumEntries() * 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 @@ -421,11 +421,19 @@ false /*Sort*/); Add(In.RelaIplt); + if (Config->X86Features & GNU_PROPERTY_X86_FEATURE_1_IBT) { + In.IBTPlt = make(); + Add(In.IBTPlt); + } + In.Plt = make(false); Add(In.Plt); In.Iplt = make(true); Add(In.Iplt); + 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 -require-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/i386-cet1.s b/lld/test/ELF/Inputs/i386-cet1.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/Inputs/i386-cet1.s @@ -0,0 +1,15 @@ +.section ".note.gnu.property", "a" +.long 4 +.long 12 +.long 5 +.asciz "GNU" + +.long 0xc0000002 +.long 4 +.long 3 + +.text +.globl func2 +.type func2,@function +func2: + ret diff --git a/lld/test/ELF/Inputs/i386-cet2.s b/lld/test/ELF/Inputs/i386-cet2.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/Inputs/i386-cet2.s @@ -0,0 +1,19 @@ +.section ".note.gnu.property", "a" +.long 4 +.long 24 +.long 5 +.asciz "GNU" + +.long 0xc0000000 +.long 4 +.long 0 + +.long 0xc0000002 +.long 4 +.long 3 + +.text +.globl func2 +.type func2,@function +func2: + ret diff --git a/lld/test/ELF/Inputs/i386-cet3.s b/lld/test/ELF/Inputs/i386-cet3.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/Inputs/i386-cet3.s @@ -0,0 +1,5 @@ +.text +.globl func2 +.type func2,@function +func2: + ret diff --git a/lld/test/ELF/Inputs/i386-cet4.s b/lld/test/ELF/Inputs/i386-cet4.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/Inputs/i386-cet4.s @@ -0,0 +1,15 @@ +.section ".note.gnu.property", "a" +.long 4 +.long 12 +.long 5 +.asciz "GNU" + +.long 0xc0000002 +.long 4 +.long 0xfffffffd + +.text +.globl func2 +.type func2,@function +func2: + ret 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,78 @@ +# 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/i386-cet1.s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %p/Inputs/i386-cet2.s -o %t2.o +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %p/Inputs/i386-cet3.s -o %t3.o +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %p/Inputs/i386-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: not ld.lld -e func1 %t.o %t3.o -o %t --require-cet 2>&1 \ +# RUN: | FileCheck -check-prefix=ERROR %s +# ERROR: i386-cet.s.tmp3.o: --require-cet: file is not compatible with CET + +# 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 -s -d %t | FileCheck -check-prefix=DISASM %s + +# DISASM: Disassembly of section .text: +# DISASM-NEXT: 0000000000401000 func1: +# DISASM-NEXT: 401000: e8 2b 00 00 00 calll 43 +# DISASM-NEXT: 401005: c3 retl +# DISASM-NEXT: Disassembly of section .plt: +# DISASM-NEXT: 0000000000401010 .plt: +# DISASM-NEXT: 401010: ff 35 04 30 40 00 pushl 4206596 +# DISASM-NEXT: 401016: ff 25 08 30 40 00 jmpl *4206600 +# DISASM-NEXT: 40101c: 90 nop +# DISASM-NEXT: 40101d: 90 nop +# DISASM-NEXT: 40101e: 90 nop +# DISASM-NEXT: 40101f: 90 nop +# DISASM-NEXT: 401020: f3 0f 1e fb endbr32 +# DISASM-NEXT: 401024: 68 00 00 00 00 pushl $0 +# DISASM-NEXT: 401029: e9 e2 ff ff ff jmp -30 <.plt> +# DISASM-NEXT: 40102e: 66 90 nop +# DISASM-NEXT: Disassembly of section .plt.sec: +# DISASM-NEXT: 0000000000401030 .plt.sec: +# DISASM-NEXT: 401030: f3 0f 1e fb endbr32 +# DISASM-NEXT: 401034: ff 25 0c 30 40 00 jmpl *4206604 +# DISASM-NEXT: 40103a: 66 0f 1f 44 00 00 nopw (%eax,%eax) +# +# DISASM: Contents of section .got.plt: +# DISASM-NEXT: 403000 00204000 00000000 00000000 20104000 + +.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,76 @@ +# 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: not ld.lld -e func1 %t.o %t3.o -o %t --require-cet 2>&1 \ +# RUN: | FileCheck -check-prefix=ERROR %s +# ERROR: x86-64-cet.s.tmp3.o: --require-cet: file is not compatible with CET + +# 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 -s -d %t | FileCheck -check-prefix=DISASM %s + +# DISASM: Disassembly of section .text: +# DISASM-NEXT: 0000000000201000 func1: +# DISASM-NEXT: 201000: e8 2b 00 00 00 callq 43 +# DISASM-NEXT: 201005: c3 retq +# DISASM-NEXT: Disassembly of section .plt: +# DISASM-NEXT: 0000000000201010 .plt: +# DISASM-NEXT: 201010: ff 35 f2 1f 00 00 pushq 8178(%rip) +# DISASM-NEXT: 201016: ff 25 f4 1f 00 00 jmpq *8180(%rip) +# DISASM-NEXT: 20101c: 0f 1f 40 00 nopl (%rax) +# DISASM-NEXT: 201020: f3 0f 1e fa endbr64 +# DISASM-NEXT: 201024: 68 00 00 00 00 pushq $0 +# DISASM-NEXT: 201029: e9 e2 ff ff ff jmp -30 <.plt> +# DISASM-NEXT: 20102e: 66 90 nop +# DISASM-NEXT: Disassembly of section .plt.sec: +# DISASM-NEXT: 0000000000201030 .plt.sec: +# DISASM-NEXT: 201030: f3 0f 1e fa endbr64 +# DISASM-NEXT: 201034: ff 25 de 1f 00 00 jmpq *8158(%rip) +# DISASM-NEXT: 20103a: 66 0f 1f 44 00 00 nopw (%rax,%rax) +# +# DISASM: Contents of section .got.plt: +# DISASM-NEXT: 203000 00202000 00000000 00000000 00000000 +# DISASM-NEXT: 203010 00000000 00000000 20102000 00000000 + +.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)