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,71 @@ 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; + + static const unsigned IBTPltHeaderSize = 16; +}; +} // namespace + +IntelCET::IntelCET() { pltHeaderSize = 0; } + +void IntelCET::writeGotPlt(uint8_t *buf, const Symbol &s) const { + uint64_t va = + in.ibtPlt->getVA() + IBTPltHeaderSize + 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->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, 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 += 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: @@ -550,6 +615,11 @@ return &t; } + if (config->andFeatures & 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 @@ -150,7 +150,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 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; + + static const unsigned IBTPltHeaderSize = 16; +}; +} // namespace + +IntelCET::IntelCET() { pltHeaderSize = 0; } + +void IntelCET::writeGotPlt(uint8_t *buf, const Symbol &s) const { + uint64_t va = + in.ibtPlt->getVA() + IBTPltHeaderSize + 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 += 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 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 @@ -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 @@ -349,8 +349,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) @@ -412,18 +412,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="); } @@ -880,7 +880,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); @@ -969,6 +968,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"); @@ -986,6 +986,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"); @@ -1690,12 +1691,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) @@ -1707,8 +1704,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; } @@ -1716,6 +1717,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 @@ -674,14 +674,23 @@ bool isNeeded() const override { return !entries.empty(); } void addSymbols(); template void addEntry(Symbol &sym); + size_t getNumEntries() const { 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 { @@ -1164,6 +1173,7 @@ PltSection *plt; PltSection *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 @@ -2443,14 +2443,22 @@ } } +static StringRef getPltName() { + if (config->emachine == EM_PPC || config->emachine == EM_PPC64) + return ".glink"; + + if ((config->emachine == EM_386 || config->emachine == EM_X86_64) && + (config->andFeatures & GNU_PROPERTY_X86_FEATURE_1_IBT)) + return ".plt.sec"; + + return ".plt"; +} + // 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_PPC || config->emachine == EM_PPC64) - ? ".glink" - : ".plt"), + : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 16, + getPltName()), headerSize(!isIplt || config->zRetpolineplt ? target->pltHeaderSize : 0), isIplt(isIplt) { // The PLT needs to be writable on SPARC as the dynamic linker will @@ -2509,6 +2517,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.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 @@ -44,6 +44,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 &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(false); add(in.plt); in.iplt = make(true); 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 @@ -428,6 +428,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,80 @@ +# 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: 0x00403298 30224000 00000000 00000000 10124000 + +# DISASM: Disassembly of section .text: +# DISASM: 004011f8 func1: +# DISASM-NEXT: 4011f8: calll 0x23 +# DISASM-NEXT: retl + +# DISASM: Disassembly of section .plt: +# DISASM: 00401200 .plt: +# DISASM-NEXT: 401200: pushl 0x40329c +# DISASM-NEXT: jmpl *0x4032a0 +# 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: 00401220 .plt.sec: +# DISASM-NEXT: 401220: endbr32 +# DISASM-NEXT: jmpl *0x4032a4 +# 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 + 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,79 @@ +# 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: 203420 50232000 00000000 00000000 00000000 +# GOTPLT-NEXT: 203430 00000000 00000000 30132000 00000000 + +# DISASM: Disassembly of section .text: +# DISASM: 0000000000201318 func1: +# DISASM-NEXT: 201318: callq 0x23 +# DISASM-NEXT: retq + +# DISASM: Disassembly of section .plt: +# DISASM: 0000000000201320 .plt: +# DISASM-NEXT: 201320: pushq 0x2102(%rip) +# DISASM-NEXT: jmpq *0x2104(%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: 0000000000201340 .plt.sec: +# DISASM-NEXT: 201340: endbr64 +# DISASM-NEXT: jmpq *0x20ee(%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 + ret