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 @@ -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/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -1689,12 +1689,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) diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -176,10 +176,6 @@ 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">; @@ -321,6 +317,9 @@ def relocatable: F<"relocatable">, HelpText<"Create relocatable object file">; +def require_cet: F<"require-cet">, + HelpText<"Force enable x86 Control-Flow Enforcement Technology">; + defm retain_symbols_file: Eq<"retain-symbols-file", "Retain only the symbols listed in the file">, MetaVarName<"">; 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 @@ -432,6 +432,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: not ld.lld -e func1 %t.o %t3.o -o %t --require-cet 2>&1 \ +# RUN: | FileCheck --check-prefix=ERROR %s +# ERROR: {{.*}}.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 -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: not ld.lld -e func1 %t.o %t3.o -o %t --require-cet 2>&1 \ +# RUN: | FileCheck --check-prefix=ERROR %s +# ERROR: {{.*}}.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 -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 0x20e2(%rip) +# DISASM-NEXT: jmpq *0x20e4(%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