1 //===- X86_64.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 "Symbols.h" 11 #include "SyntheticSections.h" 12 #include "Target.h" 13 14 #include "lld/Common/ErrorHandler.h" 15 #include "llvm/BinaryFormat/MachO.h" 16 #include "llvm/Support/Endian.h" 17 18 using namespace llvm::MachO; 19 using namespace llvm::support::endian; 20 using namespace lld; 21 using namespace lld::macho; 22 23 namespace { 24 25 struct X86_64 : TargetInfo { 26 X86_64(); 27 28 bool isPairedReloc(relocation_info) const override; 29 uint64_t getAddend(MemoryBufferRef, const section_64 &, relocation_info, 30 relocation_info) const override; 31 void relocateOne(uint8_t *loc, const Reloc &, uint64_t val) const override; 32 33 void writeStub(uint8_t *buf, const macho::Symbol &) const override; 34 void writeStubHelperHeader(uint8_t *buf) const override; 35 void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &, 36 uint64_t entryAddr) const override; 37 38 void prepareSymbolRelocation(lld::macho::Symbol *, const InputSection *, 39 const Reloc &) override; 40 uint64_t resolveSymbolVA(uint8_t *buf, const lld::macho::Symbol &, 41 uint8_t type) const override; 42 }; 43 44 } // namespace 45 46 static std::string getErrorLocation(MemoryBufferRef mb, const section_64 &sec, 47 relocation_info rel) { 48 return ("invalid relocation at offset " + std::to_string(rel.r_address) + 49 " of " + sec.segname + "," + sec.sectname + " in " + 50 mb.getBufferIdentifier()) 51 .str(); 52 } 53 54 static void validateLength(MemoryBufferRef mb, const section_64 &sec, 55 relocation_info rel, 56 ArrayRef<uint8_t> validLengths) { 57 if (find(validLengths, rel.r_length) != validLengths.end()) 58 return; 59 60 std::string msg = getErrorLocation(mb, sec, rel) + ": relocations of type " + 61 std::to_string(rel.r_type) + " must have r_length of "; 62 bool first = true; 63 for (uint8_t length : validLengths) { 64 if (!first) 65 msg += " or "; 66 first = false; 67 msg += std::to_string(length); 68 } 69 fatal(msg); 70 } 71 72 bool X86_64::isPairedReloc(relocation_info rel) const { 73 return rel.r_type == X86_64_RELOC_SUBTRACTOR; 74 } 75 76 uint64_t X86_64::getAddend(MemoryBufferRef mb, const section_64 &sec, 77 relocation_info rel, 78 relocation_info pairedRel) const { 79 auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart()); 80 const uint8_t *loc = buf + sec.offset + rel.r_address; 81 82 if (isThreadLocalVariables(sec.flags) && rel.r_type != X86_64_RELOC_UNSIGNED) 83 error("relocations in thread-local variable sections must be " 84 "X86_64_RELOC_UNSIGNED"); 85 86 switch (rel.r_type) { 87 case X86_64_RELOC_BRANCH: 88 // XXX: ld64 also supports r_length = 0 here but I'm not sure when such a 89 // relocation will actually be generated. 90 validateLength(mb, sec, rel, {2}); 91 break; 92 case X86_64_RELOC_SIGNED: 93 case X86_64_RELOC_SIGNED_1: 94 case X86_64_RELOC_SIGNED_2: 95 case X86_64_RELOC_SIGNED_4: 96 case X86_64_RELOC_GOT_LOAD: 97 case X86_64_RELOC_GOT: 98 case X86_64_RELOC_TLV: 99 if (!rel.r_pcrel) 100 fatal(getErrorLocation(mb, sec, rel) + ": relocations of type " + 101 std::to_string(rel.r_type) + " must be pcrel"); 102 validateLength(mb, sec, rel, {2}); 103 break; 104 case X86_64_RELOC_UNSIGNED: 105 if (rel.r_pcrel) 106 fatal(getErrorLocation(mb, sec, rel) + ": relocations of type " + 107 std::to_string(rel.r_type) + " must not be pcrel"); 108 validateLength(mb, sec, rel, {2, 3}); 109 break; 110 default: 111 error("TODO: Unhandled relocation type " + std::to_string(rel.r_type)); 112 return 0; 113 } 114 115 switch (rel.r_length) { 116 case 0: 117 return *loc; 118 case 1: 119 return read16le(loc); 120 case 2: 121 return read32le(loc); 122 case 3: 123 return read64le(loc); 124 default: 125 llvm_unreachable("invalid r_length"); 126 } 127 } 128 129 void X86_64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t val) const { 130 switch (r.type) { 131 case X86_64_RELOC_BRANCH: 132 case X86_64_RELOC_SIGNED: 133 case X86_64_RELOC_SIGNED_1: 134 case X86_64_RELOC_SIGNED_2: 135 case X86_64_RELOC_SIGNED_4: 136 case X86_64_RELOC_GOT_LOAD: 137 case X86_64_RELOC_GOT: 138 case X86_64_RELOC_TLV: 139 // These types are only used for pc-relative relocations, so offset by 4 140 // since the RIP has advanced by 4 at this point. This is only valid when 141 // r_length = 2, which is enforced by validateLength(). 142 val -= 4; 143 break; 144 case X86_64_RELOC_UNSIGNED: 145 break; 146 default: 147 llvm_unreachable( 148 "getAddend should have flagged all unhandled relocation types"); 149 } 150 151 switch (r.length) { 152 case 0: 153 *loc = val; 154 break; 155 case 1: 156 write16le(loc, val); 157 break; 158 case 2: 159 write32le(loc, val); 160 break; 161 case 3: 162 write64le(loc, val); 163 break; 164 default: 165 llvm_unreachable("invalid r_length"); 166 } 167 } 168 169 // The following methods emit a number of assembly sequences with RIP-relative 170 // addressing. Note that RIP-relative addressing on X86-64 has the RIP pointing 171 // to the next instruction, not the current instruction, so we always have to 172 // account for the current instruction's size when calculating offsets. 173 // writeRipRelative helps with that. 174 // 175 // bufAddr: The virtual address corresponding to buf[0]. 176 // bufOff: The offset within buf of the next instruction. 177 // destAddr: The destination address that the current instruction references. 178 static void writeRipRelative(uint8_t *buf, uint64_t bufAddr, uint64_t bufOff, 179 uint64_t destAddr) { 180 uint64_t rip = bufAddr + bufOff; 181 // For the instructions we care about, the RIP-relative address is always 182 // stored in the last 4 bytes of the instruction. 183 write32le(buf + bufOff - 4, destAddr - rip); 184 } 185 186 static constexpr uint8_t stub[] = { 187 0xff, 0x25, 0, 0, 0, 0, // jmpq *__la_symbol_ptr(%rip) 188 }; 189 190 void X86_64::writeStub(uint8_t *buf, const macho::Symbol &sym) const { 191 memcpy(buf, stub, 2); // just copy the two nonzero bytes 192 uint64_t stubAddr = in.stubs->addr + sym.stubsIndex * sizeof(stub); 193 writeRipRelative(buf, stubAddr, sizeof(stub), 194 in.lazyPointers->addr + sym.stubsIndex * WordSize); 195 } 196 197 static constexpr uint8_t stubHelperHeader[] = { 198 0x4c, 0x8d, 0x1d, 0, 0, 0, 0, // 0x0: leaq ImageLoaderCache(%rip), %r11 199 0x41, 0x53, // 0x7: pushq %r11 200 0xff, 0x25, 0, 0, 0, 0, // 0x9: jmpq *dyld_stub_binder@GOT(%rip) 201 0x90, // 0xf: nop 202 }; 203 204 static constexpr uint8_t stubHelperEntry[] = { 205 0x68, 0, 0, 0, 0, // 0x0: pushq <bind offset> 206 0xe9, 0, 0, 0, 0, // 0x5: jmp <__stub_helper> 207 }; 208 209 void X86_64::writeStubHelperHeader(uint8_t *buf) const { 210 memcpy(buf, stubHelperHeader, sizeof(stubHelperHeader)); 211 writeRipRelative(buf, in.stubHelper->addr, 7, in.imageLoaderCache->getVA()); 212 writeRipRelative(buf, in.stubHelper->addr, 0xf, 213 in.got->addr + 214 in.stubHelper->stubBinder->gotIndex * WordSize); 215 } 216 217 void X86_64::writeStubHelperEntry(uint8_t *buf, const DylibSymbol &sym, 218 uint64_t entryAddr) const { 219 memcpy(buf, stubHelperEntry, sizeof(stubHelperEntry)); 220 write32le(buf + 1, sym.lazyBindOffset); 221 writeRipRelative(buf, entryAddr, sizeof(stubHelperEntry), 222 in.stubHelper->addr); 223 } 224 225 void X86_64::prepareSymbolRelocation(lld::macho::Symbol *sym, 226 const InputSection *isec, const Reloc &r) { 227 switch (r.type) { 228 case X86_64_RELOC_GOT_LOAD: { 229 if (needsBinding(sym)) 230 in.got->addEntry(sym); 231 232 if (sym->isTlv()) 233 error("found GOT relocation referencing thread-local variable in " + 234 toString(isec)); 235 break; 236 } 237 case X86_64_RELOC_GOT: { 238 in.got->addEntry(sym); 239 240 if (sym->isTlv()) 241 error("found GOT relocation referencing thread-local variable in " + 242 toString(isec)); 243 break; 244 } 245 case X86_64_RELOC_BRANCH: { 246 prepareBranchTarget(sym); 247 break; 248 } 249 case X86_64_RELOC_UNSIGNED: { 250 if (auto *dysym = dyn_cast<DylibSymbol>(sym)) { 251 if (r.length != 3) { 252 error("X86_64_RELOC_UNSIGNED referencing the dynamic symbol " + 253 dysym->getName() + " must have r_length = 3"); 254 return; 255 } 256 } 257 // References from thread-local variable sections are treated as offsets 258 // relative to the start of the referent section, and therefore have no 259 // need of rebase opcodes. 260 if (!(isThreadLocalVariables(isec->flags) && isa<Defined>(sym))) 261 addNonLazyBindingEntries(sym, isec, r.offset, r.addend); 262 break; 263 } 264 case X86_64_RELOC_SIGNED: 265 case X86_64_RELOC_SIGNED_1: 266 case X86_64_RELOC_SIGNED_2: 267 case X86_64_RELOC_SIGNED_4: 268 // TODO: warn if they refer to a weak global 269 break; 270 case X86_64_RELOC_TLV: { 271 if (needsBinding(sym)) 272 in.tlvPointers->addEntry(sym); 273 274 if (!sym->isTlv()) 275 error( 276 "found X86_64_RELOC_TLV referencing a non-thread-local variable in " + 277 toString(isec)); 278 break; 279 } 280 case X86_64_RELOC_SUBTRACTOR: 281 fatal("TODO: handle relocation type " + std::to_string(r.type)); 282 break; 283 default: 284 llvm_unreachable("unexpected relocation type"); 285 } 286 } 287 288 uint64_t X86_64::resolveSymbolVA(uint8_t *buf, const lld::macho::Symbol &sym, 289 uint8_t type) const { 290 switch (type) { 291 case X86_64_RELOC_GOT_LOAD: { 292 if (!sym.isInGot()) { 293 if (buf[-2] != 0x8b) 294 error("X86_64_RELOC_GOT_LOAD must be used with movq instructions"); 295 buf[-2] = 0x8d; 296 return sym.getVA(); 297 } 298 LLVM_FALLTHROUGH; 299 } 300 case X86_64_RELOC_GOT: 301 return in.got->addr + sym.gotIndex * WordSize; 302 case X86_64_RELOC_BRANCH: { 303 if (sym.isInStubs()) 304 return in.stubs->addr + sym.stubsIndex * sizeof(stub); 305 return sym.getVA(); 306 } 307 case X86_64_RELOC_UNSIGNED: 308 case X86_64_RELOC_SIGNED: 309 case X86_64_RELOC_SIGNED_1: 310 case X86_64_RELOC_SIGNED_2: 311 case X86_64_RELOC_SIGNED_4: 312 return sym.getVA(); 313 case X86_64_RELOC_TLV: { 314 if (sym.isInGot()) 315 return in.tlvPointers->addr + sym.gotIndex * WordSize; 316 317 // Convert the movq to a leaq. 318 assert(isa<Defined>(&sym)); 319 if (buf[-2] != 0x8b) 320 error("X86_64_RELOC_TLV must be used with movq instructions"); 321 buf[-2] = 0x8d; 322 return sym.getVA(); 323 } 324 case X86_64_RELOC_SUBTRACTOR: 325 fatal("TODO: handle relocation type " + std::to_string(type)); 326 default: 327 llvm_unreachable("Unexpected relocation type"); 328 } 329 } 330 331 X86_64::X86_64() { 332 cpuType = CPU_TYPE_X86_64; 333 cpuSubtype = CPU_SUBTYPE_X86_64_ALL; 334 335 stubSize = sizeof(stub); 336 stubHelperHeaderSize = sizeof(stubHelperHeader); 337 stubHelperEntrySize = sizeof(stubHelperEntry); 338 } 339 340 TargetInfo *macho::createX86_64TargetInfo() { 341 static X86_64 t; 342 return &t; 343 } 344