summaryrefslogtreecommitdiffstats
path: root/lld/ELF/Arch/PPC.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lld/ELF/Arch/PPC.cpp')
-rw-r--r--lld/ELF/Arch/PPC.cpp186
1 files changed, 185 insertions, 1 deletions
diff --git a/lld/ELF/Arch/PPC.cpp b/lld/ELF/Arch/PPC.cpp
index ee15575bfd5..8f68d12abe8 100644
--- a/lld/ELF/Arch/PPC.cpp
+++ b/lld/ELF/Arch/PPC.cpp
@@ -39,12 +39,27 @@ public:
RelExpr getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) 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;
+ int getTlsGdRelaxSkip(RelType Type) 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 relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+ void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
};
} // namespace
static uint16_t lo(uint32_t V) { return V; }
static uint16_t ha(uint32_t V) { return (V + 0x8000) >> 16; }
+static uint32_t readFromHalf16(const uint8_t *Loc) {
+ return read32(Loc - (Config->EKind == ELF32BEKind ? 2 : 0));
+}
+
+static void writeFromHalf16(uint8_t *Loc, uint32_t Insn) {
+ write32(Loc - (Config->EKind == ELF32BEKind ? 2 : 0), Insn);
+}
+
void elf::writePPC32GlinkSection(uint8_t *Buf, size_t NumEntries) {
// On PPC Secure PLT ABI, bl foo@plt jumps to a call stub, which loads an
// absolute address from a specific .plt slot (usually called .got.plt on
@@ -128,6 +143,10 @@ PPC::PPC() {
NeedsThunks = true;
+ TlsModuleIndexRel = R_PPC_DTPMOD32;
+ TlsOffsetRel = R_PPC_DTPREL32;
+ TlsGotRel = R_PPC_TPREL32;
+
DefaultMaxPageSize = 65536;
DefaultImageBase = 0x10000000;
@@ -169,6 +188,12 @@ bool PPC::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
RelExpr PPC::getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const {
switch (Type) {
+ case R_PPC_DTPREL16:
+ case R_PPC_DTPREL16_HA:
+ case R_PPC_DTPREL16_HI:
+ case R_PPC_DTPREL16_LO:
+ case R_PPC_DTPREL32:
+ return R_DTPREL;
case R_PPC_REL14:
case R_PPC_REL32:
case R_PPC_LOCAL24PC:
@@ -182,28 +207,84 @@ RelExpr PPC::getRelExpr(RelType Type, const Symbol &S,
return R_PLT_PC;
case R_PPC_PLTREL24:
return R_PPC32_PLTREL;
+ case R_PPC_GOT_TLSGD16:
+ return R_TLSGD_GOT;
+ case R_PPC_GOT_TLSLD16:
+ return R_TLSLD_GOT;
+ case R_PPC_GOT_TPREL16:
+ return R_GOT_OFF;
+ case R_PPC_TLS:
+ return R_TLSIE_HINT;
+ case R_PPC_TLSGD:
+ return R_TLSDESC_CALL;
+ case R_PPC_TLSLD:
+ return R_TLSLD_HINT;
+ case R_PPC_TPREL16:
+ case R_PPC_TPREL16_HA:
+ case R_PPC_TPREL16_LO:
+ case R_PPC_TPREL16_HI:
+ return R_TLS;
default:
return R_ABS;
}
}
-void PPC::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
+static std::pair<RelType, uint64_t> fromDTPREL(RelType Type, uint64_t Val) {
+ uint64_t DTPBiasedVal = Val - 0x8000;
switch (Type) {
+ case R_PPC_DTPREL16:
+ return {R_PPC64_ADDR16, DTPBiasedVal};
+ case R_PPC_DTPREL16_HA:
+ return {R_PPC_ADDR16_HA, DTPBiasedVal};
+ case R_PPC_DTPREL16_HI:
+ return {R_PPC_ADDR16_HI, DTPBiasedVal};
+ case R_PPC_DTPREL16_LO:
+ return {R_PPC_ADDR16_LO, DTPBiasedVal};
+ case R_PPC_DTPREL32:
+ return {R_PPC_ADDR32, DTPBiasedVal};
+ default:
+ return {Type, Val};
+ }
+}
+
+void PPC::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
+ RelType NewType;
+ std::tie(NewType, Val) = fromDTPREL(Type, Val);
+ switch (NewType) {
case R_PPC_ADDR16:
case R_PPC_GOT16:
+ case R_PPC_GOT_TLSGD16:
+ case R_PPC_GOT_TLSLD16:
+ case R_PPC_GOT_TPREL16:
+ case R_PPC_TPREL16:
checkInt(Loc, Val, 16, Type);
write16(Loc, Val);
break;
case R_PPC_ADDR16_HA:
+ case R_PPC_DTPREL16_HA:
+ case R_PPC_GOT_TLSGD16_HA:
+ case R_PPC_GOT_TLSLD16_HA:
+ case R_PPC_GOT_TPREL16_HA:
case R_PPC_REL16_HA:
+ case R_PPC_TPREL16_HA:
write16(Loc, ha(Val));
break;
case R_PPC_ADDR16_HI:
+ case R_PPC_DTPREL16_HI:
+ case R_PPC_GOT_TLSGD16_HI:
+ case R_PPC_GOT_TLSLD16_HI:
+ case R_PPC_GOT_TPREL16_HI:
case R_PPC_REL16_HI:
+ case R_PPC_TPREL16_HI:
write16(Loc, Val >> 16);
break;
case R_PPC_ADDR16_LO:
+ case R_PPC_DTPREL16_LO:
+ case R_PPC_GOT_TLSGD16_LO:
+ case R_PPC_GOT_TLSLD16_LO:
+ case R_PPC_GOT_TPREL16_LO:
case R_PPC_REL16_LO:
+ case R_PPC_TPREL16_LO:
write16(Loc, Val);
break;
case R_PPC_ADDR32:
@@ -232,6 +313,109 @@ void PPC::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
}
}
+RelExpr PPC::adjustRelaxExpr(RelType Type, const uint8_t *Data,
+ RelExpr Expr) const {
+ if (Expr == R_RELAX_TLS_GD_TO_IE)
+ return R_RELAX_TLS_GD_TO_IE_GOT_OFF;
+ if (Expr == R_RELAX_TLS_LD_TO_LE)
+ return R_RELAX_TLS_LD_TO_LE_ABS;
+ return Expr;
+}
+
+int PPC::getTlsGdRelaxSkip(RelType Type) const {
+ // A __tls_get_addr call instruction is marked with 2 relocations:
+ //
+ // R_PPC_TLSGD / R_PPC_TLSLD: marker relocation
+ // R_PPC_REL24: __tls_get_addr
+ //
+ // After the relaxation we no longer call __tls_get_addr and should skip both
+ // relocations to not create a false dependence on __tls_get_addr being
+ // defined.
+ if (Type == R_PPC_TLSGD || Type == R_PPC_TLSLD)
+ return 2;
+ return 1;
+}
+
+void PPC::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const {
+ switch (Type) {
+ case R_PPC_GOT_TLSGD16: {
+ // addi rT, rA, x@got@tlsgd --> lwz rT, x@got@tprel(rA)
+ uint32_t Insn = readFromHalf16(Loc);
+ writeFromHalf16(Loc, 0x80000000 | (Insn & 0x03ff0000));
+ relocateOne(Loc, R_PPC_GOT_TPREL16, Val);
+ break;
+ }
+ case R_PPC_TLSGD:
+ // bl __tls_get_addr(x@tldgd) --> add r3, r3, r2
+ write32(Loc, 0x7c631214);
+ break;
+ default:
+ llvm_unreachable("unsupported relocation for TLS GD to IE relaxation");
+ }
+}
+
+void PPC::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
+ switch (Type) {
+ case R_PPC_GOT_TLSGD16:
+ // addi r3, r31, x@got@tlsgd --> addis r3, r2, x@tprel@ha
+ writeFromHalf16(Loc, 0x3c620000 | ha(Val));
+ break;
+ case R_PPC_TLSGD:
+ // bl __tls_get_addr(x@tldgd) --> add r3, r3, x@tprel@l
+ write32(Loc, 0x38630000 | lo(Val));
+ break;
+ default:
+ llvm_unreachable("unsupported relocation for TLS GD to LE relaxation");
+ }
+}
+
+void PPC::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
+ switch (Type) {
+ case R_PPC_GOT_TLSLD16:
+ // addi r3, rA, x@got@tlsgd --> addis r3, r2, 0
+ writeFromHalf16(Loc, 0x3c620000);
+ break;
+ case R_PPC_TLSLD:
+ // r3+x@dtprel computes r3+x-0x8000, while we want it to compute r3+x@tprel
+ // = r3+x-0x7000, so add 4096 to r3.
+ // bl __tls_get_addr(x@tlsld) --> addi r3, r3, 4096
+ write32(Loc, 0x38631000);
+ break;
+ case R_PPC_DTPREL16:
+ case R_PPC_DTPREL16_HA:
+ case R_PPC_DTPREL16_HI:
+ case R_PPC_DTPREL16_LO:
+ relocateOne(Loc, Type, Val);
+ break;
+ default:
+ llvm_unreachable("unsupported relocation for TLS LD to LE relaxation");
+ }
+}
+
+void PPC::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
+ switch (Type) {
+ case R_PPC_GOT_TPREL16: {
+ // lwz rT, x@got@tprel(rA) --> addis rT, r2, x@tprel@ha
+ uint32_t RT = readFromHalf16(Loc) & 0x03e00000;
+ writeFromHalf16(Loc, 0x3c020000 | RT | ha(Val));
+ break;
+ }
+ case R_PPC_TLS: {
+ uint32_t Insn = read32(Loc);
+ if (Insn >> 26 != 31)
+ error("unrecognized instruction for IE to LE R_PPC_TLS");
+ // addi rT, rT, x@tls --> addi rT, rT, x@tprel@l
+ uint32_t DFormOp = getPPCDFormOp((read32(Loc) & 0x000007fe) >> 1);
+ if (DFormOp == 0)
+ error("unrecognized instruction for IE to LE R_PPC_TLS");
+ write32(Loc, (DFormOp << 26) | (Insn & 0x03ff0000) | lo(Val));
+ break;
+ }
+ default:
+ llvm_unreachable("unsupported relocation for TLS IE to LE relaxation");
+ }
+}
+
TargetInfo *elf::getPPCTargetInfo() {
static PPC Target;
return &Target;
OpenPOWER on IntegriCloud