summaryrefslogtreecommitdiff
path: root/ELF/Arch/X86.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ELF/Arch/X86.cpp')
-rw-r--r--ELF/Arch/X86.cpp137
1 files changed, 89 insertions, 48 deletions
diff --git a/ELF/Arch/X86.cpp b/ELF/Arch/X86.cpp
index a1e9bcaf1b129..fc848917d4e95 100644
--- a/ELF/Arch/X86.cpp
+++ b/ELF/Arch/X86.cpp
@@ -7,11 +7,11 @@
//
//===----------------------------------------------------------------------===//
-#include "Error.h"
#include "InputFiles.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
+#include "lld/Common/ErrorHandler.h"
#include "llvm/Support/Endian.h"
using namespace llvm;
@@ -24,24 +24,24 @@ namespace {
class X86 final : public TargetInfo {
public:
X86();
- RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
+ RelExpr getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const override;
- int64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override;
+ int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const override;
void writeGotPltHeader(uint8_t *Buf) const override;
- uint32_t getDynRel(uint32_t Type) const override;
- void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
- void writeIgotPlt(uint8_t *Buf, const SymbolBody &S) 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, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
int32_t Index, unsigned RelOff) const override;
- void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
+ void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
- RelExpr adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
+ RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data,
RelExpr Expr) const override;
- void relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
- void relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
- void relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
- void relaxTlsLdToLe(uint8_t *Loc, uint32_t 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;
};
} // namespace
@@ -63,7 +63,9 @@ X86::X86() {
TrapInstr = 0xcccccccc; // 0xcc = INT3
}
-RelExpr X86::getRelExpr(uint32_t Type, const SymbolBody &S,
+static bool hasBaseReg(uint8_t ModRM) { return (ModRM & 0xc7) != 0x5; }
+
+RelExpr X86::getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const {
switch (Type) {
case R_386_8:
@@ -87,24 +89,42 @@ RelExpr X86::getRelExpr(uint32_t Type, const SymbolBody &S,
return R_GOT;
case R_386_GOT32:
case R_386_GOT32X:
- // These relocations can be calculated in two different ways.
- // Usual calculation is G + A - GOT what means an offset in GOT table
- // (R_GOT_FROM_END). When instruction pointed by relocation has no base
- // register, then relocations can be used when PIC code is disabled. In that
- // case calculation is G + A, it resolves to an address of entry in GOT
- // (R_GOT) and not an offset.
+ // These relocations are arguably mis-designed because their calculations
+ // depend on the instructions they are applied to. This is bad because we
+ // usually don't care about whether the target section contains valid
+ // machine instructions or not. But this is part of the documented ABI, so
+ // we had to implement as the standard requires.
//
- // To check that instruction has no base register we scan ModR/M byte.
- // See "Table 2-2. 32-Bit Addressing Forms with the ModR/M Byte"
- // (http://www.intel.com/content/dam/www/public/us/en/documents/manuals/
- // 64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf)
- if ((Loc[-1] & 0xc7) != 0x5)
- return R_GOT_FROM_END;
- if (Config->Pic)
- error(toString(S.File) + ": relocation " + toString(Type) + " against '" +
- S.getName() +
- "' without base register can not be used when PIC enabled");
- return R_GOT;
+ // x86 does not support PC-relative data access. Therefore, in order to
+ // access GOT contents, a GOT address needs to be known at link-time
+ // (which means non-PIC) or compilers have to emit code to get a GOT
+ // address at runtime (which means code is position-independent but
+ // compilers need to emit extra code for each GOT access.) This decision
+ // is made at compile-time. In the latter case, compilers emit code to
+ // load an GOT address to a register, which is usually %ebx.
+ //
+ // So, there are two ways to refer to symbol foo's GOT entry: foo@GOT or
+ // foo@GOT(%reg).
+ //
+ // foo@GOT is not usable in PIC. If we are creating a PIC output and if we
+ // find such relocation, we should report an error. foo@GOT is resolved to
+ // an *absolute* address of foo's GOT entry, because both GOT address and
+ // foo's offset are known. In other words, it's G + A.
+ //
+ // foo@GOT(%reg) needs to be resolved to a *relative* offset from a GOT to
+ // foo's GOT entry in the table, because GOT address is not known but foo's
+ // offset in the table is known. It's G + A - GOT.
+ //
+ // It's unfortunate that compilers emit the same relocation for these
+ // different use cases. In order to distinguish them, we have to read a
+ // machine instruction.
+ //
+ // The following code implements it. We assume that Loc[0] is the first
+ // byte of a displacement or an immediate field of a valid machine
+ // instruction. That means a ModRM byte is at Loc[-1]. By taking a look at
+ // the byte, we can determine whether the instruction is register-relative
+ // (i.e. it was generated for foo@GOT(%reg)) or absolute (i.e. foo@GOT).
+ return hasBaseReg(Loc[-1]) ? R_GOT_FROM_END : R_GOT;
case R_386_TLS_GOTIE:
return R_GOT_FROM_END;
case R_386_GOTOFF:
@@ -116,12 +136,11 @@ RelExpr X86::getRelExpr(uint32_t Type, const SymbolBody &S,
case R_386_NONE:
return R_NONE;
default:
- error(toString(S.File) + ": unknown relocation type: " + toString(Type));
- return R_HINT;
+ return R_INVALID;
}
}
-RelExpr X86::adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
+RelExpr X86::adjustRelaxExpr(RelType Type, const uint8_t *Data,
RelExpr Expr) const {
switch (Expr) {
default:
@@ -137,18 +156,18 @@ void X86::writeGotPltHeader(uint8_t *Buf) const {
write32le(Buf, InX::Dynamic->getVA());
}
-void X86::writeGotPlt(uint8_t *Buf, const SymbolBody &S) const {
+void X86::writeGotPlt(uint8_t *Buf, const Symbol &S) const {
// Entries in .got.plt initially points back to the corresponding
// PLT entries with a fixed offset to skip the first instruction.
write32le(Buf, S.getPltVA() + 6);
}
-void X86::writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const {
+void X86::writeIgotPlt(uint8_t *Buf, const Symbol &S) const {
// An x86 entry is the address of the ifunc resolver function.
write32le(Buf, S.getVA());
}
-uint32_t X86::getDynRel(uint32_t Type) const {
+RelType X86::getDynRel(RelType Type) const {
if (Type == R_386_TLS_LE)
return R_386_TLS_TPOFF;
if (Type == R_386_TLS_LE_32)
@@ -208,10 +227,8 @@ void X86::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
write32le(Buf + 12, -Index * PltEntrySize - PltHeaderSize - 16);
}
-int64_t X86::getImplicitAddend(const uint8_t *Buf, uint32_t Type) const {
+int64_t X86::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
switch (Type) {
- default:
- return 0;
case R_386_8:
case R_386_PC8:
return SignExtend64<8>(*Buf);
@@ -228,15 +245,17 @@ int64_t X86::getImplicitAddend(const uint8_t *Buf, uint32_t Type) const {
case R_386_TLS_LDO_32:
case R_386_TLS_LE:
return SignExtend64<32>(read32le(Buf));
+ default:
+ return 0;
}
}
-void X86::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
- // R_386_{PC,}{8,16} are not part of the i386 psABI, but they are
- // being used for some 16-bit programs such as boot loaders, so
- // we want to support them.
+void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
switch (Type) {
case R_386_8:
+ // R_386_{PC,}{8,16} are not part of the i386 psABI, but they are
+ // being used for some 16-bit programs such as boot loaders, so
+ // we want to support them.
checkUInt<8>(Loc, Val, Type);
*Loc = Val;
break;
@@ -262,13 +281,35 @@ void X86::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
checkInt<17>(Loc, Val, Type);
write16le(Loc, Val);
break;
- default:
+ 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_PC32:
+ case R_386_PLT32:
+ case R_386_RELATIVE:
+ 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<32>(Loc, Val, Type);
write32le(Loc, Val);
+ break;
+ default:
+ error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
}
}
-void X86::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
+void X86::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
// Convert
// leal x@tlsgd(, %ebx, 1),
// call __tls_get_addr@plt
@@ -283,7 +324,7 @@ void X86::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
write32le(Loc + 5, Val);
}
-void X86::relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
+void X86::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const {
// Convert
// leal x@tlsgd(, %ebx, 1),
// call __tls_get_addr@plt
@@ -300,7 +341,7 @@ void X86::relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
// In some conditions, relocations can be optimized to avoid using GOT.
// This function does that for Initial Exec to Local Exec case.
-void X86::relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
+void X86::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
// Ulrich's document section 6.2 says that @gotntpoff can
// be used with MOVL or ADDL instructions.
// @indntpoff is similar to @gotntpoff, but for use in
@@ -337,7 +378,7 @@ void X86::relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
write32le(Loc, Val);
}
-void X86::relaxTlsLdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
+void X86::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
if (Type == R_386_TLS_LDO_32) {
write32le(Loc, Val);
return;