1 //===- Target.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 10 #include "Target.h" 11 #include "Error.h" 12 #include "Symbols.h" 13 14 #include "llvm/ADT/ArrayRef.h" 15 #include "llvm/Object/ELF.h" 16 #include "llvm/Support/Endian.h" 17 #include "llvm/Support/ELF.h" 18 19 using namespace llvm; 20 using namespace llvm::object; 21 using namespace llvm::support::endian; 22 using namespace llvm::ELF; 23 24 namespace lld { 25 namespace elf2 { 26 27 std::unique_ptr<TargetInfo> Target; 28 29 TargetInfo::~TargetInfo() {} 30 31 bool TargetInfo::relocPointsToGot(uint32_t Type) const { return false; } 32 33 X86TargetInfo::X86TargetInfo() { 34 PCRelReloc = R_386_PC32; 35 GotReloc = R_386_GLOB_DAT; 36 GotRefReloc = R_386_GOT32; 37 } 38 39 void X86TargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, 40 uint64_t PltEntryAddr) const { 41 // jmpl *val; nop; nop 42 const uint8_t Inst[] = {0xff, 0x25, 0, 0, 0, 0, 0x90, 0x90}; 43 memcpy(Buf, Inst, sizeof(Inst)); 44 assert(isUInt<32>(GotEntryAddr)); 45 write32le(Buf + 2, GotEntryAddr); 46 } 47 48 bool X86TargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const { 49 return Type == R_386_GOT32 || relocNeedsPlt(Type, S); 50 } 51 52 bool X86TargetInfo::relocPointsToGot(uint32_t Type) const { 53 return Type == R_386_GOTPC; 54 } 55 56 bool X86TargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const { 57 return Type == R_386_PLT32; 58 } 59 60 static void add32le(uint8_t *P, int32_t V) { write32le(P, read32le(P) + V); } 61 62 void X86TargetInfo::relocateOne(uint8_t *Buf, const void *RelP, uint32_t Type, 63 uint64_t BaseAddr, uint64_t SymVA, 64 uint64_t GotVA) const { 65 typedef ELFFile<ELF32LE>::Elf_Rel Elf_Rel; 66 auto &Rel = *reinterpret_cast<const Elf_Rel *>(RelP); 67 68 uint32_t Offset = Rel.r_offset; 69 uint8_t *Location = Buf + Offset; 70 switch (Type) { 71 case R_386_GOT32: 72 add32le(Location, SymVA - GotVA); 73 break; 74 case R_386_PC32: 75 add32le(Location, SymVA - (BaseAddr + Offset)); 76 break; 77 case R_386_32: 78 add32le(Location, SymVA); 79 break; 80 default: 81 error(Twine("unrecognized reloc ") + Twine(Type)); 82 break; 83 } 84 } 85 86 X86_64TargetInfo::X86_64TargetInfo() { 87 PCRelReloc = R_X86_64_PC32; 88 GotReloc = R_X86_64_GLOB_DAT; 89 GotRefReloc = R_X86_64_PC32; 90 } 91 92 void X86_64TargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, 93 uint64_t PltEntryAddr) const { 94 // jmpq *val(%rip); nop; nop 95 const uint8_t Inst[] = {0xff, 0x25, 0, 0, 0, 0, 0x90, 0x90}; 96 memcpy(Buf, Inst, sizeof(Inst)); 97 98 uint64_t NextPC = PltEntryAddr + 6; 99 int64_t Delta = GotEntryAddr - NextPC; 100 assert(isInt<32>(Delta)); 101 write32le(Buf + 2, Delta); 102 } 103 104 bool X86_64TargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const { 105 return Type == R_X86_64_GOTPCREL || relocNeedsPlt(Type, S); 106 } 107 108 bool X86_64TargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const { 109 switch (Type) { 110 default: 111 return false; 112 case R_X86_64_PC32: 113 // This relocation is defined to have a value of (S + A - P). 114 // The problems start when a non PIC program calls a function in a shared 115 // library. 116 // In an ideal world, we could just report an error saying the relocation 117 // can overflow at runtime. 118 // In the real world with glibc, crt1.o has a R_X86_64_PC32 pointing to 119 // libc.so. 120 // 121 // The general idea on how to handle such cases is to create a PLT entry 122 // and use that as the function value. 123 // 124 // For the static linking part, we just return true and everything else 125 // will use the the PLT entry as the address. 126 // 127 // The remaining (unimplemented) problem is making sure pointer equality 128 // still works. We need the help of the dynamic linker for that. We 129 // let it know that we have a direct reference to a so symbol by creating 130 // an undefined symbol with a non zero st_value. Seeing that, the 131 // dynamic linker resolves the symbol to the value of the symbol we created. 132 // This is true even for got entries, so pointer equality is maintained. 133 // To avoid an infinite loop, the only entry that points to the 134 // real function is a dedicated got entry used by the plt. That is 135 // identified by special relocation types (R_X86_64_JUMP_SLOT, 136 // R_386_JMP_SLOT, etc). 137 return S.isShared(); 138 case R_X86_64_PLT32: 139 return true; 140 } 141 } 142 143 void X86_64TargetInfo::relocateOne(uint8_t *Buf, const void *RelP, 144 uint32_t Type, uint64_t BaseAddr, 145 uint64_t SymVA, uint64_t GotVA) const { 146 typedef ELFFile<ELF64LE>::Elf_Rela Elf_Rela; 147 auto &Rel = *reinterpret_cast<const Elf_Rela *>(RelP); 148 149 uint64_t Offset = Rel.r_offset; 150 uint8_t *Location = Buf + Offset; 151 switch (Type) { 152 case R_X86_64_PC32: 153 case R_X86_64_GOTPCREL: 154 write32le(Location, SymVA + Rel.r_addend - (BaseAddr + Offset)); 155 break; 156 case R_X86_64_64: 157 write64le(Location, SymVA + Rel.r_addend); 158 break; 159 case R_X86_64_32: { 160 case R_X86_64_32S: 161 uint64_t VA = SymVA + Rel.r_addend; 162 if (Type == R_X86_64_32 && !isUInt<32>(VA)) 163 error("R_X86_64_32 out of range"); 164 else if (!isInt<32>(VA)) 165 error("R_X86_64_32S out of range"); 166 167 write32le(Location, VA); 168 break; 169 } 170 default: 171 error(Twine("unrecognized reloc ") + Twine(Type)); 172 break; 173 } 174 } 175 176 PPC64TargetInfo::PPC64TargetInfo() { 177 // PCRelReloc = FIXME 178 // GotReloc = FIXME 179 } 180 void PPC64TargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, 181 uint64_t PltEntryAddr) const {} 182 bool PPC64TargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const { 183 return false; 184 } 185 bool PPC64TargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const { 186 return false; 187 } 188 void PPC64TargetInfo::relocateOne(uint8_t *Buf, const void *RelP, uint32_t Type, 189 uint64_t BaseAddr, uint64_t SymVA, 190 uint64_t GotVA) const { 191 typedef ELFFile<ELF64BE>::Elf_Rela Elf_Rela; 192 auto &Rel = *reinterpret_cast<const Elf_Rela *>(RelP); 193 194 uint64_t Offset = Rel.r_offset; 195 uint8_t *Location = Buf + Offset; 196 switch (Type) { 197 case R_PPC64_ADDR64: 198 write64be(Location, SymVA + Rel.r_addend); 199 break; 200 case R_PPC64_TOC: 201 // We don't create a TOC yet. 202 break; 203 default: 204 error(Twine("unrecognized reloc ") + Twine(Type)); 205 break; 206 } 207 } 208 209 PPCTargetInfo::PPCTargetInfo() { 210 // PCRelReloc = FIXME 211 // GotReloc = FIXME 212 } 213 void PPCTargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, 214 uint64_t PltEntryAddr) const {} 215 bool PPCTargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const { 216 return false; 217 } 218 bool PPCTargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const { 219 return false; 220 } 221 void PPCTargetInfo::relocateOne(uint8_t *Buf, const void *RelP, uint32_t Type, 222 uint64_t BaseAddr, uint64_t SymVA, 223 uint64_t GotVA) const {} 224 225 ARMTargetInfo::ARMTargetInfo() { 226 // PCRelReloc = FIXME 227 // GotReloc = FIXME 228 } 229 void ARMTargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, 230 uint64_t PltEntryAddr) const {} 231 bool ARMTargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const { 232 return false; 233 } 234 bool ARMTargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const { 235 return false; 236 } 237 void ARMTargetInfo::relocateOne(uint8_t *Buf, const void *RelP, uint32_t Type, 238 uint64_t BaseAddr, uint64_t SymVA, 239 uint64_t GotVA) const {} 240 241 AArch64TargetInfo::AArch64TargetInfo() { 242 // PCRelReloc = FIXME 243 // GotReloc = FIXME 244 } 245 void AArch64TargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, 246 uint64_t PltEntryAddr) const {} 247 bool AArch64TargetInfo::relocNeedsGot(uint32_t Type, 248 const SymbolBody &S) const { 249 return false; 250 } 251 bool AArch64TargetInfo::relocNeedsPlt(uint32_t Type, 252 const SymbolBody &S) const { 253 return false; 254 } 255 256 static void handle_ADR_PREL_LO21(uint8_t *Location, uint64_t S, int64_t A, 257 uint64_t P) { 258 uint64_t X = S + A - P; 259 if (!isInt<21>(X)) 260 error("Relocation R_AARCH64_ADR_PREL_LO21 out of range"); 261 uint32_t Imm = X & 0x1FFFFF; 262 uint32_t ImmLo = (Imm & 0x3) << 29; 263 uint32_t ImmHi = ((Imm & 0x1FFFFC) >> 2) << 5; 264 uint64_t Mask = (0x3 << 29) | (0x7FFFF << 5); 265 write32le(Location, (read32le(Location) & ~Mask) | ImmLo | ImmHi); 266 } 267 268 void AArch64TargetInfo::relocateOne(uint8_t *Buf, const void *RelP, 269 uint32_t Type, uint64_t BaseAddr, 270 uint64_t SymVA, uint64_t GotVA) const { 271 typedef ELFFile<ELF64LE>::Elf_Rela Elf_Rela; 272 auto &Rel = *reinterpret_cast<const Elf_Rela *>(RelP); 273 274 uint8_t *Location = Buf + Rel.r_offset; 275 uint64_t S = SymVA; 276 int64_t A = Rel.r_addend; 277 uint64_t P = BaseAddr + Rel.r_offset; 278 switch (Type) { 279 case R_AARCH64_ADR_PREL_LO21: 280 handle_ADR_PREL_LO21(Location, S, A, P); 281 break; 282 default: 283 error(Twine("unrecognized reloc ") + Twine(Type)); 284 break; 285 } 286 } 287 288 MipsTargetInfo::MipsTargetInfo() { 289 // PCRelReloc = FIXME 290 // GotReloc = FIXME 291 DefaultEntry = "__start"; 292 } 293 294 void MipsTargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, 295 uint64_t PltEntryAddr) const {} 296 297 bool MipsTargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const { 298 return false; 299 } 300 301 bool MipsTargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const { 302 return false; 303 } 304 305 void MipsTargetInfo::relocateOne(uint8_t *Buf, const void *RelP, uint32_t Type, 306 uint64_t BaseAddr, uint64_t SymVA, 307 uint64_t GotVA) const {} 308 } 309 } 310