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