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