Rui Ueyama | 21c0a9c | 2017-06-16 17:32:43 +0000 | [diff] [blame] | 1 | //===- PPC64.cpp ----------------------------------------------------------===// |
| 2 | // |
| 3 | // The LLVM Linker |
| 4 | // |
| 5 | // This file is distributed under the University of Illinois Open Source |
| 6 | // License. See LICENSE.TXT for details. |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | |
Rui Ueyama | 21c0a9c | 2017-06-16 17:32:43 +0000 | [diff] [blame] | 10 | #include "Symbols.h" |
| 11 | #include "SyntheticSections.h" |
| 12 | #include "Target.h" |
Bob Haarman | b8a59c8 | 2017-10-25 22:28:38 +0000 | [diff] [blame] | 13 | #include "lld/Common/ErrorHandler.h" |
Rui Ueyama | 21c0a9c | 2017-06-16 17:32:43 +0000 | [diff] [blame] | 14 | #include "llvm/Support/Endian.h" |
| 15 | |
| 16 | using namespace llvm; |
Rui Ueyama | 21c0a9c | 2017-06-16 17:32:43 +0000 | [diff] [blame] | 17 | using namespace llvm::ELF; |
| 18 | using namespace lld; |
| 19 | using namespace lld::elf; |
| 20 | |
| 21 | static uint64_t PPC64TocOffset = 0x8000; |
| 22 | |
| 23 | uint64_t elf::getPPC64TocBase() { |
| 24 | // The TOC consists of sections .got, .toc, .tocbss, .plt in that order. The |
| 25 | // TOC starts where the first of these sections starts. We always create a |
| 26 | // .got when we see a relocation that uses it, so for us the start is always |
| 27 | // the .got. |
| 28 | uint64_t TocVA = InX::Got->getVA(); |
| 29 | |
| 30 | // Per the ppc64-elf-linux ABI, The TOC base is TOC value plus 0x8000 |
| 31 | // thus permitting a full 64 Kbytes segment. Note that the glibc startup |
| 32 | // code (crt1.o) assumes that you can get from the TOC base to the |
| 33 | // start of the .toc section with only a single (signed) 16-bit relocation. |
| 34 | return TocVA + PPC64TocOffset; |
| 35 | } |
| 36 | |
| 37 | namespace { |
| 38 | class PPC64 final : public TargetInfo { |
| 39 | public: |
| 40 | PPC64(); |
Rui Ueyama | f52496e | 2017-11-03 21:21:47 +0000 | [diff] [blame] | 41 | RelExpr getRelExpr(RelType Type, const Symbol &S, |
Rui Ueyama | 21c0a9c | 2017-06-16 17:32:43 +0000 | [diff] [blame] | 42 | const uint8_t *Loc) const override; |
| 43 | void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, |
| 44 | int32_t Index, unsigned RelOff) const override; |
Rui Ueyama | 67533a2 | 2017-10-11 22:49:24 +0000 | [diff] [blame] | 45 | void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; |
Rui Ueyama | 21c0a9c | 2017-06-16 17:32:43 +0000 | [diff] [blame] | 46 | }; |
| 47 | } // namespace |
| 48 | |
| 49 | // Relocation masks following the #lo(value), #hi(value), #ha(value), |
| 50 | // #higher(value), #highera(value), #highest(value), and #highesta(value) |
| 51 | // macros defined in section 4.5.1. Relocation Types of the PPC-elf64abi |
| 52 | // document. |
| 53 | static uint16_t applyPPCLo(uint64_t V) { return V; } |
| 54 | static uint16_t applyPPCHi(uint64_t V) { return V >> 16; } |
| 55 | static uint16_t applyPPCHa(uint64_t V) { return (V + 0x8000) >> 16; } |
| 56 | static uint16_t applyPPCHigher(uint64_t V) { return V >> 32; } |
| 57 | static uint16_t applyPPCHighera(uint64_t V) { return (V + 0x8000) >> 32; } |
| 58 | static uint16_t applyPPCHighest(uint64_t V) { return V >> 48; } |
| 59 | static uint16_t applyPPCHighesta(uint64_t V) { return (V + 0x8000) >> 48; } |
| 60 | |
| 61 | PPC64::PPC64() { |
| 62 | PltRel = GotRel = R_PPC64_GLOB_DAT; |
| 63 | RelativeRel = R_PPC64_RELATIVE; |
| 64 | GotEntrySize = 8; |
| 65 | GotPltEntrySize = 8; |
| 66 | PltEntrySize = 32; |
| 67 | PltHeaderSize = 0; |
| 68 | |
| 69 | // We need 64K pages (at least under glibc/Linux, the loader won't |
| 70 | // set different permissions on a finer granularity than that). |
| 71 | DefaultMaxPageSize = 65536; |
| 72 | |
| 73 | // The PPC64 ELF ABI v1 spec, says: |
| 74 | // |
| 75 | // It is normally desirable to put segments with different characteristics |
| 76 | // in separate 256 Mbyte portions of the address space, to give the |
| 77 | // operating system full paging flexibility in the 64-bit address space. |
| 78 | // |
| 79 | // And because the lowest non-zero 256M boundary is 0x10000000, PPC64 linkers |
| 80 | // use 0x10000000 as the starting address. |
| 81 | DefaultImageBase = 0x10000000; |
| 82 | } |
| 83 | |
Rui Ueyama | f52496e | 2017-11-03 21:21:47 +0000 | [diff] [blame] | 84 | RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S, |
Rui Ueyama | be85529 | 2017-10-12 03:14:06 +0000 | [diff] [blame] | 85 | const uint8_t *Loc) const { |
Rui Ueyama | 21c0a9c | 2017-06-16 17:32:43 +0000 | [diff] [blame] | 86 | switch (Type) { |
Rui Ueyama | 21c0a9c | 2017-06-16 17:32:43 +0000 | [diff] [blame] | 87 | case R_PPC64_TOC16: |
| 88 | case R_PPC64_TOC16_DS: |
| 89 | case R_PPC64_TOC16_HA: |
| 90 | case R_PPC64_TOC16_HI: |
| 91 | case R_PPC64_TOC16_LO: |
| 92 | case R_PPC64_TOC16_LO_DS: |
| 93 | return R_GOTREL; |
| 94 | case R_PPC64_TOC: |
| 95 | return R_PPC_TOC; |
| 96 | case R_PPC64_REL24: |
| 97 | return R_PPC_PLT_OPD; |
Rui Ueyama | be85529 | 2017-10-12 03:14:06 +0000 | [diff] [blame] | 98 | default: |
| 99 | return R_ABS; |
Rui Ueyama | 21c0a9c | 2017-06-16 17:32:43 +0000 | [diff] [blame] | 100 | } |
| 101 | } |
| 102 | |
| 103 | void PPC64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, |
| 104 | uint64_t PltEntryAddr, int32_t Index, |
| 105 | unsigned RelOff) const { |
| 106 | uint64_t Off = GotPltEntryAddr - getPPC64TocBase(); |
| 107 | |
| 108 | // FIXME: What we should do, in theory, is get the offset of the function |
| 109 | // descriptor in the .opd section, and use that as the offset from %r2 (the |
| 110 | // TOC-base pointer). Instead, we have the GOT-entry offset, and that will |
| 111 | // be a pointer to the function descriptor in the .opd section. Using |
| 112 | // this scheme is simpler, but requires an extra indirection per PLT dispatch. |
| 113 | |
Fangrui Song | 0c48302 | 2018-03-09 18:03:22 +0000 | [diff] [blame] | 114 | write32(Buf, 0xf8410028); // std %r2, 40(%r1) |
| 115 | write32(Buf + 4, 0x3d620000 | applyPPCHa(Off)); // addis %r11, %r2, X@ha |
| 116 | write32(Buf + 8, 0xe98b0000 | applyPPCLo(Off)); // ld %r12, X@l(%r11) |
| 117 | write32(Buf + 12, 0xe96c0000); // ld %r11,0(%r12) |
| 118 | write32(Buf + 16, 0x7d6903a6); // mtctr %r11 |
| 119 | write32(Buf + 20, 0xe84c0008); // ld %r2,8(%r12) |
| 120 | write32(Buf + 24, 0xe96c0010); // ld %r11,16(%r12) |
| 121 | write32(Buf + 28, 0x4e800420); // bctr |
Rui Ueyama | 21c0a9c | 2017-06-16 17:32:43 +0000 | [diff] [blame] | 122 | } |
| 123 | |
Rui Ueyama | 67533a2 | 2017-10-11 22:49:24 +0000 | [diff] [blame] | 124 | static std::pair<RelType, uint64_t> toAddr16Rel(RelType Type, uint64_t Val) { |
Rui Ueyama | 21c0a9c | 2017-06-16 17:32:43 +0000 | [diff] [blame] | 125 | uint64_t V = Val - PPC64TocOffset; |
| 126 | switch (Type) { |
| 127 | case R_PPC64_TOC16: |
| 128 | return {R_PPC64_ADDR16, V}; |
| 129 | case R_PPC64_TOC16_DS: |
| 130 | return {R_PPC64_ADDR16_DS, V}; |
| 131 | case R_PPC64_TOC16_HA: |
| 132 | return {R_PPC64_ADDR16_HA, V}; |
| 133 | case R_PPC64_TOC16_HI: |
| 134 | return {R_PPC64_ADDR16_HI, V}; |
| 135 | case R_PPC64_TOC16_LO: |
| 136 | return {R_PPC64_ADDR16_LO, V}; |
| 137 | case R_PPC64_TOC16_LO_DS: |
| 138 | return {R_PPC64_ADDR16_LO_DS, V}; |
| 139 | default: |
| 140 | return {Type, Val}; |
| 141 | } |
| 142 | } |
| 143 | |
Rui Ueyama | 67533a2 | 2017-10-11 22:49:24 +0000 | [diff] [blame] | 144 | void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { |
Rui Ueyama | 21c0a9c | 2017-06-16 17:32:43 +0000 | [diff] [blame] | 145 | // For a TOC-relative relocation, proceed in terms of the corresponding |
| 146 | // ADDR16 relocation type. |
| 147 | std::tie(Type, Val) = toAddr16Rel(Type, Val); |
| 148 | |
| 149 | switch (Type) { |
| 150 | case R_PPC64_ADDR14: { |
| 151 | checkAlignment<4>(Loc, Val, Type); |
| 152 | // Preserve the AA/LK bits in the branch instruction |
| 153 | uint8_t AALK = Loc[3]; |
Fangrui Song | 0c48302 | 2018-03-09 18:03:22 +0000 | [diff] [blame] | 154 | write16(Loc + 2, (AALK & 3) | (Val & 0xfffc)); |
Rui Ueyama | 21c0a9c | 2017-06-16 17:32:43 +0000 | [diff] [blame] | 155 | break; |
| 156 | } |
| 157 | case R_PPC64_ADDR16: |
| 158 | checkInt<16>(Loc, Val, Type); |
Fangrui Song | 0c48302 | 2018-03-09 18:03:22 +0000 | [diff] [blame] | 159 | write16(Loc, Val); |
Rui Ueyama | 21c0a9c | 2017-06-16 17:32:43 +0000 | [diff] [blame] | 160 | break; |
| 161 | case R_PPC64_ADDR16_DS: |
| 162 | checkInt<16>(Loc, Val, Type); |
Fangrui Song | 0c48302 | 2018-03-09 18:03:22 +0000 | [diff] [blame] | 163 | write16(Loc, (read16(Loc) & 3) | (Val & ~3)); |
Rui Ueyama | 21c0a9c | 2017-06-16 17:32:43 +0000 | [diff] [blame] | 164 | break; |
| 165 | case R_PPC64_ADDR16_HA: |
| 166 | case R_PPC64_REL16_HA: |
Fangrui Song | 0c48302 | 2018-03-09 18:03:22 +0000 | [diff] [blame] | 167 | write16(Loc, applyPPCHa(Val)); |
Rui Ueyama | 21c0a9c | 2017-06-16 17:32:43 +0000 | [diff] [blame] | 168 | break; |
| 169 | case R_PPC64_ADDR16_HI: |
| 170 | case R_PPC64_REL16_HI: |
Fangrui Song | 0c48302 | 2018-03-09 18:03:22 +0000 | [diff] [blame] | 171 | write16(Loc, applyPPCHi(Val)); |
Rui Ueyama | 21c0a9c | 2017-06-16 17:32:43 +0000 | [diff] [blame] | 172 | break; |
| 173 | case R_PPC64_ADDR16_HIGHER: |
Fangrui Song | 0c48302 | 2018-03-09 18:03:22 +0000 | [diff] [blame] | 174 | write16(Loc, applyPPCHigher(Val)); |
Rui Ueyama | 21c0a9c | 2017-06-16 17:32:43 +0000 | [diff] [blame] | 175 | break; |
| 176 | case R_PPC64_ADDR16_HIGHERA: |
Fangrui Song | 0c48302 | 2018-03-09 18:03:22 +0000 | [diff] [blame] | 177 | write16(Loc, applyPPCHighera(Val)); |
Rui Ueyama | 21c0a9c | 2017-06-16 17:32:43 +0000 | [diff] [blame] | 178 | break; |
| 179 | case R_PPC64_ADDR16_HIGHEST: |
Fangrui Song | 0c48302 | 2018-03-09 18:03:22 +0000 | [diff] [blame] | 180 | write16(Loc, applyPPCHighest(Val)); |
Rui Ueyama | 21c0a9c | 2017-06-16 17:32:43 +0000 | [diff] [blame] | 181 | break; |
| 182 | case R_PPC64_ADDR16_HIGHESTA: |
Fangrui Song | 0c48302 | 2018-03-09 18:03:22 +0000 | [diff] [blame] | 183 | write16(Loc, applyPPCHighesta(Val)); |
Rui Ueyama | 21c0a9c | 2017-06-16 17:32:43 +0000 | [diff] [blame] | 184 | break; |
| 185 | case R_PPC64_ADDR16_LO: |
Fangrui Song | 0c48302 | 2018-03-09 18:03:22 +0000 | [diff] [blame] | 186 | write16(Loc, applyPPCLo(Val)); |
Rui Ueyama | 21c0a9c | 2017-06-16 17:32:43 +0000 | [diff] [blame] | 187 | break; |
| 188 | case R_PPC64_ADDR16_LO_DS: |
| 189 | case R_PPC64_REL16_LO: |
Fangrui Song | 0c48302 | 2018-03-09 18:03:22 +0000 | [diff] [blame] | 190 | write16(Loc, (read16(Loc) & 3) | (applyPPCLo(Val) & ~3)); |
Rui Ueyama | 21c0a9c | 2017-06-16 17:32:43 +0000 | [diff] [blame] | 191 | break; |
| 192 | case R_PPC64_ADDR32: |
| 193 | case R_PPC64_REL32: |
| 194 | checkInt<32>(Loc, Val, Type); |
Fangrui Song | 0c48302 | 2018-03-09 18:03:22 +0000 | [diff] [blame] | 195 | write32(Loc, Val); |
Rui Ueyama | 21c0a9c | 2017-06-16 17:32:43 +0000 | [diff] [blame] | 196 | break; |
| 197 | case R_PPC64_ADDR64: |
| 198 | case R_PPC64_REL64: |
| 199 | case R_PPC64_TOC: |
Fangrui Song | 0c48302 | 2018-03-09 18:03:22 +0000 | [diff] [blame] | 200 | write64(Loc, Val); |
Rui Ueyama | 21c0a9c | 2017-06-16 17:32:43 +0000 | [diff] [blame] | 201 | break; |
| 202 | case R_PPC64_REL24: { |
| 203 | uint32_t Mask = 0x03FFFFFC; |
| 204 | checkInt<24>(Loc, Val, Type); |
Fangrui Song | 0c48302 | 2018-03-09 18:03:22 +0000 | [diff] [blame] | 205 | write32(Loc, (read32(Loc) & ~Mask) | (Val & Mask)); |
Rui Ueyama | 21c0a9c | 2017-06-16 17:32:43 +0000 | [diff] [blame] | 206 | break; |
| 207 | } |
| 208 | default: |
| 209 | error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); |
| 210 | } |
| 211 | } |
| 212 | |
Rui Ueyama | e145bc2 | 2017-06-16 20:15:03 +0000 | [diff] [blame] | 213 | TargetInfo *elf::getPPC64TargetInfo() { |
| 214 | static PPC64 Target; |
| 215 | return &Target; |
| 216 | } |