diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2023-05-23 09:37:50 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2023-05-23 09:37:50 +0000 |
commit | db88e9ed6b9abd0b912f2bf2eed512cb82b313d6 (patch) | |
tree | 9221bdc4239def2982dac8e4316284070b3ae44e | |
parent | bce3295c82a586d492c1a7a45ef3cbb50cb05fec (diff) |
Add IBT support to the retpoline PLTs. Since we use retpoline PLTs by
default on OpenBSD this will give us IBT support by default. Fixes
indirect function calls for functions in shared libraries.
This doesn't fix retpoline+znow PLTs yet; a fix for that will follow.
ok miod@, guenther@
-rw-r--r-- | gnu/llvm/lld/ELF/Arch/X86_64.cpp | 504 |
1 files changed, 447 insertions, 57 deletions
diff --git a/gnu/llvm/lld/ELF/Arch/X86_64.cpp b/gnu/llvm/lld/ELF/Arch/X86_64.cpp index 74b72eb9129..271558c6599 100644 --- a/gnu/llvm/lld/ELF/Arch/X86_64.cpp +++ b/gnu/llvm/lld/ELF/Arch/X86_64.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "InputFiles.h" +#include "OutputSections.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" @@ -18,9 +19,8 @@ using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; - -namespace lld { -namespace elf { +using namespace lld; +using namespace lld::elf; namespace { class X86_64 : public TargetInfo { @@ -32,23 +32,49 @@ public: RelType getDynRel(RelType type) const override; void writeGotPltHeader(uint8_t *buf) const override; void writeGotPlt(uint8_t *buf, const Symbol &s) const override; + void writeIgotPlt(uint8_t *buf, const Symbol &s) const override; void writePltHeader(uint8_t *buf) const override; void writePlt(uint8_t *buf, const Symbol &sym, uint64_t pltEntryAddr) const override; - void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; - - RelExpr adjustRelaxExpr(RelType type, const uint8_t *data, - RelExpr expr) const override; - void relaxGot(uint8_t *loc, RelType type, uint64_t val) const override; - void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override; - void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override; - void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override; - void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override; + void relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; + int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override; + void applyJumpInstrMod(uint8_t *loc, JumpModType type, + unsigned size) const override; + + RelExpr adjustGotPcExpr(RelType type, int64_t addend, + const uint8_t *loc) const override; + void relaxGot(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; + void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; + void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; + void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; + void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end, uint8_t stOther) const override; + bool deleteFallThruJmpInsn(InputSection &is, InputFile *file, + InputSection *nextIS) const override; }; } // namespace +// This is vector of NOP instructions of sizes from 1 to 8 bytes. The +// appropriately sized instructions are used to fill the gaps between sections +// which are executed during fall through. +static const std::vector<std::vector<uint8_t>> nopInstructions = { + {0x90}, + {0x66, 0x90}, + {0x0f, 0x1f, 0x00}, + {0x0f, 0x1f, 0x40, 0x00}, + {0x0f, 0x1f, 0x44, 0x00, 0x00}, + {0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00}, + {0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00}, + {0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}}; + X86_64::X86_64() { copyRel = R_X86_64_COPY; gotRel = R_X86_64_GLOB_DAT; @@ -61,10 +87,12 @@ X86_64::X86_64() { tlsGotRel = R_X86_64_TPOFF64; tlsModuleIndexRel = R_X86_64_DTPMOD64; tlsOffsetRel = R_X86_64_DTPOFF64; + gotEntrySize = 8; pltHeaderSize = 16; pltEntrySize = 16; ipltEntrySize = 16; trapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3 + nopInstrs = nopInstructions; // Align to the large page size (known as a superpage or huge page). // FreeBSD automatically promotes large, superpage-aligned allocations. @@ -73,6 +101,216 @@ X86_64::X86_64() { int X86_64::getTlsGdRelaxSkip(RelType type) const { return 2; } +// Opcodes for the different X86_64 jmp instructions. +enum JmpInsnOpcode : uint32_t { + J_JMP_32, + J_JNE_32, + J_JE_32, + J_JG_32, + J_JGE_32, + J_JB_32, + J_JBE_32, + J_JL_32, + J_JLE_32, + J_JA_32, + J_JAE_32, + J_UNKNOWN, +}; + +// Given the first (optional) and second byte of the insn's opcode, this +// returns the corresponding enum value. +static JmpInsnOpcode getJmpInsnType(const uint8_t *first, + const uint8_t *second) { + if (*second == 0xe9) + return J_JMP_32; + + if (first == nullptr) + return J_UNKNOWN; + + if (*first == 0x0f) { + switch (*second) { + case 0x84: + return J_JE_32; + case 0x85: + return J_JNE_32; + case 0x8f: + return J_JG_32; + case 0x8d: + return J_JGE_32; + case 0x82: + return J_JB_32; + case 0x86: + return J_JBE_32; + case 0x8c: + return J_JL_32; + case 0x8e: + return J_JLE_32; + case 0x87: + return J_JA_32; + case 0x83: + return J_JAE_32; + } + } + return J_UNKNOWN; +} + +// Return the relocation index for input section IS with a specific Offset. +// Returns the maximum size of the vector if no such relocation is found. +static unsigned getRelocationWithOffset(const InputSection &is, + uint64_t offset) { + unsigned size = is.relocations.size(); + for (unsigned i = size - 1; i + 1 > 0; --i) { + if (is.relocations[i].offset == offset && is.relocations[i].expr != R_NONE) + return i; + } + return size; +} + +// Returns true if R corresponds to a relocation used for a jump instruction. +// TODO: Once special relocations for relaxable jump instructions are available, +// this should be modified to use those relocations. +static bool isRelocationForJmpInsn(Relocation &R) { + return R.type == R_X86_64_PLT32 || R.type == R_X86_64_PC32 || + R.type == R_X86_64_PC8; +} + +// Return true if Relocation R points to the first instruction in the +// next section. +// TODO: Delete this once psABI reserves a new relocation type for fall thru +// jumps. +static bool isFallThruRelocation(InputSection &is, InputFile *file, + InputSection *nextIS, Relocation &r) { + if (!isRelocationForJmpInsn(r)) + return false; + + uint64_t addrLoc = is.getOutputSection()->addr + is.outSecOff + r.offset; + uint64_t targetOffset = InputSectionBase::getRelocTargetVA( + file, r.type, r.addend, addrLoc, *r.sym, r.expr); + + // If this jmp is a fall thru, the target offset is the beginning of the + // next section. + uint64_t nextSectionOffset = + nextIS->getOutputSection()->addr + nextIS->outSecOff; + return (addrLoc + 4 + targetOffset) == nextSectionOffset; +} + +// Return the jmp instruction opcode that is the inverse of the given +// opcode. For example, JE inverted is JNE. +static JmpInsnOpcode invertJmpOpcode(const JmpInsnOpcode opcode) { + switch (opcode) { + case J_JE_32: + return J_JNE_32; + case J_JNE_32: + return J_JE_32; + case J_JG_32: + return J_JLE_32; + case J_JGE_32: + return J_JL_32; + case J_JB_32: + return J_JAE_32; + case J_JBE_32: + return J_JA_32; + case J_JL_32: + return J_JGE_32; + case J_JLE_32: + return J_JG_32; + case J_JA_32: + return J_JBE_32; + case J_JAE_32: + return J_JB_32; + default: + return J_UNKNOWN; + } +} + +// Deletes direct jump instruction in input sections that jumps to the +// following section as it is not required. If there are two consecutive jump +// instructions, it checks if they can be flipped and one can be deleted. +// For example: +// .section .text +// a.BB.foo: +// ... +// 10: jne aa.BB.foo +// 16: jmp bar +// aa.BB.foo: +// ... +// +// can be converted to: +// a.BB.foo: +// ... +// 10: je bar #jne flipped to je and the jmp is deleted. +// aa.BB.foo: +// ... +bool X86_64::deleteFallThruJmpInsn(InputSection &is, InputFile *file, + InputSection *nextIS) const { + const unsigned sizeOfDirectJmpInsn = 5; + + if (nextIS == nullptr) + return false; + + if (is.getSize() < sizeOfDirectJmpInsn) + return false; + + // If this jmp insn can be removed, it is the last insn and the + // relocation is 4 bytes before the end. + unsigned rIndex = getRelocationWithOffset(is, is.getSize() - 4); + if (rIndex == is.relocations.size()) + return false; + + Relocation &r = is.relocations[rIndex]; + + // Check if the relocation corresponds to a direct jmp. + const uint8_t *secContents = is.data().data(); + // If it is not a direct jmp instruction, there is nothing to do here. + if (*(secContents + r.offset - 1) != 0xe9) + return false; + + if (isFallThruRelocation(is, file, nextIS, r)) { + // This is a fall thru and can be deleted. + r.expr = R_NONE; + r.offset = 0; + is.drop_back(sizeOfDirectJmpInsn); + is.nopFiller = true; + return true; + } + + // Now, check if flip and delete is possible. + const unsigned sizeOfJmpCCInsn = 6; + // To flip, there must be atleast one JmpCC and one direct jmp. + if (is.getSize() < sizeOfDirectJmpInsn + sizeOfJmpCCInsn) + return 0; + + unsigned rbIndex = + getRelocationWithOffset(is, (is.getSize() - sizeOfDirectJmpInsn - 4)); + if (rbIndex == is.relocations.size()) + return 0; + + Relocation &rB = is.relocations[rbIndex]; + + const uint8_t *jmpInsnB = secContents + rB.offset - 1; + JmpInsnOpcode jmpOpcodeB = getJmpInsnType(jmpInsnB - 1, jmpInsnB); + if (jmpOpcodeB == J_UNKNOWN) + return false; + + if (!isFallThruRelocation(is, file, nextIS, rB)) + return false; + + // jmpCC jumps to the fall thru block, the branch can be flipped and the + // jmp can be deleted. + JmpInsnOpcode jInvert = invertJmpOpcode(jmpOpcodeB); + if (jInvert == J_UNKNOWN) + return false; + is.jumpInstrMods.push_back({jInvert, (rB.offset - 1), 4}); + // Move R's values to rB except the offset. + rB = {r.expr, r.type, rB.offset, r.addend, r.sym}; + // Cancel R + r.expr = R_NONE; + r.offset = 0; + is.drop_back(sizeOfDirectJmpInsn); + is.nopFiller = true; + return true; +} + RelExpr X86_64::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const { if (type == R_X86_64_GOTTPOFF) @@ -89,7 +327,7 @@ RelExpr X86_64::getRelExpr(RelType type, const Symbol &s, case R_X86_64_DTPOFF64: return R_DTPREL; case R_X86_64_TPOFF32: - return R_TLS; + return R_TPREL; case R_X86_64_TLSDESC_CALL: return R_TLSDESC_CALL; case R_X86_64_TLSLD: @@ -143,6 +381,12 @@ void X86_64::writeGotPlt(uint8_t *buf, const Symbol &s) const { write64le(buf, s.getPltVA() + 6); } +void X86_64::writeIgotPlt(uint8_t *buf, const Symbol &s) const { + // An x86 entry is the address of the ifunc resolver function (for -z rel). + if (config->writeAddends) + write64le(buf, s.getVA()); +} + void X86_64::writePltHeader(uint8_t *buf) const { const uint8_t pltData[] = { 0xff, 0x35, 0, 0, 0, 0, // pushq GOTPLT+8(%rip) @@ -177,8 +421,9 @@ RelType X86_64::getDynRel(RelType type) const { return R_X86_64_NONE; } -void X86_64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { - if (type == R_X86_64_TLSGD) { +void X86_64::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const { + if (rel.type == R_X86_64_TLSGD) { // Convert // .byte 0x66 // leaq x@tlsgd(%rip), %rdi @@ -201,7 +446,7 @@ void X86_64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { // lea x@tlsgd(%rip), %rax // call *(%rax) // to the following two instructions. - assert(type == R_X86_64_GOTPC32_TLSDESC); + assert(rel.type == R_X86_64_GOTPC32_TLSDESC); if (memcmp(loc - 3, "\x48\x8d\x05", 3)) { error(getErrorLocation(loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used " "in callq *x@tlsdesc(%rip), %rax"); @@ -217,8 +462,9 @@ void X86_64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const { } } -void X86_64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { - if (type == R_X86_64_TLSGD) { +void X86_64::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, + uint64_t val) const { + if (rel.type == R_X86_64_TLSGD) { // Convert // .byte 0x66 // leaq x@tlsgd(%rip), %rdi @@ -241,7 +487,7 @@ void X86_64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { // lea x@tlsgd(%rip), %rax // call *(%rax) // to the following two instructions. - assert(type == R_X86_64_GOTPC32_TLSDESC); + assert(rel.type == R_X86_64_GOTPC32_TLSDESC); if (memcmp(loc - 3, "\x48\x8d\x05", 3)) { error(getErrorLocation(loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used " "in callq *x@tlsdesc(%rip), %rax"); @@ -258,7 +504,8 @@ void X86_64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const { // In some conditions, R_X86_64_GOTTPOFF relocation can be optimized to // R_X86_64_TPOFF32 so that it does not use GOT. -void X86_64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { +void X86_64::relaxTlsIeToLe(uint8_t *loc, const Relocation &, + uint64_t val) const { uint8_t *inst = loc - 3; uint8_t reg = loc[-1] >> 3; uint8_t *regSlot = loc - 1; @@ -299,12 +546,13 @@ void X86_64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const { write32le(loc, val + 4); } -void X86_64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { - if (type == R_X86_64_DTPOFF64) { +void X86_64::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const { + if (rel.type == R_X86_64_DTPOFF64) { write64le(loc, val); return; } - if (type == R_X86_64_DTPOFF32) { + if (rel.type == R_X86_64_DTPOFF32) { write32le(loc, val); return; } @@ -347,26 +595,163 @@ void X86_64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const { "expected R_X86_64_PLT32 or R_X86_64_GOTPCRELX after R_X86_64_TLSLD"); } -void X86_64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { +// A JumpInstrMod at a specific offset indicates that the jump instruction +// opcode at that offset must be modified. This is specifically used to relax +// jump instructions with basic block sections. This function looks at the +// JumpMod and effects the change. +void X86_64::applyJumpInstrMod(uint8_t *loc, JumpModType type, + unsigned size) const { switch (type) { + case J_JMP_32: + if (size == 4) + *loc = 0xe9; + else + *loc = 0xeb; + break; + case J_JE_32: + if (size == 4) { + loc[-1] = 0x0f; + *loc = 0x84; + } else + *loc = 0x74; + break; + case J_JNE_32: + if (size == 4) { + loc[-1] = 0x0f; + *loc = 0x85; + } else + *loc = 0x75; + break; + case J_JG_32: + if (size == 4) { + loc[-1] = 0x0f; + *loc = 0x8f; + } else + *loc = 0x7f; + break; + case J_JGE_32: + if (size == 4) { + loc[-1] = 0x0f; + *loc = 0x8d; + } else + *loc = 0x7d; + break; + case J_JB_32: + if (size == 4) { + loc[-1] = 0x0f; + *loc = 0x82; + } else + *loc = 0x72; + break; + case J_JBE_32: + if (size == 4) { + loc[-1] = 0x0f; + *loc = 0x86; + } else + *loc = 0x76; + break; + case J_JL_32: + if (size == 4) { + loc[-1] = 0x0f; + *loc = 0x8c; + } else + *loc = 0x7c; + break; + case J_JLE_32: + if (size == 4) { + loc[-1] = 0x0f; + *loc = 0x8e; + } else + *loc = 0x7e; + break; + case J_JA_32: + if (size == 4) { + loc[-1] = 0x0f; + *loc = 0x87; + } else + *loc = 0x77; + break; + case J_JAE_32: + if (size == 4) { + loc[-1] = 0x0f; + *loc = 0x83; + } else + *loc = 0x73; + break; + case J_UNKNOWN: + llvm_unreachable("Unknown Jump Relocation"); + } +} + +int64_t X86_64::getImplicitAddend(const uint8_t *buf, RelType type) const { + switch (type) { + case R_X86_64_8: + case R_X86_64_PC8: + return SignExtend64<8>(*buf); + case R_X86_64_16: + case R_X86_64_PC16: + return SignExtend64<16>(read16le(buf)); + case R_X86_64_32: + case R_X86_64_32S: + case R_X86_64_TPOFF32: + case R_X86_64_GOT32: + case R_X86_64_GOTPC32: + case R_X86_64_GOTPC32_TLSDESC: + case R_X86_64_GOTPCREL: + case R_X86_64_GOTPCRELX: + case R_X86_64_REX_GOTPCRELX: + case R_X86_64_PC32: + case R_X86_64_GOTTPOFF: + case R_X86_64_PLT32: + case R_X86_64_TLSGD: + case R_X86_64_TLSLD: + case R_X86_64_DTPOFF32: + case R_X86_64_SIZE32: + return SignExtend64<32>(read32le(buf)); + case R_X86_64_64: + case R_X86_64_TPOFF64: + case R_X86_64_DTPOFF64: + case R_X86_64_DTPMOD64: + case R_X86_64_PC64: + case R_X86_64_SIZE64: + case R_X86_64_GLOB_DAT: + case R_X86_64_GOT64: + case R_X86_64_GOTOFF64: + case R_X86_64_GOTPC64: + case R_X86_64_IRELATIVE: + case R_X86_64_RELATIVE: + return read64le(buf); + case R_X86_64_JUMP_SLOT: + case R_X86_64_NONE: + // These relocations are defined as not having an implicit addend. + return 0; + default: + internalLinkerError(getErrorLocation(buf), + "cannot read addend for relocation " + toString(type)); + return 0; + } +} + +void X86_64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { + switch (rel.type) { case R_X86_64_8: - checkIntUInt(loc, val, 8, type); + checkIntUInt(loc, val, 8, rel); *loc = val; break; case R_X86_64_PC8: - checkInt(loc, val, 8, type); + checkInt(loc, val, 8, rel); *loc = val; break; case R_X86_64_16: - checkIntUInt(loc, val, 16, type); + checkIntUInt(loc, val, 16, rel); write16le(loc, val); break; case R_X86_64_PC16: - checkInt(loc, val, 16, type); + checkInt(loc, val, 16, rel); write16le(loc, val); break; case R_X86_64_32: - checkUInt(loc, val, 32, type); + checkUInt(loc, val, 32, rel); write32le(loc, val); break; case R_X86_64_32S: @@ -384,7 +769,7 @@ void X86_64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { case R_X86_64_TLSLD: case R_X86_64_DTPOFF32: case R_X86_64_SIZE32: - checkInt(loc, val, 32, type); + checkInt(loc, val, 32, rel); write32le(loc, val); break; case R_X86_64_64: @@ -401,12 +786,17 @@ void X86_64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { } } -RelExpr X86_64::adjustRelaxExpr(RelType type, const uint8_t *data, - RelExpr relExpr) const { - if (type != R_X86_64_GOTPCRELX && type != R_X86_64_REX_GOTPCRELX) - return relExpr; - const uint8_t op = data[-2]; - const uint8_t modRm = data[-1]; +RelExpr X86_64::adjustGotPcExpr(RelType type, int64_t addend, + const uint8_t *loc) const { + // Only R_X86_64_[REX_]GOTPCRELX can be relaxed. GNU as may emit GOTPCRELX + // with addend != -4. Such an instruction does not load the full GOT entry, so + // we cannot relax the relocation. E.g. movl x@GOTPCREL+4(%rip), %rax + // (addend=0) loads the high 32 bits of the GOT entry. + if ((type != R_X86_64_GOTPCRELX && type != R_X86_64_REX_GOTPCRELX) || + addend != -4) + return R_GOT_PC; + const uint8_t op = loc[-2]; + const uint8_t modRm = loc[-1]; // FIXME: When PIC is disabled and foo is defined locally in the // lower 32 bit address space, memory operand in mov can be converted into @@ -419,12 +809,13 @@ RelExpr X86_64::adjustRelaxExpr(RelType type, const uint8_t *data, if (op == 0xff && (modRm == 0x15 || modRm == 0x25)) return R_RELAX_GOT_PC; + // We don't support test/binop instructions without a REX prefix. + if (type == R_X86_64_GOTPCRELX) + return R_GOT_PC; + // Relaxation of test, adc, add, and, cmp, or, sbb, sub, xor. // If PIC then no relaxation is available. - // We also don't relax test/binop instructions without REX byte, - // they are 32bit operations and not common to have. - assert(type == R_X86_64_REX_GOTPCRELX); - return config->isPic ? relExpr : R_RELAX_GOT_PC_NOPIC; + return config->isPic ? R_GOT_PC : R_RELAX_GOT_PC_NOPIC; } // A subset of relaxations can only be applied for no-PIC. This method @@ -495,7 +886,8 @@ static void relaxGotNoPic(uint8_t *loc, uint64_t val, uint8_t op, write32le(loc, val); } -void X86_64::relaxGot(uint8_t *loc, RelType type, uint64_t val) const { +void X86_64::relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) const { + checkInt(loc, val, 32, rel); const uint8_t op = loc[-2]; const uint8_t modRm = loc[-1]; @@ -658,7 +1050,7 @@ Retpoline::Retpoline() { } void Retpoline::writeGotPlt(uint8_t *buf, const Symbol &s) const { - write64le(buf, s.getPltVA() + 17); + write64le(buf, s.getPltVA() + 21); } void Retpoline::writePltHeader(uint8_t *buf) const { @@ -686,22 +1078,23 @@ void Retpoline::writePltHeader(uint8_t *buf) const { void Retpoline::writePlt(uint8_t *buf, const Symbol &sym, uint64_t pltEntryAddr) const { const uint8_t insn[] = { - 0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // 0: mov foo@GOTPLT(%rip), %r11 - 0xe8, 0, 0, 0, 0, // 7: callq plt+0x20 - 0xe9, 0, 0, 0, 0, // c: jmp plt+0x12 - 0x68, 0, 0, 0, 0, // 11: pushq <relocation index> - 0xe9, 0, 0, 0, 0, // 16: jmp plt+0 - 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1b: int3; padding + 0xf3, 0x0f, 0x1e, 0xfa, // 0: endbr64 + 0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // 4: mov foo@GOTPLT(%rip), %r11 + 0xe8, 0, 0, 0, 0, // b: callq plt+0x20 + 0xe9, 0, 0, 0, 0, // 10: jmp plt+0x12 + 0x68, 0, 0, 0, 0, // 15: pushq <relocation index> + 0xe9, 0, 0, 0, 0, // 1a: jmp plt+0 + 0xcc, // 1f: int3; padding }; memcpy(buf, insn, sizeof(insn)); uint64_t off = pltEntryAddr - in.plt->getVA(); - write32le(buf + 3, sym.getGotPltVA() - pltEntryAddr - 7); - write32le(buf + 8, -off - 12 + 32); - write32le(buf + 13, -off - 17 + 18); - write32le(buf + 18, sym.pltIndex); - write32le(buf + 23, -off - 27); + write32le(buf + 7, sym.getGotPltVA() - pltEntryAddr - 11); + write32le(buf + 12, -off - 16 + 32); + write32le(buf + 17, -off - 21 + 18); + write32le(buf + 22, sym.pltIndex); + write32le(buf + 27, -off - 31); } RetpolineZNow::RetpolineZNow() { @@ -758,7 +1151,4 @@ static TargetInfo *getTargetInfo() { return &t; } -TargetInfo *getX86_64TargetInfo() { return getTargetInfo(); } - -} // namespace elf -} // namespace lld +TargetInfo *elf::getX86_64TargetInfo() { return getTargetInfo(); } |