xref: /llvm-project-15.0.7/lld/ELF/Target.cpp (revision b4e2e7a2)
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