#include "OutputSections.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
#include "lld/Common/ErrorHandler.h"
#include "llvm/Support/Endian.h"
using namespace llvm;
using namespace llvm::support::endian;
using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
namespace {
class X86 : public TargetInfo {
public:
X86();
int getTlsGdRelaxSkip(RelType type) const override;
RelExpr getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const override;
int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
void writeGotPltHeader(uint8_t *buf) const override;
RelType getDynRel(RelType type) 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 relocate(uint8_t *loc, const Relocation &rel,
uint64_t val) const override;
RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override;
void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override;
};
}
X86::X86() {
copyRel = R_386_COPY;
gotRel = R_386_GLOB_DAT;
pltRel = R_386_JUMP_SLOT;
iRelativeRel = R_386_IRELATIVE;
relativeRel = R_386_RELATIVE;
symbolicRel = R_386_32;
tlsDescRel = R_386_TLS_DESC;
tlsGotRel = R_386_TLS_TPOFF;
tlsModuleIndexRel = R_386_TLS_DTPMOD32;
tlsOffsetRel = R_386_TLS_DTPOFF32;
gotBaseSymInGotPlt = true;
pltHeaderSize = 16;
pltEntrySize = 16;
ipltEntrySize = 16;
trapInstr = {0xcc, 0xcc, 0xcc, 0xcc};
defaultImageBase = 0x400000;
}
int X86::getTlsGdRelaxSkip(RelType type) const {
return type == R_386_TLS_GOTDESC || type == R_386_TLS_DESC_CALL ? 1 : 2;
}
RelExpr X86::getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const {
switch (type) {
case R_386_8:
case R_386_16:
case R_386_32:
return R_ABS;
case R_386_TLS_LDO_32:
return R_DTPREL;
case R_386_TLS_GD:
return R_TLSGD_GOTPLT;
case R_386_TLS_LDM:
return R_TLSLD_GOTPLT;
case R_386_PLT32:
return R_PLT_PC;
case R_386_PC8:
case R_386_PC16:
case R_386_PC32:
return R_PC;
case R_386_GOTPC:
return R_GOTPLTONLY_PC;
case R_386_TLS_IE:
return R_GOT;
case R_386_GOT32:
case R_386_GOT32X:
return (loc[-1] & 0xc7) == 0x5 ? R_GOT : R_GOTPLT;
case R_386_TLS_GOTDESC:
return R_TLSDESC_GOTPLT;
case R_386_TLS_DESC_CALL:
return R_TLSDESC_CALL;
case R_386_TLS_GOTIE:
return R_GOTPLT;
case R_386_GOTOFF:
return R_GOTPLTREL;
case R_386_TLS_LE:
return R_TPREL;
case R_386_TLS_LE_32:
return R_TPREL_NEG;
case R_386_NONE:
return R_NONE;
default:
error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
") against symbol " + toString(s));
return R_NONE;
}
}
RelExpr X86::adjustTlsExpr(RelType type, RelExpr expr) const {
switch (expr) {
default:
return expr;
case R_RELAX_TLS_GD_TO_IE:
return R_RELAX_TLS_GD_TO_IE_GOTPLT;
case R_RELAX_TLS_GD_TO_LE:
return type == R_386_TLS_GD ? R_RELAX_TLS_GD_TO_LE_NEG
: R_RELAX_TLS_GD_TO_LE;
}
}
void X86::writeGotPltHeader(uint8_t *buf) const {
write32le(buf, mainPart->dynamic->getVA());
}
void X86::writeGotPlt(uint8_t *buf, const Symbol &s) const {
write32le(buf, s.getPltVA() + 6);
}
void X86::writeIgotPlt(uint8_t *buf, const Symbol &s) const {
write32le(buf, s.getVA());
}
RelType X86::getDynRel(RelType type) const {
if (type == R_386_TLS_LE)
return R_386_TLS_TPOFF;
if (type == R_386_TLS_LE_32)
return R_386_TLS_TPOFF32;
return type;
}
void X86::writePltHeader(uint8_t *buf) const {
if (config->isPic) {
const uint8_t v[] = {
0xff, 0xb3, 0x04, 0x00, 0x00, 0x00,
0xff, 0xa3, 0x08, 0x00, 0x00, 0x00,
0x90, 0x90, 0x90, 0x90
};
memcpy(buf, v, sizeof(v));
return;
}
const uint8_t pltData[] = {
0xff, 0x35, 0, 0, 0, 0,
0xff, 0x25, 0, 0, 0, 0,
0x90, 0x90, 0x90, 0x90,
};
memcpy(buf, pltData, sizeof(pltData));
uint32_t gotPlt = in.gotPlt->getVA();
write32le(buf + 2, gotPlt + 4);
write32le(buf + 8, gotPlt + 8);
}
void X86::writePlt(uint8_t *buf, const Symbol &sym,
uint64_t pltEntryAddr) const {
unsigned relOff = in.relaPlt->entsize * sym.getPltIdx();
if (config->isPic) {
const uint8_t inst[] = {
0xff, 0xa3, 0, 0, 0, 0,
0x68, 0, 0, 0, 0,
0xe9, 0, 0, 0, 0,
};
memcpy(buf, inst, sizeof(inst));
write32le(buf + 2, sym.getGotPltVA() - in.gotPlt->getVA());
} else {
const uint8_t inst[] = {
0xff, 0x25, 0, 0, 0, 0,
0x68, 0, 0, 0, 0,
0xe9, 0, 0, 0, 0,
};
memcpy(buf, inst, sizeof(inst));
write32le(buf + 2, sym.getGotPltVA());
}
write32le(buf + 7, relOff);
write32le(buf + 12, in.plt->getVA() - pltEntryAddr - 16);
}
int64_t X86::getImplicitAddend(const uint8_t *buf, RelType type) const {
switch (type) {
case R_386_8:
case R_386_PC8:
return SignExtend64<8>(*buf);
case R_386_16:
case R_386_PC16:
return SignExtend64<16>(read16le(buf));
case R_386_32:
case R_386_GLOB_DAT:
case R_386_GOT32:
case R_386_GOT32X:
case R_386_GOTOFF:
case R_386_GOTPC:
case R_386_IRELATIVE:
case R_386_PC32:
case R_386_PLT32:
case R_386_RELATIVE:
case R_386_TLS_GOTDESC:
case R_386_TLS_DESC_CALL:
case R_386_TLS_DTPMOD32:
case R_386_TLS_DTPOFF32:
case R_386_TLS_LDO_32:
case R_386_TLS_LDM:
case R_386_TLS_IE:
case R_386_TLS_IE_32:
case R_386_TLS_LE:
case R_386_TLS_LE_32:
case R_386_TLS_GD:
case R_386_TLS_GD_32:
case R_386_TLS_GOTIE:
case R_386_TLS_TPOFF:
case R_386_TLS_TPOFF32:
return SignExtend64<32>(read32le(buf));
case R_386_TLS_DESC:
return SignExtend64<32>(read32le(buf + 4));
case R_386_NONE:
case R_386_JUMP_SLOT:
return 0;
default:
internalLinkerError(getErrorLocation(buf),
"cannot read addend for relocation " + toString(type));
return 0;
}
}
void X86::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
switch (rel.type) {
case R_386_8:
checkIntUInt(loc, val, 8, rel);
*loc = val;
break;
case R_386_PC8:
checkInt(loc, val, 8, rel);
*loc = val;
break;
case R_386_16:
checkIntUInt(loc, val, 16, rel);
write16le(loc, val);
break;
case R_386_PC16:
checkInt(loc, val, 17, rel);
write16le(loc, val);
break;
case R_386_32:
case R_386_GOT32:
case R_386_GOT32X:
case R_386_GOTOFF:
case R_386_GOTPC:
case R_386_PC32:
case R_386_PLT32:
case R_386_RELATIVE:
case R_386_TLS_GOTDESC:
case R_386_TLS_DESC_CALL:
case R_386_TLS_DTPMOD32:
case R_386_TLS_DTPOFF32:
case R_386_TLS_GD:
case R_386_TLS_GOTIE:
case R_386_TLS_IE:
case R_386_TLS_LDM:
case R_386_TLS_LDO_32:
case R_386_TLS_LE:
case R_386_TLS_LE_32:
case R_386_TLS_TPOFF:
case R_386_TLS_TPOFF32:
checkInt(loc, val, 32, rel);
write32le(loc, val);
break;
case R_386_TLS_DESC:
write32le(loc + 4, val);
break;
default:
llvm_unreachable("unknown relocation");
}
}
static void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) {
if (rel.type == R_386_TLS_GD) {
const uint8_t inst[] = {
0x65, 0xa1, 0x00, 0x00, 0x00, 0x00,
0x81, 0xe8, 0, 0, 0, 0,
};
uint8_t *w = loc[-2] == 0x04 ? loc - 3 : loc - 2;
memcpy(w, inst, sizeof(inst));
write32le(w + 8, val);
} else if (rel.type == R_386_TLS_GOTDESC) {
if (memcmp(loc - 2, "\x8d\x83", 2)) {
error(getErrorLocation(loc - 2) +
"R_386_TLS_GOTDESC must be used in leal x@tlsdesc(%ebx), %eax");
return;
}
loc[-1] = 0x05;
write32le(loc, val);
} else {
assert(rel.type == R_386_TLS_DESC_CALL);
loc[0] = 0x66;
loc[1] = 0x90;
}
}
static void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) {
if (rel.type == R_386_TLS_GD) {
const uint8_t inst[] = {
0x65, 0xa1, 0x00, 0x00, 0x00, 0x00,
0x03, 0x83, 0, 0, 0, 0,
};
uint8_t *w = loc[-2] == 0x04 ? loc - 3 : loc - 2;
memcpy(w, inst, sizeof(inst));
write32le(w + 8, val);
} else if (rel.type == R_386_TLS_GOTDESC) {
if (memcmp(loc - 2, "\x8d\x83", 2)) {
error(getErrorLocation(loc - 2) +
"R_386_TLS_GOTDESC must be used in leal x@tlsdesc(%ebx), %eax");
return;
}
loc[-2] = 0x8b;
write32le(loc, val);
} else {
assert(rel.type == R_386_TLS_DESC_CALL);
loc[0] = 0x66;
loc[1] = 0x90;
}
}
static void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, uint64_t val) {
uint8_t reg = (loc[-1] >> 3) & 7;
if (rel.type == R_386_TLS_IE) {
if (loc[-1] == 0xa1) {
loc[-1] = 0xb8;
} else if (loc[-2] == 0x8b) {
loc[-2] = 0xc7;
loc[-1] = 0xc0 | reg;
} else {
loc[-2] = 0x81;
loc[-1] = 0xc0 | reg;
}
} else {
assert(rel.type == R_386_TLS_GOTIE);
if (loc[-2] == 0x8b) {
loc[-2] = 0xc7;
loc[-1] = 0xc0 | reg;
} else {
loc[-2] = 0x8d;
loc[-1] = 0x80 | (reg << 3) | reg;
}
}
write32le(loc, val);
}
static void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) {
if (rel.type == R_386_TLS_LDO_32) {
write32le(loc, val);
return;
}
if (loc[4] == 0xe8) {
const uint8_t inst[] = {
0x65, 0xa1, 0x00, 0x00, 0x00, 0x00,
0x90,
0x8d, 0x74, 0x26, 0x00,
};
memcpy(loc - 2, inst, sizeof(inst));
return;
}
const uint8_t inst[] = {
0x65, 0xa1, 0x00, 0x00, 0x00, 0x00,
0x8d, 0xb6, 0x00, 0x00, 0x00, 0x00,
};
memcpy(loc - 2, inst, sizeof(inst));
}
void X86::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const {
uint64_t secAddr = sec.getOutputSection()->addr;
if (auto *s = dyn_cast<InputSection>(&sec))
secAddr += s->outSecOff;
for (const Relocation &rel : sec.relocs()) {
uint8_t *loc = buf + rel.offset;
const uint64_t val = SignExtend64(
sec.getRelocTargetVA(sec.file, rel.type, rel.addend,
secAddr + rel.offset, *rel.sym, rel.expr),
32);
switch (rel.expr) {
case R_RELAX_TLS_GD_TO_IE_GOTPLT:
relaxTlsGdToIe(loc, rel, val);
continue;
case R_RELAX_TLS_GD_TO_LE:
case R_RELAX_TLS_GD_TO_LE_NEG:
relaxTlsGdToLe(loc, rel, val);
continue;
case R_RELAX_TLS_LD_TO_LE:
relaxTlsLdToLe(loc, rel, val);
break;
case R_RELAX_TLS_IE_TO_LE:
relaxTlsIeToLe(loc, rel, val);
continue;
default:
relocate(loc, rel, val);
break;
}
}
}
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;
};
}
IntelIBT::IntelIBT() { pltHeaderSize = 0; }
void IntelIBT::writeGotPlt(uint8_t *buf, const Symbol &s) const {
uint64_t va =
in.ibtPlt->getVA() + IBTPltHeaderSize + s.getPltIdx() * pltEntrySize;
write32le(buf, va);
}
void IntelIBT::writePlt(uint8_t *buf, const Symbol &sym,
uint64_t ) const {
if (config->isPic) {
const uint8_t inst[] = {
0xf3, 0x0f, 0x1e, 0xfb,
0xff, 0xa3, 0, 0, 0, 0,
0x66, 0x0f, 0x1f, 0x44, 0, 0,
};
memcpy(buf, inst, sizeof(inst));
write32le(buf + 6, sym.getGotPltVA() - in.gotPlt->getVA());
return;
}
const uint8_t inst[] = {
0xf3, 0x0f, 0x1e, 0xfb,
0xff, 0x25, 0, 0, 0, 0,
0x66, 0x0f, 0x1f, 0x44, 0, 0,
};
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,
0x68, 0, 0, 0, 0,
0xe9, 0, 0, 0, 0,
0x66, 0x90,
};
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:
RetpolinePic();
void writeGotPlt(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;
};
class RetpolineNoPic : public X86 {
public:
RetpolineNoPic();
void writeGotPlt(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;
};
}
RetpolinePic::RetpolinePic() {
pltHeaderSize = 48;
pltEntrySize = 32;
ipltEntrySize = 32;
}
void RetpolinePic::writeGotPlt(uint8_t *buf, const Symbol &s) const {
write32le(buf, s.getPltVA() + 17);
}
void RetpolinePic::writePltHeader(uint8_t *buf) const {
const uint8_t insn[] = {
0xff, 0xb3, 4, 0, 0, 0,
0x50,
0x8b, 0x83, 8, 0, 0, 0,
0xe8, 0x0e, 0x00, 0x00, 0x00,
0xf3, 0x90,
0x0f, 0xae, 0xe8,
0xeb, 0xf9,
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
0x89, 0x0c, 0x24,
0x8b, 0x4c, 0x24, 0x04,
0x89, 0x44, 0x24, 0x04,
0x89, 0xc8,
0x59,
0xc3,
0xcc,
};
memcpy(buf, insn, sizeof(insn));
}
void RetpolinePic::writePlt(uint8_t *buf, const Symbol &sym,
uint64_t pltEntryAddr) const {
unsigned relOff = in.relaPlt->entsize * sym.getPltIdx();
const uint8_t insn[] = {
0x50,
0x8b, 0x83, 0, 0, 0, 0,
0xe8, 0, 0, 0, 0,
0xe9, 0, 0, 0, 0,
0x68, 0, 0, 0, 0,
0xe9, 0, 0, 0, 0,
0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
};
memcpy(buf, insn, sizeof(insn));
uint32_t ebx = in.gotPlt->getVA();
unsigned off = pltEntryAddr - in.plt->getVA();
write32le(buf + 3, sym.getGotPltVA() - ebx);
write32le(buf + 8, -off - 12 + 32);
write32le(buf + 13, -off - 17 + 18);
write32le(buf + 18, relOff);
write32le(buf + 23, -off - 27);
}
RetpolineNoPic::RetpolineNoPic() {
pltHeaderSize = 48;
pltEntrySize = 32;
ipltEntrySize = 32;
}
void RetpolineNoPic::writeGotPlt(uint8_t *buf, const Symbol &s) const {
write32le(buf, s.getPltVA() + 16);
}
void RetpolineNoPic::writePltHeader(uint8_t *buf) const {
const uint8_t insn[] = {
0xff, 0x35, 0, 0, 0, 0,
0x50,
0xa1, 0, 0, 0, 0,
0xe8, 0x0f, 0x00, 0x00, 0x00,
0xf3, 0x90,
0x0f, 0xae, 0xe8,
0xeb, 0xf9,
0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
0xcc, 0xcc, 0xcc,
0x89, 0x0c, 0x24,
0x8b, 0x4c, 0x24, 0x04,
0x89, 0x44, 0x24, 0x04,
0x89, 0xc8,
0x59,
0xc3,
0xcc,
};
memcpy(buf, insn, sizeof(insn));
uint32_t gotPlt = in.gotPlt->getVA();
write32le(buf + 2, gotPlt + 4);
write32le(buf + 8, gotPlt + 8);
}
void RetpolineNoPic::writePlt(uint8_t *buf, const Symbol &sym,
uint64_t pltEntryAddr) const {
unsigned relOff = in.relaPlt->entsize * sym.getPltIdx();
const uint8_t insn[] = {
0x50,
0xa1, 0, 0, 0, 0,
0xe8, 0, 0, 0, 0,
0xe9, 0, 0, 0, 0,
0x68, 0, 0, 0, 0,
0xe9, 0, 0, 0, 0,
0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
0xcc,
};
memcpy(buf, insn, sizeof(insn));
unsigned off = pltEntryAddr - in.plt->getVA();
write32le(buf + 2, sym.getGotPltVA());
write32le(buf + 7, -off - 11 + 32);
write32le(buf + 12, -off - 16 + 17);
write32le(buf + 17, relOff);
write32le(buf + 22, -off - 26);
}
TargetInfo *elf::getX86TargetInfo() {
if (config->zRetpolineplt) {
if (config->isPic) {
static RetpolinePic t;
return &t;
}
static RetpolineNoPic t;
return &t;
}
if (config->andFeatures & GNU_PROPERTY_X86_FEATURE_1_IBT) {
static IntelIBT t;
return &t;
}
static X86 t;
return &t;
}