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 @@ -410,6 +410,71 @@ memcpy(loc - 2, inst, sizeof(inst)); } +// If Intel Indirect Branch Tracking is enabled, we have to emit special PLT +// entries containing endbr32 instructions. A PLT entry will be split into two +// parts, one in .plt.sec (writePlt), and the other in .plt (writeIBTPlt). +namespace { +class IntelIBT : public X86 { +public: + IntelIBT(); + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePlt(uint8_t *buf, const Symbol &sym, + uint64_t pltEntryAddr) const override; + void writeIBTPlt(uint8_t *buf, size_t numEntries) const override; + + static const unsigned IBTPltHeaderSize = 16; +}; +} // namespace + +IntelIBT::IntelIBT() { pltHeaderSize = 0; } + +void IntelIBT::writeGotPlt(uint8_t *buf, const Symbol &s) const { + uint64_t va = + in.ibtPlt->getVA() + IBTPltHeaderSize + s.pltIndex * pltEntrySize; + write32le(buf, va); +} + +void IntelIBT::writePlt(uint8_t *buf, const Symbol &sym, + uint64_t /*pltEntryAddr*/) const { + if (config->isPic) { + 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, sym.getGotPltVA() - 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, sym.getGotPltVA()); +} + +void IntelIBT::writeIBTPlt(uint8_t *buf, size_t numEntries) const { + writePltHeader(buf); + buf += IBTPltHeaderSize; + + 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: @@ -553,6 +618,11 @@ return &t; } + if (config->andFeatures & GNU_PROPERTY_X86_FEATURE_1_IBT) { + static IntelIBT 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 @@ -151,7 +151,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 } @@ -568,6 +568,60 @@ return false; } +// If Intel Indirect Branch Tracking is enabled, we have to emit special PLT +// entries containing endbr64 instructions. A PLT entry will be split into two +// parts, one in .plt.sec (writePlt), and the other in .plt (writeIBTPlt). +namespace { +class IntelIBT : public X86_64 { +public: + IntelIBT(); + void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writePlt(uint8_t *buf, const Symbol &sym, + uint64_t pltEntryAddr) const override; + void writeIBTPlt(uint8_t *buf, size_t numEntries) const override; + + static const unsigned IBTPltHeaderSize = 16; +}; +} // namespace + +IntelIBT::IntelIBT() { pltHeaderSize = 0; } + +void IntelIBT::writeGotPlt(uint8_t *buf, const Symbol &s) const { + uint64_t va = + in.ibtPlt->getVA() + IBTPltHeaderSize + s.pltIndex * pltEntrySize; + write64le(buf, va); +} + +void IntelIBT::writePlt(uint8_t *buf, const Symbol &sym, + 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)); + write32le(buf + 6, sym.getGotPltVA() - pltEntryAddr - 10); +} + +void IntelIBT::writeIBTPlt(uint8_t *buf, size_t numEntries) const { + writePltHeader(buf); + buf += IBTPltHeaderSize; + + 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 @@ -695,6 +749,11 @@ return &t; } + if (config->andFeatures & GNU_PROPERTY_X86_FEATURE_1_IBT) { + static IntelIBT 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 @@ -154,7 +154,6 @@ bool fixCortexA8; bool forceBTI; bool formatBinary = false; - bool requireCET; bool gcSections; bool gdbIndex; bool gnuHash = false; @@ -202,6 +201,7 @@ bool writeAddends; bool zCombreloc; bool zCopyreloc; + bool zForceIbt; bool zGlobal; bool zHazardplt; bool zIfuncNoplt; @@ -215,6 +215,7 @@ bool zOrigin; bool zRelro; bool zRodynamic; + bool zShstk; bool zText; bool zRetpolineplt; bool zWxneeded; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -346,8 +346,8 @@ 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"); + if (config->zRetpolineplt && config->zForceIbt) + error("-z force-ibt may not be used with -z retpolineplt"); if (config->emachine != EM_AARCH64) { if (config->pacPlt) @@ -409,18 +409,18 @@ static bool isKnownZFlag(StringRef s) { return s == "combreloc" || s == "copyreloc" || s == "defs" || - s == "execstack" || s == "force-bti" || s == "global" || - s == "hazardplt" || s == "ifunc-noplt" || s == "initfirst" || - s == "interpose" || s == "keep-text-section-prefix" || s == "lazy" || - s == "muldefs" || s == "separate-code" || - s == "separate-loadable-segments" || s == "nocombreloc" || - s == "nocopyreloc" || s == "nodefaultlib" || s == "nodelete" || - s == "nodlopen" || s == "noexecstack" || s == "nognustack" || - s == "nokeep-text-section-prefix" || s == "norelro" || - s == "noseparate-code" || s == "notext" || s == "now" || - s == "origin" || s == "pac-plt" || s == "relro" || - s == "retpolineplt" || s == "rodynamic" || s == "text" || - s == "undefs" || s == "wxneeded" || + s == "execstack" || s == "force-bti" || s == "force-ibt" || + s == "global" || s == "hazardplt" || s == "ifunc-noplt" || + s == "initfirst" || s == "interpose" || + s == "keep-text-section-prefix" || s == "lazy" || s == "muldefs" || + s == "separate-code" || s == "separate-loadable-segments" || + s == "nocombreloc" || s == "nocopyreloc" || s == "nodefaultlib" || + s == "nodelete" || s == "nodlopen" || s == "noexecstack" || + s == "nognustack" || s == "nokeep-text-section-prefix" || + s == "norelro" || s == "noseparate-code" || s == "notext" || + s == "now" || s == "origin" || s == "pac-plt" || s == "relro" || + s == "retpolineplt" || s == "rodynamic" || s == "shstk" || + s == "text" || s == "undefs" || s == "wxneeded" || s.startswith("common-page-size=") || s.startswith("max-page-size=") || s.startswith("stack-size="); } @@ -877,7 +877,6 @@ config->fixCortexA53Errata843419 = args.hasArg(OPT_fix_cortex_a53_843419); config->fixCortexA8 = args.hasArg(OPT_fix_cortex_a8); config->forceBTI = hasZOption(args, "force-bti"); - 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); @@ -966,6 +965,7 @@ args.hasFlag(OPT_warn_symbol_ordering, OPT_no_warn_symbol_ordering, true); config->zCombreloc = getZFlag(args, "combreloc", "nocombreloc", true); config->zCopyreloc = getZFlag(args, "copyreloc", "nocopyreloc", true); + config->zForceIbt = hasZOption(args, "force-ibt"); config->zGlobal = hasZOption(args, "global"); config->zGnustack = getZGnuStack(args); config->zHazardplt = hasZOption(args, "hazardplt"); @@ -983,6 +983,7 @@ config->zRetpolineplt = hasZOption(args, "retpolineplt"); config->zRodynamic = hasZOption(args, "rodynamic"); config->zSeparate = getZSeparate(args); + config->zShstk = hasZOption(args, "shstk"); config->zStackSize = args::getZOptionValue(args, OPT_z, "stack-size", 0); config->zText = getZFlag(args, "text", "notext", true); config->zWxneeded = hasZOption(args, "wxneeded"); @@ -1687,12 +1688,8 @@ // 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. // This is also the case with AARCH64's BTI and PAC which use the similar // GNU_PROPERTY_AARCH64_FEATURE_1_AND mechanism. -// -// Note that the CET-aware PLT is not implemented yet. We do error -// check only. template static uint32_t getAndFeatures() { if (config->emachine != EM_386 && config->emachine != EM_X86_64 && config->emachine != EM_AARCH64) @@ -1704,8 +1701,12 @@ if (config->forceBTI && !(features & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)) { warn(toString(f) + ": -z force-bti: file does not have BTI property"); features |= GNU_PROPERTY_AARCH64_FEATURE_1_BTI; - } else if (!features && config->requireCET) - error(toString(f) + ": --require-cet: file is not compatible with CET"); + } else if (config->zForceIbt && + !(features & GNU_PROPERTY_X86_FEATURE_1_IBT)) { + warn(toString(f) + ": -z force-ibt: file does not have " + "GNU_PROPERTY_X86_FEATURE_1_IBT property"); + features |= GNU_PROPERTY_X86_FEATURE_1_IBT; + } ret &= features; } @@ -1713,6 +1714,9 @@ // this does not require support in the object for correctness. if (config->pacPlt) ret |= GNU_PROPERTY_AARCH64_FEATURE_1_PAC; + // Force enable Shadow Stack. + if (config->zShstk) + ret |= GNU_PROPERTY_X86_FEATURE_1_SHSTK; return ret; } diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -176,9 +176,8 @@ def fix_cortex_a8: F<"fix-cortex-a8">, HelpText<"Apply fixes for ARM Cortex-A8 erratum 657417">; -// This option is intentionally hidden from the user as the implementation -// is not complete. -def require_cet: F<"require-cet">; +def force_bti: F<"force-bti">, + HelpText<"Force enable AArch64 BTI in PLT, warn if Input ELF file does not have GNU_PROPERTY_AARCH64_FEATURE_1_BTI property">; 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 @@ -665,6 +665,14 @@ // Used for PLT entries. It usually has a PLT header for lazy binding. Each PLT // entry is associated with a JUMP_SLOT relocation, which may be resolved lazily // at runtime. +// +// On PowerPC, this section contains lazy symbol resolvers. A branch instruction +// jumps to a PLT call stub, which will then jump to the target (BIND_NOW) or a +// lazy symbol resolver. +// +// On x86 when IBT is enabled, this section (.plt.sec) contains PLT call stubs. +// A call instruction jumps to a .plt.sec entry, which will then jump to the +// target (BIND_NOW) or a .plt entry. class PltSection : public SyntheticSection { public: PltSection(); @@ -673,8 +681,9 @@ bool isNeeded() const override; void addSymbols(); void addEntry(Symbol &sym); + size_t getNumEntries() const { return entries.size(); } - size_t headerSize; + size_t headerSize = 0; private: std::vector entries; @@ -696,6 +705,14 @@ void addEntry(Symbol &sym); }; +// 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 { @@ -1178,6 +1195,7 @@ PltSection *plt; IpltSection *iplt; PPC32Got2Section *ppc32Got2; + IBTPltSection *ibtPlt; RelocationBaseSection *relaPlt; RelocationBaseSection *relaIplt; StringTableSection *shStrTab; diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -2442,16 +2442,21 @@ } } -// On PowerPC64 the lazy symbol resolvers go into the `global linkage table` -// in the .glink section, rather then the typical .plt section. PltSection::PltSection() : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 16, ".plt"), headerSize(target->pltHeaderSize) { + // On PowerPC, this section contains lazy symbol resolvers. if (config->emachine == EM_PPC || config->emachine == EM_PPC64) { name = ".glink"; alignment = 4; } + // On x86 when IBT is enabled, this section contains the second PLT (lazy + // symbol resolvers). + if ((config->emachine == EM_386 || config->emachine == EM_X86_64) && + (config->andFeatures & GNU_PROPERTY_X86_FEATURE_1_IBT)) + name = ".plt.sec"; + // 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) @@ -2535,6 +2540,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 continuing 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 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. + 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 @@ -48,6 +48,7 @@ // All but PPC32 and PPC64 use the same format for .plt and .iplt entries. writePlt(buf, sym, pltEntryAddr); } + virtual void writeIBTPlt(uint8_t *buf, size_t numEntries) const {} virtual void addPltHeaderSymbols(InputSection &isec) const {} virtual void addPltSymbols(InputSection &isec, 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 @@ -515,6 +515,12 @@ /*sort=*/false); add(in.relaIplt); + if ((config->emachine == EM_386 || config->emachine == EM_X86_64) && + (config->andFeatures & GNU_PROPERTY_X86_FEATURE_1_IBT)) { + in.ibtPlt = make(); + add(in.ibtPlt); + } + in.plt = make(); add(in.plt); in.iplt = make(); 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 @@ -435,6 +435,9 @@ version.txt containing the output of ld.lld --version. The archive when unpacked can be used to re-run the linker with the same options and input files. +.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 -retain-symbols-file Ns = Ns Ar file Retain only the symbols listed in the file. .It Fl -rpath Ns = Ns Ar value , Fl R Ar value diff --git a/lld/test/ELF/i386-cet.s b/lld/test/ELF/i386-cet.s deleted file mode 100644 --- a/lld/test/ELF/i386-cet.s +++ /dev/null @@ -1,47 +0,0 @@ -# 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 - -.section ".note.gnu.property", "a" -.long 4 -.long 0xc -.long 0x5 -.asciz "GNU" - -.long 0xc0000002 -.long 4 -.long 3 - -.text -.globl func1 -.type func1,@function -func1: - call func2 - ret diff --git a/lld/test/ELF/i386-feature-cet.s b/lld/test/ELF/i386-feature-cet.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/i386-feature-cet.s @@ -0,0 +1,93 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=i386 %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=i386 %p/Inputs/i386-cet1.s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=i386 %p/Inputs/i386-cet2.s -o %t2.o +# RUN: llvm-mc -filetype=obj -triple=i386 %p/Inputs/i386-cet3.s -o %t3.o +# RUN: llvm-mc -filetype=obj -triple=i386 %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: ld.lld -e func1 %t.o %t3.o -o %t -z force-ibt 2>&1 \ +# RUN: | FileCheck --check-prefix=WARN %s +# WARN: {{.*}}.o: -z force-ibt: file does not have GNU_PROPERTY_X86_FEATURE_1_IBT property + +# RUN: ld.lld -e func1 %t.o %t4.o -o %t +# RUN: llvm-readelf -n %t | FileCheck --check-prefix=NOSHSTK %s + +# Check .note.gnu.protery without property SHSTK. +# NOSHSTK: Properties: x86 feature: IBT{{$}} + +# RUN: ld.lld -shared %t1.o -soname=so -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-readelf -x .got.plt %t | FileCheck --check-prefix=GOTPLT %s +# RUN: llvm-objdump -d --no-show-raw-insn --print-imm-hex %t | FileCheck --check-prefix=DISASM %s + +# GOTPLT: Hex dump of section '.got.plt': +# GOTPLT-NEXT: 0x004032d0 50224000 00000000 00000000 20124000 +# GOTPLT-NEXT: 0x004032e0 0b124000 + +# DISASM: Disassembly of section .text: +# DISASM: 00401200 func1: +# DISASM-NEXT: 401200: calll 0x2b +# DISASM-NEXT: 401205: calll 0x36 +# DISASM-NEXT: retl + +# DISASM: Disassembly of section .plt: +# DISASM: 00401210 .plt: +# DISASM-NEXT: 401210: pushl 0x4032d4 +# DISASM-NEXT: jmpl *0x4032d8 +# DISASM-NEXT: nop +# DISASM-NEXT: nop +# DISASM-NEXT: nop +# DISASM-NEXT: nop +# DISASM-NEXT: endbr32 +# DISASM-NEXT: pushl $0x0 +# DISASM-NEXT: jmp -0x1e <.plt> +# DISASM-NEXT: nop + +# DISASM: Disassembly of section .plt.sec: +# DISASM: 00401230 .plt.sec: +# DISASM-NEXT: 401230: endbr32 +# DISASM-NEXT: jmpl *0x4032dc +# DISASM-NEXT: nopw (%eax,%eax) + +# DISASM: Disassembly of section .iplt: +# DISASM: 00401240 ifunc: +# DISASM-NEXT: 401240: endbr32 +# DISASM-NEXT: jmpl *0x4032e0 +# DISASM-NEXT: nopw (%eax,%eax) + +.section ".note.gnu.property", "a" +.long 4 +.long 0xc +.long 0x5 +.asciz "GNU" + +.long 0xc0000002 # GNU_PROPERTY_X86_FEATURE_1_AND +.long 4 +.long 3 # GNU_PROPERTY_X86_FEATURE_1_IBT and SHSTK + +.text +.globl func1 +.type func1,@function +func1: + call func2 + call ifunc + ret + +.type ifunc,@gnu_indirect_function +ifunc: + ret diff --git a/lld/test/ELF/x86-64-cet.s b/lld/test/ELF/x86-64-cet.s deleted file mode 100644 --- a/lld/test/ELF/x86-64-cet.s +++ /dev/null @@ -1,48 +0,0 @@ -# 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 - -.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-feature-cet.s b/lld/test/ELF/x86-64-feature-cet.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/x86-64-feature-cet.s @@ -0,0 +1,92 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=x86_64 %p/Inputs/x86-64-cet1.s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=x86_64 %p/Inputs/x86-64-cet2.s -o %t2.o +# RUN: llvm-mc -filetype=obj -triple=x86_64 %p/Inputs/x86-64-cet3.s -o %t3.o +# RUN: llvm-mc -filetype=obj -triple=x86_64 %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 %t3.o -o %t -z force-ibt 2>&1 \ +# RUN: | FileCheck --check-prefix=WARN %s +# WARN: {{.*}}.o: -z force-ibt: file does not have GNU_PROPERTY_X86_FEATURE_1_IBT property + +# RUN: ld.lld -e func1 %t.o %t4.o -o %t +# RUN: llvm-readelf -n %t | FileCheck --check-prefix=NOSHSTK %s + +# Check .note.gnu.protery without property SHSTK. +# NOSHSTK: Properties: x86 feature: IBT{{$}} + +# RUN: ld.lld -shared %t1.o -soname=so -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-readelf -x .got.plt %t | FileCheck --check-prefix=GOTPLT %s +# RUN: llvm-objdump -d --no-show-raw-insn --print-imm-hex %t | FileCheck --check-prefix=DISASM %s + +# GOTPLT: Hex dump of section '.got.plt': +# GOTPLT-NEXT: 203480 80232000 00000000 00000000 00000000 +# GOTPLT-NEXT: 203490 00000000 00000000 50132000 00000000 +# GOTPLT-NEXT: 2034a0 00000000 00000000 + +# DISASM: Disassembly of section .text: +# DISASM: 0000000000201330 func1: +# DISASM-NEXT: 201330: callq 0x2b +# DISASM-NEXT: 201335: callq 0x36 +# DISASM-NEXT: retq + +# DISASM: Disassembly of section .plt: +# DISASM: 0000000000201340 .plt: +# DISASM-NEXT: 201340: pushq 0x2142(%rip) +# DISASM-NEXT: jmpq *0x2144(%rip) +# DISASM-NEXT: nopl (%rax) +# DISASM-NEXT: endbr64 +# DISASM-NEXT: pushq $0x0 +# DISASM-NEXT: jmp -0x1e <.plt> +# DISASM-NEXT: nop + +# DISASM: Disassembly of section .plt.sec: +# DISASM: 0000000000201360 .plt.sec: +# DISASM-NEXT: 201360: endbr64 +# DISASM-NEXT: jmpq *0x212e(%rip) +# DISASM-NEXT: nopw (%rax,%rax) + +# DISASM: Disassembly of section .iplt: +# DISASM: 0000000000201370 .iplt: +# DISASM-NEXT: 201370: endbr64 +# DISASM-NEXT: jmpq *0x2126(%rip) +# DISASM-NEXT: nopw (%rax,%rax) + +.section ".note.gnu.property", "a" +.long 4 +.long 0x10 +.long 0x5 +.asciz "GNU" + +.long 0xc0000002 # GNU_PROPERTY_X86_FEATURE_1_AND +.long 4 +.long 3 # GNU_PROPERTY_X86_FEATURE_1_IBT and SHSTK +.long 0 + +.text +.globl func1 +.type func1,@function +func1: + call func2 + call ifunc + ret + +.type ifunc,@gnu_indirect_function +ifunc: + ret