| 1 | //===- PPC.cpp ------------------------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "OutputSections.h" |
| 10 | #include "Symbols.h" |
| 11 | #include "SyntheticSections.h" |
| 12 | #include "Target.h" |
| 13 | #include "Thunks.h" |
| 14 | |
| 15 | using namespace llvm; |
| 16 | using namespace llvm::support::endian; |
| 17 | using namespace llvm::ELF; |
| 18 | using namespace lld; |
| 19 | using namespace lld::elf; |
| 20 | |
| 21 | // Undefine the macro predefined by GCC powerpc32. |
| 22 | #undef PPC |
| 23 | |
| 24 | namespace { |
| 25 | class PPC final : public TargetInfo { |
| 26 | public: |
| 27 | PPC(Ctx &); |
| 28 | RelExpr getRelExpr(RelType type, const Symbol &s, |
| 29 | const uint8_t *loc) const override; |
| 30 | RelType getDynRel(RelType type) const override; |
| 31 | int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override; |
| 32 | void writeGotHeader(uint8_t *buf) const override; |
| 33 | void (uint8_t *buf) const override { |
| 34 | llvm_unreachable("should call writePPC32GlinkSection() instead" ); |
| 35 | } |
| 36 | void writePlt(uint8_t *buf, const Symbol &sym, |
| 37 | uint64_t pltEntryAddr) const override { |
| 38 | llvm_unreachable("should call writePPC32GlinkSection() instead" ); |
| 39 | } |
| 40 | void writeIplt(uint8_t *buf, const Symbol &sym, |
| 41 | uint64_t pltEntryAddr) const override; |
| 42 | void writeGotPlt(uint8_t *buf, const Symbol &s) const override; |
| 43 | bool needsThunk(RelExpr expr, RelType relocType, const InputFile *file, |
| 44 | uint64_t branchAddr, const Symbol &s, |
| 45 | int64_t a) const override; |
| 46 | uint32_t getThunkSectionSpacing() const override; |
| 47 | bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override; |
| 48 | void relocate(uint8_t *loc, const Relocation &rel, |
| 49 | uint64_t val) const override; |
| 50 | RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override; |
| 51 | int getTlsGdRelaxSkip(RelType type) const override; |
| 52 | void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override; |
| 53 | |
| 54 | private: |
| 55 | void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) const; |
| 56 | void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const; |
| 57 | void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const; |
| 58 | void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const; |
| 59 | }; |
| 60 | } // namespace |
| 61 | |
| 62 | static uint16_t lo(uint32_t v) { return v; } |
| 63 | static uint16_t ha(uint32_t v) { return (v + 0x8000) >> 16; } |
| 64 | |
| 65 | static uint32_t readFromHalf16(Ctx &ctx, const uint8_t *loc) { |
| 66 | return read32(ctx, p: ctx.arg.isLE ? loc : loc - 2); |
| 67 | } |
| 68 | |
| 69 | static void writeFromHalf16(Ctx &ctx, uint8_t *loc, uint32_t insn) { |
| 70 | write32(ctx, p: ctx.arg.isLE ? loc : loc - 2, v: insn); |
| 71 | } |
| 72 | |
| 73 | void elf::writePPC32GlinkSection(Ctx &ctx, uint8_t *buf, size_t numEntries) { |
| 74 | // Create canonical PLT entries for non-PIE code. Compilers don't generate |
| 75 | // non-GOT-non-PLT relocations referencing external functions for -fpie/-fPIE. |
| 76 | uint32_t glink = ctx.in.plt->getVA(); // VA of .glink |
| 77 | if (!ctx.arg.isPic) { |
| 78 | for (const Symbol *sym : |
| 79 | cast<PPC32GlinkSection>(Val&: *ctx.in.plt).canonical_plts) { |
| 80 | writePPC32PltCallStub(ctx, buf, gotPltVA: sym->getGotPltVA(ctx), file: nullptr, addend: 0); |
| 81 | buf += 16; |
| 82 | glink += 16; |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | // On PPC Secure PLT ABI, bl foo@plt jumps to a call stub, which loads an |
| 87 | // absolute address from a specific .plt slot (usually called .got.plt on |
| 88 | // other targets) and jumps there. |
| 89 | // |
| 90 | // a) With immediate binding (BIND_NOW), the .plt entry is resolved at load |
| 91 | // time. The .glink section is not used. |
| 92 | // b) With lazy binding, the .plt entry points to a `b PLTresolve` |
| 93 | // instruction in .glink, filled in by PPC::writeGotPlt(). |
| 94 | |
| 95 | // Write N `b PLTresolve` first. |
| 96 | for (size_t i = 0; i != numEntries; ++i) |
| 97 | write32(ctx, p: buf + 4 * i, v: 0x48000000 | 4 * (numEntries - i)); |
| 98 | buf += 4 * numEntries; |
| 99 | |
| 100 | // Then write PLTresolve(), which has two forms: PIC and non-PIC. PLTresolve() |
| 101 | // computes the PLT index (by computing the distance from the landing b to |
| 102 | // itself) and calls _dl_runtime_resolve() (in glibc). |
| 103 | uint32_t got = ctx.in.got->getVA(); |
| 104 | const uint8_t *end = buf + 64; |
| 105 | if (ctx.arg.isPic) { |
| 106 | uint32_t afterBcl = 4 * ctx.in.plt->getNumEntries() + 12; |
| 107 | uint32_t gotBcl = got + 4 - (glink + afterBcl); |
| 108 | write32(ctx, p: buf + 0, |
| 109 | v: 0x3d6b0000 | ha(v: afterBcl)); // addis r11,r11,1f-glink@ha |
| 110 | write32(ctx, p: buf + 4, v: 0x7c0802a6); // mflr r0 |
| 111 | write32(ctx, p: buf + 8, v: 0x429f0005); // bcl 20,30,.+4 |
| 112 | write32(ctx, p: buf + 12, |
| 113 | v: 0x396b0000 | lo(v: afterBcl)); // 1: addi r11,r11,1b-glink@l |
| 114 | write32(ctx, p: buf + 16, v: 0x7d8802a6); // mflr r12 |
| 115 | write32(ctx, p: buf + 20, v: 0x7c0803a6); // mtlr r0 |
| 116 | write32(ctx, p: buf + 24, v: 0x7d6c5850); // sub r11,r11,r12 |
| 117 | write32(ctx, p: buf + 28, v: 0x3d8c0000 | ha(v: gotBcl)); // addis 12,12,GOT+4-1b@ha |
| 118 | if (ha(v: gotBcl) == ha(v: gotBcl + 4)) { |
| 119 | write32(ctx, p: buf + 32, |
| 120 | v: 0x800c0000 | lo(v: gotBcl)); // lwz r0,r12,GOT+4-1b@l(r12) |
| 121 | write32(ctx, p: buf + 36, |
| 122 | v: 0x818c0000 | lo(v: gotBcl + 4)); // lwz r12,r12,GOT+8-1b@l(r12) |
| 123 | } else { |
| 124 | write32(ctx, p: buf + 32, |
| 125 | v: 0x840c0000 | lo(v: gotBcl)); // lwzu r0,r12,GOT+4-1b@l(r12) |
| 126 | write32(ctx, p: buf + 36, v: 0x818c0000 | 4); // lwz r12,r12,4(r12) |
| 127 | } |
| 128 | write32(ctx, p: buf + 40, v: 0x7c0903a6); // mtctr 0 |
| 129 | write32(ctx, p: buf + 44, v: 0x7c0b5a14); // add r0,11,11 |
| 130 | write32(ctx, p: buf + 48, v: 0x7d605a14); // add r11,0,11 |
| 131 | write32(ctx, p: buf + 52, v: 0x4e800420); // bctr |
| 132 | buf += 56; |
| 133 | } else { |
| 134 | write32(ctx, p: buf + 0, v: 0x3d800000 | ha(v: got + 4)); // lis r12,GOT+4@ha |
| 135 | write32(ctx, p: buf + 4, v: 0x3d6b0000 | ha(v: -glink)); // addis r11,r11,-glink@ha |
| 136 | if (ha(v: got + 4) == ha(v: got + 8)) |
| 137 | write32(ctx, p: buf + 8, v: 0x800c0000 | lo(v: got + 4)); // lwz r0,GOT+4@l(r12) |
| 138 | else |
| 139 | write32(ctx, p: buf + 8, v: 0x840c0000 | lo(v: got + 4)); // lwzu r0,GOT+4@l(r12) |
| 140 | write32(ctx, p: buf + 12, v: 0x396b0000 | lo(v: -glink)); // addi r11,r11,-glink@l |
| 141 | write32(ctx, p: buf + 16, v: 0x7c0903a6); // mtctr r0 |
| 142 | write32(ctx, p: buf + 20, v: 0x7c0b5a14); // add r0,r11,r11 |
| 143 | if (ha(v: got + 4) == ha(v: got + 8)) |
| 144 | write32(ctx, p: buf + 24, v: 0x818c0000 | lo(v: got + 8)); // lwz r12,GOT+8@l(r12) |
| 145 | else |
| 146 | write32(ctx, p: buf + 24, v: 0x818c0000 | 4); // lwz r12,4(r12) |
| 147 | write32(ctx, p: buf + 28, v: 0x7d605a14); // add r11,r0,r11 |
| 148 | write32(ctx, p: buf + 32, v: 0x4e800420); // bctr |
| 149 | buf += 36; |
| 150 | } |
| 151 | |
| 152 | // Pad with nop. They should not be executed. |
| 153 | for (; buf < end; buf += 4) |
| 154 | write32(ctx, p: buf, v: 0x60000000); |
| 155 | } |
| 156 | |
| 157 | PPC::PPC(Ctx &ctx) : TargetInfo(ctx) { |
| 158 | copyRel = R_PPC_COPY; |
| 159 | gotRel = R_PPC_GLOB_DAT; |
| 160 | pltRel = R_PPC_JMP_SLOT; |
| 161 | relativeRel = R_PPC_RELATIVE; |
| 162 | iRelativeRel = R_PPC_IRELATIVE; |
| 163 | symbolicRel = R_PPC_ADDR32; |
| 164 | gotHeaderEntriesNum = 3; |
| 165 | gotPltHeaderEntriesNum = 0; |
| 166 | pltHeaderSize = 0; |
| 167 | pltEntrySize = 4; |
| 168 | ipltEntrySize = 16; |
| 169 | |
| 170 | needsThunks = true; |
| 171 | |
| 172 | tlsModuleIndexRel = R_PPC_DTPMOD32; |
| 173 | tlsOffsetRel = R_PPC_DTPREL32; |
| 174 | tlsGotRel = R_PPC_TPREL32; |
| 175 | |
| 176 | defaultMaxPageSize = 65536; |
| 177 | defaultImageBase = 0x10000000; |
| 178 | |
| 179 | write32(ctx, p: trapInstr.data(), v: 0x7fe00008); |
| 180 | } |
| 181 | |
| 182 | void PPC::writeIplt(uint8_t *buf, const Symbol &sym, |
| 183 | uint64_t /*pltEntryAddr*/) const { |
| 184 | // In -pie or -shared mode, assume r30 points to .got2+0x8000, and use a |
| 185 | // .got2.plt_pic32. thunk. |
| 186 | writePPC32PltCallStub(ctx, buf, gotPltVA: sym.getGotPltVA(ctx), file: sym.file, addend: 0x8000); |
| 187 | } |
| 188 | |
| 189 | void PPC::(uint8_t *buf) const { |
| 190 | // _GLOBAL_OFFSET_TABLE_[0] = _DYNAMIC |
| 191 | // glibc stores _dl_runtime_resolve in _GLOBAL_OFFSET_TABLE_[1], |
| 192 | // link_map in _GLOBAL_OFFSET_TABLE_[2]. |
| 193 | write32(ctx, p: buf, v: ctx.mainPart->dynamic->getVA()); |
| 194 | } |
| 195 | |
| 196 | void PPC::writeGotPlt(uint8_t *buf, const Symbol &s) const { |
| 197 | // Address of the symbol resolver stub in .glink . |
| 198 | write32(ctx, p: buf, |
| 199 | v: ctx.in.plt->getVA() + ctx.in.plt->headerSize + 4 * s.getPltIdx(ctx)); |
| 200 | } |
| 201 | |
| 202 | bool PPC::needsThunk(RelExpr expr, RelType type, const InputFile *file, |
| 203 | uint64_t branchAddr, const Symbol &s, int64_t a) const { |
| 204 | if (type != R_PPC_LOCAL24PC && type != R_PPC_REL24 && type != R_PPC_PLTREL24) |
| 205 | return false; |
| 206 | if (s.isInPlt(ctx)) |
| 207 | return true; |
| 208 | if (s.isUndefWeak()) |
| 209 | return false; |
| 210 | return !PPC::inBranchRange(type, src: branchAddr, dst: s.getVA(ctx, addend: a)); |
| 211 | } |
| 212 | |
| 213 | uint32_t PPC::getThunkSectionSpacing() const { return 0x2000000; } |
| 214 | |
| 215 | bool PPC::inBranchRange(RelType type, uint64_t src, uint64_t dst) const { |
| 216 | uint64_t offset = dst - src; |
| 217 | if (type == R_PPC_LOCAL24PC || type == R_PPC_REL24 || type == R_PPC_PLTREL24) |
| 218 | return isInt<26>(x: offset); |
| 219 | llvm_unreachable("unsupported relocation type used in branch" ); |
| 220 | } |
| 221 | |
| 222 | RelExpr PPC::getRelExpr(RelType type, const Symbol &s, |
| 223 | const uint8_t *loc) const { |
| 224 | switch (type) { |
| 225 | case R_PPC_NONE: |
| 226 | return R_NONE; |
| 227 | case R_PPC_ADDR16_HA: |
| 228 | case R_PPC_ADDR16_HI: |
| 229 | case R_PPC_ADDR16_LO: |
| 230 | case R_PPC_ADDR24: |
| 231 | case R_PPC_ADDR32: |
| 232 | return R_ABS; |
| 233 | case R_PPC_DTPREL16: |
| 234 | case R_PPC_DTPREL16_HA: |
| 235 | case R_PPC_DTPREL16_HI: |
| 236 | case R_PPC_DTPREL16_LO: |
| 237 | case R_PPC_DTPREL32: |
| 238 | return R_DTPREL; |
| 239 | case R_PPC_REL14: |
| 240 | case R_PPC_REL32: |
| 241 | case R_PPC_REL16_LO: |
| 242 | case R_PPC_REL16_HI: |
| 243 | case R_PPC_REL16_HA: |
| 244 | return R_PC; |
| 245 | case R_PPC_GOT16: |
| 246 | return R_GOT_OFF; |
| 247 | case R_PPC_LOCAL24PC: |
| 248 | case R_PPC_REL24: |
| 249 | return R_PLT_PC; |
| 250 | case R_PPC_PLTREL24: |
| 251 | return RE_PPC32_PLTREL; |
| 252 | case R_PPC_GOT_TLSGD16: |
| 253 | return R_TLSGD_GOT; |
| 254 | case R_PPC_GOT_TLSLD16: |
| 255 | return R_TLSLD_GOT; |
| 256 | case R_PPC_GOT_TPREL16: |
| 257 | return R_GOT_OFF; |
| 258 | case R_PPC_TLS: |
| 259 | return R_TLSIE_HINT; |
| 260 | case R_PPC_TLSGD: |
| 261 | return R_TLSDESC_CALL; |
| 262 | case R_PPC_TLSLD: |
| 263 | return R_TLSLD_HINT; |
| 264 | case R_PPC_TPREL16: |
| 265 | case R_PPC_TPREL16_HA: |
| 266 | case R_PPC_TPREL16_LO: |
| 267 | case R_PPC_TPREL16_HI: |
| 268 | return R_TPREL; |
| 269 | default: |
| 270 | Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << type.v |
| 271 | << ") against symbol " << &s; |
| 272 | return R_NONE; |
| 273 | } |
| 274 | } |
| 275 | |
| 276 | RelType PPC::getDynRel(RelType type) const { |
| 277 | if (type == R_PPC_ADDR32) |
| 278 | return type; |
| 279 | return R_PPC_NONE; |
| 280 | } |
| 281 | |
| 282 | int64_t PPC::getImplicitAddend(const uint8_t *buf, RelType type) const { |
| 283 | switch (type) { |
| 284 | case R_PPC_NONE: |
| 285 | case R_PPC_GLOB_DAT: |
| 286 | case R_PPC_JMP_SLOT: |
| 287 | return 0; |
| 288 | case R_PPC_ADDR32: |
| 289 | case R_PPC_REL32: |
| 290 | case R_PPC_RELATIVE: |
| 291 | case R_PPC_IRELATIVE: |
| 292 | case R_PPC_DTPMOD32: |
| 293 | case R_PPC_DTPREL32: |
| 294 | case R_PPC_TPREL32: |
| 295 | return SignExtend64<32>(x: read32(ctx, p: buf)); |
| 296 | default: |
| 297 | InternalErr(ctx, buf) << "cannot read addend for relocation " << type; |
| 298 | return 0; |
| 299 | } |
| 300 | } |
| 301 | |
| 302 | static std::pair<RelType, uint64_t> fromDTPREL(RelType type, uint64_t val) { |
| 303 | uint64_t dtpBiasedVal = val - 0x8000; |
| 304 | switch (type) { |
| 305 | case R_PPC_DTPREL16: |
| 306 | return {R_PPC64_ADDR16, dtpBiasedVal}; |
| 307 | case R_PPC_DTPREL16_HA: |
| 308 | return {R_PPC_ADDR16_HA, dtpBiasedVal}; |
| 309 | case R_PPC_DTPREL16_HI: |
| 310 | return {R_PPC_ADDR16_HI, dtpBiasedVal}; |
| 311 | case R_PPC_DTPREL16_LO: |
| 312 | return {R_PPC_ADDR16_LO, dtpBiasedVal}; |
| 313 | case R_PPC_DTPREL32: |
| 314 | return {R_PPC_ADDR32, dtpBiasedVal}; |
| 315 | default: |
| 316 | return {type, val}; |
| 317 | } |
| 318 | } |
| 319 | |
| 320 | void PPC::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { |
| 321 | RelType newType; |
| 322 | std::tie(args&: newType, args&: val) = fromDTPREL(type: rel.type, val); |
| 323 | switch (newType) { |
| 324 | case R_PPC_ADDR16: |
| 325 | checkIntUInt(ctx, loc, v: val, n: 16, rel); |
| 326 | write16(ctx, p: loc, v: val); |
| 327 | break; |
| 328 | case R_PPC_GOT16: |
| 329 | case R_PPC_GOT_TLSGD16: |
| 330 | case R_PPC_GOT_TLSLD16: |
| 331 | case R_PPC_GOT_TPREL16: |
| 332 | case R_PPC_TPREL16: |
| 333 | checkInt(ctx, loc, v: val, n: 16, rel); |
| 334 | write16(ctx, p: loc, v: val); |
| 335 | break; |
| 336 | case R_PPC_ADDR16_HA: |
| 337 | case R_PPC_DTPREL16_HA: |
| 338 | case R_PPC_GOT_TLSGD16_HA: |
| 339 | case R_PPC_GOT_TLSLD16_HA: |
| 340 | case R_PPC_GOT_TPREL16_HA: |
| 341 | case R_PPC_REL16_HA: |
| 342 | case R_PPC_TPREL16_HA: |
| 343 | write16(ctx, p: loc, v: ha(v: val)); |
| 344 | break; |
| 345 | case R_PPC_ADDR16_HI: |
| 346 | case R_PPC_DTPREL16_HI: |
| 347 | case R_PPC_GOT_TLSGD16_HI: |
| 348 | case R_PPC_GOT_TLSLD16_HI: |
| 349 | case R_PPC_GOT_TPREL16_HI: |
| 350 | case R_PPC_REL16_HI: |
| 351 | case R_PPC_TPREL16_HI: |
| 352 | write16(ctx, p: loc, v: val >> 16); |
| 353 | break; |
| 354 | case R_PPC_ADDR16_LO: |
| 355 | case R_PPC_DTPREL16_LO: |
| 356 | case R_PPC_GOT_TLSGD16_LO: |
| 357 | case R_PPC_GOT_TLSLD16_LO: |
| 358 | case R_PPC_GOT_TPREL16_LO: |
| 359 | case R_PPC_REL16_LO: |
| 360 | case R_PPC_TPREL16_LO: |
| 361 | write16(ctx, p: loc, v: val); |
| 362 | break; |
| 363 | case R_PPC_ADDR32: |
| 364 | case R_PPC_REL32: |
| 365 | write32(ctx, p: loc, v: val); |
| 366 | break; |
| 367 | case R_PPC_REL14: { |
| 368 | uint32_t mask = 0x0000FFFC; |
| 369 | checkInt(ctx, loc, v: val, n: 16, rel); |
| 370 | checkAlignment(ctx, loc, v: val, n: 4, rel); |
| 371 | write32(ctx, p: loc, v: (read32(ctx, p: loc) & ~mask) | (val & mask)); |
| 372 | break; |
| 373 | } |
| 374 | case R_PPC_ADDR24: |
| 375 | case R_PPC_REL24: |
| 376 | case R_PPC_LOCAL24PC: |
| 377 | case R_PPC_PLTREL24: { |
| 378 | uint32_t mask = 0x03FFFFFC; |
| 379 | checkInt(ctx, loc, v: val, n: 26, rel); |
| 380 | checkAlignment(ctx, loc, v: val, n: 4, rel); |
| 381 | write32(ctx, p: loc, v: (read32(ctx, p: loc) & ~mask) | (val & mask)); |
| 382 | break; |
| 383 | } |
| 384 | default: |
| 385 | llvm_unreachable("unknown relocation" ); |
| 386 | } |
| 387 | } |
| 388 | |
| 389 | RelExpr PPC::adjustTlsExpr(RelType type, RelExpr expr) const { |
| 390 | if (expr == R_RELAX_TLS_GD_TO_IE) |
| 391 | return R_RELAX_TLS_GD_TO_IE_GOT_OFF; |
| 392 | if (expr == R_RELAX_TLS_LD_TO_LE) |
| 393 | return R_RELAX_TLS_LD_TO_LE_ABS; |
| 394 | return expr; |
| 395 | } |
| 396 | |
| 397 | int PPC::getTlsGdRelaxSkip(RelType type) const { |
| 398 | // A __tls_get_addr call instruction is marked with 2 relocations: |
| 399 | // |
| 400 | // R_PPC_TLSGD / R_PPC_TLSLD: marker relocation |
| 401 | // R_PPC_REL24: __tls_get_addr |
| 402 | // |
| 403 | // After the relaxation we no longer call __tls_get_addr and should skip both |
| 404 | // relocations to not create a false dependence on __tls_get_addr being |
| 405 | // defined. |
| 406 | if (type == R_PPC_TLSGD || type == R_PPC_TLSLD) |
| 407 | return 2; |
| 408 | return 1; |
| 409 | } |
| 410 | |
| 411 | void PPC::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, |
| 412 | uint64_t val) const { |
| 413 | switch (rel.type) { |
| 414 | case R_PPC_GOT_TLSGD16: { |
| 415 | // addi rT, rA, x@got@tlsgd --> lwz rT, x@got@tprel(rA) |
| 416 | uint32_t insn = readFromHalf16(ctx, loc); |
| 417 | writeFromHalf16(ctx, loc, insn: 0x80000000 | (insn & 0x03ff0000)); |
| 418 | relocateNoSym(loc, type: R_PPC_GOT_TPREL16, val); |
| 419 | break; |
| 420 | } |
| 421 | case R_PPC_TLSGD: |
| 422 | // bl __tls_get_addr(x@tldgd) --> add r3, r3, r2 |
| 423 | write32(ctx, p: loc, v: 0x7c631214); |
| 424 | break; |
| 425 | default: |
| 426 | llvm_unreachable("unsupported relocation for TLS GD to IE relaxation" ); |
| 427 | } |
| 428 | } |
| 429 | |
| 430 | void PPC::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, |
| 431 | uint64_t val) const { |
| 432 | switch (rel.type) { |
| 433 | case R_PPC_GOT_TLSGD16: |
| 434 | // addi r3, r31, x@got@tlsgd --> addis r3, r2, x@tprel@ha |
| 435 | writeFromHalf16(ctx, loc, insn: 0x3c620000 | ha(v: val)); |
| 436 | break; |
| 437 | case R_PPC_TLSGD: |
| 438 | // bl __tls_get_addr(x@tldgd) --> add r3, r3, x@tprel@l |
| 439 | write32(ctx, p: loc, v: 0x38630000 | lo(v: val)); |
| 440 | break; |
| 441 | default: |
| 442 | llvm_unreachable("unsupported relocation for TLS GD to LE relaxation" ); |
| 443 | } |
| 444 | } |
| 445 | |
| 446 | void PPC::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, |
| 447 | uint64_t val) const { |
| 448 | switch (rel.type) { |
| 449 | case R_PPC_GOT_TLSLD16: |
| 450 | // addi r3, rA, x@got@tlsgd --> addis r3, r2, 0 |
| 451 | writeFromHalf16(ctx, loc, insn: 0x3c620000); |
| 452 | break; |
| 453 | case R_PPC_TLSLD: |
| 454 | // r3+x@dtprel computes r3+x-0x8000, while we want it to compute r3+x@tprel |
| 455 | // = r3+x-0x7000, so add 4096 to r3. |
| 456 | // bl __tls_get_addr(x@tlsld) --> addi r3, r3, 4096 |
| 457 | write32(ctx, p: loc, v: 0x38631000); |
| 458 | break; |
| 459 | case R_PPC_DTPREL16: |
| 460 | case R_PPC_DTPREL16_HA: |
| 461 | case R_PPC_DTPREL16_HI: |
| 462 | case R_PPC_DTPREL16_LO: |
| 463 | relocate(loc, rel, val); |
| 464 | break; |
| 465 | default: |
| 466 | llvm_unreachable("unsupported relocation for TLS LD to LE relaxation" ); |
| 467 | } |
| 468 | } |
| 469 | |
| 470 | void PPC::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, |
| 471 | uint64_t val) const { |
| 472 | switch (rel.type) { |
| 473 | case R_PPC_GOT_TPREL16: { |
| 474 | // lwz rT, x@got@tprel(rA) --> addis rT, r2, x@tprel@ha |
| 475 | uint32_t rt = readFromHalf16(ctx, loc) & 0x03e00000; |
| 476 | writeFromHalf16(ctx, loc, insn: 0x3c020000 | rt | ha(v: val)); |
| 477 | break; |
| 478 | } |
| 479 | case R_PPC_TLS: { |
| 480 | uint32_t insn = read32(ctx, p: loc); |
| 481 | if (insn >> 26 != 31) |
| 482 | ErrAlways(ctx) << "unrecognized instruction for IE to LE R_PPC_TLS" ; |
| 483 | // addi rT, rT, x@tls --> addi rT, rT, x@tprel@l |
| 484 | unsigned secondaryOp = (read32(ctx, p: loc) & 0x000007fe) >> 1; |
| 485 | uint32_t dFormOp = getPPCDFormOp(secondaryOp); |
| 486 | if (dFormOp == 0) { // Expecting a DS-Form instruction. |
| 487 | dFormOp = getPPCDSFormOp(secondaryOp); |
| 488 | if (dFormOp == 0) |
| 489 | ErrAlways(ctx) << "unrecognized instruction for IE to LE R_PPC_TLS" ; |
| 490 | } |
| 491 | write32(ctx, p: loc, v: (dFormOp | (insn & 0x03ff0000) | lo(v: val))); |
| 492 | break; |
| 493 | } |
| 494 | default: |
| 495 | llvm_unreachable("unsupported relocation for TLS IE to LE relaxation" ); |
| 496 | } |
| 497 | } |
| 498 | |
| 499 | void PPC::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const { |
| 500 | uint64_t secAddr = sec.getOutputSection()->addr; |
| 501 | if (auto *s = dyn_cast<InputSection>(Val: &sec)) |
| 502 | secAddr += s->outSecOff; |
| 503 | for (const Relocation &rel : sec.relocs()) { |
| 504 | uint8_t *loc = buf + rel.offset; |
| 505 | const uint64_t val = |
| 506 | SignExtend64(X: sec.getRelocTargetVA(ctx, r: rel, p: secAddr + rel.offset), B: 32); |
| 507 | switch (rel.expr) { |
| 508 | case R_RELAX_TLS_GD_TO_IE_GOT_OFF: |
| 509 | relaxTlsGdToIe(loc, rel, val); |
| 510 | break; |
| 511 | case R_RELAX_TLS_GD_TO_LE: |
| 512 | relaxTlsGdToLe(loc, rel, val); |
| 513 | break; |
| 514 | case R_RELAX_TLS_LD_TO_LE_ABS: |
| 515 | relaxTlsLdToLe(loc, rel, val); |
| 516 | break; |
| 517 | case R_RELAX_TLS_IE_TO_LE: |
| 518 | relaxTlsIeToLe(loc, rel, val); |
| 519 | break; |
| 520 | default: |
| 521 | relocate(loc, rel, val); |
| 522 | break; |
| 523 | } |
| 524 | } |
| 525 | } |
| 526 | |
| 527 | void elf::setPPCTargetInfo(Ctx &ctx) { ctx.target.reset(p: new PPC(ctx)); } |
| 528 | |