1 | //===-- X86ELFObjectWriter.cpp - X86 ELF Writer ---------------------------===// |
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 "MCTargetDesc/X86FixupKinds.h" |
10 | #include "MCTargetDesc/X86MCTargetDesc.h" |
11 | #include "llvm/BinaryFormat/ELF.h" |
12 | #include "llvm/MC/MCAsmInfo.h" |
13 | #include "llvm/MC/MCContext.h" |
14 | #include "llvm/MC/MCELFObjectWriter.h" |
15 | #include "llvm/MC/MCExpr.h" |
16 | #include "llvm/MC/MCFixup.h" |
17 | #include "llvm/MC/MCObjectWriter.h" |
18 | #include "llvm/MC/MCValue.h" |
19 | #include "llvm/Support/ErrorHandling.h" |
20 | #include <cassert> |
21 | #include <cstdint> |
22 | |
23 | using namespace llvm; |
24 | |
25 | namespace { |
26 | |
27 | class X86ELFObjectWriter : public MCELFObjectTargetWriter { |
28 | public: |
29 | X86ELFObjectWriter(bool IsELF64, uint8_t OSABI, uint16_t EMachine); |
30 | ~X86ELFObjectWriter() override = default; |
31 | |
32 | protected: |
33 | unsigned getRelocType(MCContext &Ctx, const MCValue &Target, |
34 | const MCFixup &Fixup, bool IsPCRel) const override; |
35 | }; |
36 | |
37 | } // end anonymous namespace |
38 | |
39 | X86ELFObjectWriter::X86ELFObjectWriter(bool IsELF64, uint8_t OSABI, |
40 | uint16_t EMachine) |
41 | : MCELFObjectTargetWriter(IsELF64, OSABI, EMachine, |
42 | // Only i386 and IAMCU use Rel instead of RelA. |
43 | /*HasRelocationAddend*/ |
44 | (EMachine != ELF::EM_386) && |
45 | (EMachine != ELF::EM_IAMCU)) {} |
46 | |
47 | enum X86_64RelType { RT64_NONE, RT64_64, RT64_32, RT64_32S, RT64_16, RT64_8 }; |
48 | |
49 | static X86_64RelType getType64(MCFixupKind Kind, |
50 | MCSymbolRefExpr::VariantKind &Modifier, |
51 | bool &IsPCRel) { |
52 | switch (unsigned(Kind)) { |
53 | default: |
54 | llvm_unreachable("Unimplemented" ); |
55 | case FK_NONE: |
56 | return RT64_NONE; |
57 | case X86::reloc_global_offset_table8: |
58 | Modifier = MCSymbolRefExpr::VK_GOT; |
59 | IsPCRel = true; |
60 | return RT64_64; |
61 | case FK_Data_8: |
62 | return RT64_64; |
63 | case X86::reloc_signed_4byte: |
64 | case X86::reloc_signed_4byte_relax: |
65 | if (Modifier == MCSymbolRefExpr::VK_None && !IsPCRel) |
66 | return RT64_32S; |
67 | return RT64_32; |
68 | case X86::reloc_global_offset_table: |
69 | Modifier = MCSymbolRefExpr::VK_GOT; |
70 | IsPCRel = true; |
71 | return RT64_32; |
72 | case FK_Data_4: |
73 | case FK_PCRel_4: |
74 | case X86::reloc_riprel_4byte: |
75 | case X86::reloc_riprel_4byte_relax: |
76 | case X86::reloc_riprel_4byte_relax_rex: |
77 | case X86::reloc_riprel_4byte_movq_load: |
78 | return RT64_32; |
79 | case X86::reloc_branch_4byte_pcrel: |
80 | Modifier = MCSymbolRefExpr::VK_PLT; |
81 | return RT64_32; |
82 | case FK_PCRel_2: |
83 | case FK_Data_2: |
84 | return RT64_16; |
85 | case FK_PCRel_1: |
86 | case FK_Data_1: |
87 | return RT64_8; |
88 | } |
89 | } |
90 | |
91 | static void checkIs32(MCContext &Ctx, SMLoc Loc, X86_64RelType Type) { |
92 | if (Type != RT64_32) |
93 | Ctx.reportError(L: Loc, |
94 | Msg: "32 bit reloc applied to a field with a different size" ); |
95 | } |
96 | |
97 | static void checkIs64(MCContext &Ctx, SMLoc Loc, X86_64RelType Type) { |
98 | if (Type != RT64_64) |
99 | Ctx.reportError(L: Loc, |
100 | Msg: "64 bit reloc applied to a field with a different size" ); |
101 | } |
102 | |
103 | static unsigned getRelocType64(MCContext &Ctx, SMLoc Loc, |
104 | MCSymbolRefExpr::VariantKind Modifier, |
105 | X86_64RelType Type, bool IsPCRel, |
106 | MCFixupKind Kind) { |
107 | switch (Modifier) { |
108 | default: |
109 | llvm_unreachable("Unimplemented" ); |
110 | case MCSymbolRefExpr::VK_None: |
111 | case MCSymbolRefExpr::VK_X86_ABS8: |
112 | switch (Type) { |
113 | case RT64_NONE: |
114 | if (Modifier == MCSymbolRefExpr::VK_None) |
115 | return ELF::R_X86_64_NONE; |
116 | llvm_unreachable("Unimplemented" ); |
117 | case RT64_64: |
118 | return IsPCRel ? ELF::R_X86_64_PC64 : ELF::R_X86_64_64; |
119 | case RT64_32: |
120 | return IsPCRel ? ELF::R_X86_64_PC32 : ELF::R_X86_64_32; |
121 | case RT64_32S: |
122 | return ELF::R_X86_64_32S; |
123 | case RT64_16: |
124 | return IsPCRel ? ELF::R_X86_64_PC16 : ELF::R_X86_64_16; |
125 | case RT64_8: |
126 | return IsPCRel ? ELF::R_X86_64_PC8 : ELF::R_X86_64_8; |
127 | } |
128 | llvm_unreachable("unexpected relocation type!" ); |
129 | case MCSymbolRefExpr::VK_GOT: |
130 | switch (Type) { |
131 | case RT64_64: |
132 | return IsPCRel ? ELF::R_X86_64_GOTPC64 : ELF::R_X86_64_GOT64; |
133 | case RT64_32: |
134 | return IsPCRel ? ELF::R_X86_64_GOTPC32 : ELF::R_X86_64_GOT32; |
135 | case RT64_32S: |
136 | case RT64_16: |
137 | case RT64_8: |
138 | case RT64_NONE: |
139 | llvm_unreachable("Unimplemented" ); |
140 | } |
141 | llvm_unreachable("unexpected relocation type!" ); |
142 | case MCSymbolRefExpr::VK_GOTOFF: |
143 | assert(!IsPCRel); |
144 | if (Type != RT64_64) |
145 | Ctx.reportError(L: Loc, Msg: "unsupported relocation type" ); |
146 | return ELF::R_X86_64_GOTOFF64; |
147 | case MCSymbolRefExpr::VK_TPOFF: |
148 | assert(!IsPCRel); |
149 | switch (Type) { |
150 | case RT64_64: |
151 | return ELF::R_X86_64_TPOFF64; |
152 | case RT64_32: |
153 | return ELF::R_X86_64_TPOFF32; |
154 | case RT64_32S: |
155 | case RT64_16: |
156 | case RT64_8: |
157 | case RT64_NONE: |
158 | llvm_unreachable("Unimplemented" ); |
159 | } |
160 | llvm_unreachable("unexpected relocation type!" ); |
161 | case MCSymbolRefExpr::VK_DTPOFF: |
162 | assert(!IsPCRel); |
163 | switch (Type) { |
164 | case RT64_64: |
165 | return ELF::R_X86_64_DTPOFF64; |
166 | case RT64_32: |
167 | return ELF::R_X86_64_DTPOFF32; |
168 | case RT64_32S: |
169 | case RT64_16: |
170 | case RT64_8: |
171 | case RT64_NONE: |
172 | llvm_unreachable("Unimplemented" ); |
173 | } |
174 | llvm_unreachable("unexpected relocation type!" ); |
175 | case MCSymbolRefExpr::VK_SIZE: |
176 | assert(!IsPCRel); |
177 | switch (Type) { |
178 | case RT64_64: |
179 | return ELF::R_X86_64_SIZE64; |
180 | case RT64_32: |
181 | return ELF::R_X86_64_SIZE32; |
182 | case RT64_32S: |
183 | case RT64_16: |
184 | case RT64_8: |
185 | case RT64_NONE: |
186 | llvm_unreachable("Unimplemented" ); |
187 | } |
188 | llvm_unreachable("unexpected relocation type!" ); |
189 | case MCSymbolRefExpr::VK_TLSCALL: |
190 | return ELF::R_X86_64_TLSDESC_CALL; |
191 | case MCSymbolRefExpr::VK_TLSDESC: |
192 | return ELF::R_X86_64_GOTPC32_TLSDESC; |
193 | case MCSymbolRefExpr::VK_TLSGD: |
194 | checkIs32(Ctx, Loc, Type); |
195 | return ELF::R_X86_64_TLSGD; |
196 | case MCSymbolRefExpr::VK_GOTTPOFF: |
197 | checkIs32(Ctx, Loc, Type); |
198 | return ELF::R_X86_64_GOTTPOFF; |
199 | case MCSymbolRefExpr::VK_TLSLD: |
200 | checkIs32(Ctx, Loc, Type); |
201 | return ELF::R_X86_64_TLSLD; |
202 | case MCSymbolRefExpr::VK_PLT: |
203 | checkIs32(Ctx, Loc, Type); |
204 | return ELF::R_X86_64_PLT32; |
205 | case MCSymbolRefExpr::VK_GOTPCREL: |
206 | checkIs32(Ctx, Loc, Type); |
207 | // Older versions of ld.bfd/ld.gold/lld |
208 | // do not support GOTPCRELX/REX_GOTPCRELX, |
209 | // and we want to keep back-compatibility. |
210 | if (!Ctx.getTargetOptions()->X86RelaxRelocations) |
211 | return ELF::R_X86_64_GOTPCREL; |
212 | switch (unsigned(Kind)) { |
213 | default: |
214 | return ELF::R_X86_64_GOTPCREL; |
215 | case X86::reloc_riprel_4byte_relax: |
216 | return ELF::R_X86_64_GOTPCRELX; |
217 | case X86::reloc_riprel_4byte_relax_rex: |
218 | case X86::reloc_riprel_4byte_movq_load: |
219 | return ELF::R_X86_64_REX_GOTPCRELX; |
220 | } |
221 | llvm_unreachable("unexpected relocation type!" ); |
222 | case MCSymbolRefExpr::VK_GOTPCREL_NORELAX: |
223 | checkIs32(Ctx, Loc, Type); |
224 | return ELF::R_X86_64_GOTPCREL; |
225 | case MCSymbolRefExpr::VK_X86_PLTOFF: |
226 | checkIs64(Ctx, Loc, Type); |
227 | return ELF::R_X86_64_PLTOFF64; |
228 | } |
229 | } |
230 | |
231 | enum X86_32RelType { RT32_NONE, RT32_32, RT32_16, RT32_8 }; |
232 | |
233 | static unsigned getRelocType32(MCContext &Ctx, SMLoc Loc, |
234 | MCSymbolRefExpr::VariantKind Modifier, |
235 | X86_32RelType Type, bool IsPCRel, |
236 | MCFixupKind Kind) { |
237 | switch (Modifier) { |
238 | default: |
239 | llvm_unreachable("Unimplemented" ); |
240 | case MCSymbolRefExpr::VK_None: |
241 | case MCSymbolRefExpr::VK_X86_ABS8: |
242 | switch (Type) { |
243 | case RT32_NONE: |
244 | if (Modifier == MCSymbolRefExpr::VK_None) |
245 | return ELF::R_386_NONE; |
246 | llvm_unreachable("Unimplemented" ); |
247 | case RT32_32: |
248 | return IsPCRel ? ELF::R_386_PC32 : ELF::R_386_32; |
249 | case RT32_16: |
250 | return IsPCRel ? ELF::R_386_PC16 : ELF::R_386_16; |
251 | case RT32_8: |
252 | return IsPCRel ? ELF::R_386_PC8 : ELF::R_386_8; |
253 | } |
254 | llvm_unreachable("unexpected relocation type!" ); |
255 | case MCSymbolRefExpr::VK_GOT: |
256 | if (Type != RT32_32) |
257 | break; |
258 | if (IsPCRel) |
259 | return ELF::R_386_GOTPC; |
260 | // Older versions of ld.bfd/ld.gold/lld do not support R_386_GOT32X and we |
261 | // want to maintain compatibility. |
262 | if (!Ctx.getTargetOptions()->X86RelaxRelocations) |
263 | return ELF::R_386_GOT32; |
264 | |
265 | return Kind == MCFixupKind(X86::reloc_signed_4byte_relax) |
266 | ? ELF::R_386_GOT32X |
267 | : ELF::R_386_GOT32; |
268 | case MCSymbolRefExpr::VK_GOTOFF: |
269 | assert(!IsPCRel); |
270 | if (Type != RT32_32) |
271 | break; |
272 | return ELF::R_386_GOTOFF; |
273 | case MCSymbolRefExpr::VK_TLSCALL: |
274 | return ELF::R_386_TLS_DESC_CALL; |
275 | case MCSymbolRefExpr::VK_TLSDESC: |
276 | return ELF::R_386_TLS_GOTDESC; |
277 | case MCSymbolRefExpr::VK_TPOFF: |
278 | if (Type != RT32_32) |
279 | break; |
280 | assert(!IsPCRel); |
281 | return ELF::R_386_TLS_LE_32; |
282 | case MCSymbolRefExpr::VK_DTPOFF: |
283 | if (Type != RT32_32) |
284 | break; |
285 | assert(!IsPCRel); |
286 | return ELF::R_386_TLS_LDO_32; |
287 | case MCSymbolRefExpr::VK_TLSGD: |
288 | if (Type != RT32_32) |
289 | break; |
290 | assert(!IsPCRel); |
291 | return ELF::R_386_TLS_GD; |
292 | case MCSymbolRefExpr::VK_GOTTPOFF: |
293 | if (Type != RT32_32) |
294 | break; |
295 | assert(!IsPCRel); |
296 | return ELF::R_386_TLS_IE_32; |
297 | case MCSymbolRefExpr::VK_PLT: |
298 | if (Type != RT32_32) |
299 | break; |
300 | return ELF::R_386_PLT32; |
301 | case MCSymbolRefExpr::VK_INDNTPOFF: |
302 | if (Type != RT32_32) |
303 | break; |
304 | assert(!IsPCRel); |
305 | return ELF::R_386_TLS_IE; |
306 | case MCSymbolRefExpr::VK_NTPOFF: |
307 | if (Type != RT32_32) |
308 | break; |
309 | assert(!IsPCRel); |
310 | return ELF::R_386_TLS_LE; |
311 | case MCSymbolRefExpr::VK_GOTNTPOFF: |
312 | if (Type != RT32_32) |
313 | break; |
314 | assert(!IsPCRel); |
315 | return ELF::R_386_TLS_GOTIE; |
316 | case MCSymbolRefExpr::VK_TLSLDM: |
317 | if (Type != RT32_32) |
318 | break; |
319 | assert(!IsPCRel); |
320 | return ELF::R_386_TLS_LDM; |
321 | } |
322 | Ctx.reportError(L: Loc, Msg: "unsupported relocation type" ); |
323 | return ELF::R_386_NONE; |
324 | } |
325 | |
326 | unsigned X86ELFObjectWriter::getRelocType(MCContext &Ctx, const MCValue &Target, |
327 | const MCFixup &Fixup, |
328 | bool IsPCRel) const { |
329 | MCFixupKind Kind = Fixup.getKind(); |
330 | if (Kind >= FirstLiteralRelocationKind) |
331 | return Kind - FirstLiteralRelocationKind; |
332 | MCSymbolRefExpr::VariantKind Modifier = Target.getAccessVariant(); |
333 | X86_64RelType Type = getType64(Kind, Modifier, IsPCRel); |
334 | if (getEMachine() == ELF::EM_X86_64) |
335 | return getRelocType64(Ctx, Loc: Fixup.getLoc(), Modifier, Type, IsPCRel, Kind); |
336 | |
337 | assert((getEMachine() == ELF::EM_386 || getEMachine() == ELF::EM_IAMCU) && |
338 | "Unsupported ELF machine type." ); |
339 | |
340 | X86_32RelType RelType = RT32_NONE; |
341 | switch (Type) { |
342 | case RT64_NONE: |
343 | break; |
344 | case RT64_64: |
345 | Ctx.reportError(L: Fixup.getLoc(), Msg: "unsupported relocation type" ); |
346 | return ELF::R_386_NONE; |
347 | case RT64_32: |
348 | case RT64_32S: |
349 | RelType = RT32_32; |
350 | break; |
351 | case RT64_16: |
352 | RelType = RT32_16; |
353 | break; |
354 | case RT64_8: |
355 | RelType = RT32_8; |
356 | break; |
357 | } |
358 | return getRelocType32(Ctx, Loc: Fixup.getLoc(), Modifier, Type: RelType, IsPCRel, Kind); |
359 | } |
360 | |
361 | std::unique_ptr<MCObjectTargetWriter> |
362 | llvm::createX86ELFObjectWriter(bool IsELF64, uint8_t OSABI, uint16_t EMachine) { |
363 | return std::make_unique<X86ELFObjectWriter>(args&: IsELF64, args&: OSABI, args&: EMachine); |
364 | } |
365 | |