1//===- X86.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 "OutputSections.h"
10#include "RelocScan.h"
11#include "Symbols.h"
12#include "SyntheticSections.h"
13#include "Target.h"
14#include "llvm/Support/Endian.h"
15
16using namespace llvm;
17using namespace llvm::support::endian;
18using namespace llvm::ELF;
19using namespace lld;
20using namespace lld::elf;
21
22namespace {
23class X86 : public TargetInfo {
24public:
25 X86(Ctx &);
26 void initTargetSpecificSections() override;
27 RelExpr getRelExpr(RelType type, const Symbol &s,
28 const uint8_t *loc) const override;
29 int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
30 void writeGotPltHeader(uint8_t *buf) const override;
31 RelType getDynRel(RelType type) const override;
32 void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
33 void writeIgotPlt(uint8_t *buf, const Symbol &s) const override;
34 void writePltHeader(uint8_t *buf) const override;
35 void writePlt(uint8_t *buf, const Symbol &sym,
36 uint64_t pltEntryAddr) const override;
37 void relocate(uint8_t *loc, const Relocation &rel,
38 uint64_t val) const override;
39 template <class ELFT, class RelTy>
40 void scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
41 unsigned shard);
42 void scanSection(InputSectionBase &sec, unsigned shard) override;
43 void relocateAlloc(InputSection &sec, uint8_t *buf) const override;
44
45private:
46 void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
47 void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
48 void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
49 void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
50};
51} // namespace
52
53X86::X86(Ctx &ctx) : TargetInfo(ctx) {
54 copyRel = R_386_COPY;
55 gotRel = R_386_GLOB_DAT;
56 pltRel = R_386_JUMP_SLOT;
57 iRelativeRel = R_386_IRELATIVE;
58 relativeRel = R_386_RELATIVE;
59 symbolicRel = R_386_32;
60 tlsDescRel = R_386_TLS_DESC;
61 tlsGotRel = R_386_TLS_TPOFF;
62 tlsModuleIndexRel = R_386_TLS_DTPMOD32;
63 tlsOffsetRel = R_386_TLS_DTPOFF32;
64 gotBaseSymInGotPlt = true;
65 pltHeaderSize = 16;
66 pltEntrySize = 16;
67 ipltEntrySize = 16;
68 trapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3
69
70 // Align to the non-PAE large page size (known as a superpage or huge page).
71 // FreeBSD automatically promotes large, superpage-aligned allocations.
72 defaultImageBase = 0x400000;
73}
74
75void X86::initTargetSpecificSections() {
76 if (ctx.arg.andFeatures & GNU_PROPERTY_X86_FEATURE_1_IBT) {
77 ctx.in.ibtPlt = std::make_unique<IBTPltSection>(args&: ctx);
78 ctx.inputSections.push_back(Elt: ctx.in.ibtPlt.get());
79 }
80}
81
82// Only needed to support relocations used by relocateNonAlloc and relocateEh.
83RelExpr X86::getRelExpr(RelType type, const Symbol &s,
84 const uint8_t *loc) const {
85 switch (type) {
86 case R_386_8:
87 case R_386_16:
88 case R_386_32:
89 return R_ABS;
90 case R_386_TLS_LDO_32:
91 return R_DTPREL;
92 case R_386_PC8:
93 case R_386_PC16:
94 case R_386_PC32:
95 return R_PC;
96 case R_386_GOTPC:
97 return R_GOTPLTONLY_PC;
98 case R_386_GOTOFF:
99 return R_GOTPLTREL;
100 case R_386_NONE:
101 return R_NONE;
102 default:
103 Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << type.v
104 << ") against symbol " << &s;
105 return R_NONE;
106 }
107}
108
109void X86::writeGotPltHeader(uint8_t *buf) const {
110 write32le(P: buf, V: ctx.in.dynamic->getVA());
111}
112
113void X86::writeGotPlt(uint8_t *buf, const Symbol &s) const {
114 // Entries in .got.plt initially points back to the corresponding
115 // PLT entries with a fixed offset to skip the first instruction.
116 write32le(P: buf, V: s.getPltVA(ctx) + 6);
117}
118
119void X86::writeIgotPlt(uint8_t *buf, const Symbol &s) const {
120 // An x86 entry is the address of the ifunc resolver function.
121 write32le(P: buf, V: s.getVA(ctx));
122}
123
124RelType X86::getDynRel(RelType type) const {
125 if (type == R_386_TLS_LE)
126 return R_386_TLS_TPOFF;
127 if (type == R_386_TLS_LE_32)
128 return R_386_TLS_TPOFF32;
129 return type;
130}
131
132void X86::writePltHeader(uint8_t *buf) const {
133 if (ctx.arg.isPic) {
134 const uint8_t v[] = {
135 0xff, 0xb3, 0x04, 0x00, 0x00, 0x00, // pushl 4(%ebx)
136 0xff, 0xa3, 0x08, 0x00, 0x00, 0x00, // jmp *8(%ebx)
137 0x90, 0x90, 0x90, 0x90 // nop
138 };
139 memcpy(dest: buf, src: v, n: sizeof(v));
140 return;
141 }
142
143 const uint8_t pltData[] = {
144 0xff, 0x35, 0, 0, 0, 0, // pushl (GOTPLT+4)
145 0xff, 0x25, 0, 0, 0, 0, // jmp *(GOTPLT+8)
146 0x90, 0x90, 0x90, 0x90, // nop
147 };
148 memcpy(dest: buf, src: pltData, n: sizeof(pltData));
149 uint32_t gotPlt = ctx.in.gotPlt->getVA();
150 write32le(P: buf + 2, V: gotPlt + 4);
151 write32le(P: buf + 8, V: gotPlt + 8);
152}
153
154void X86::writePlt(uint8_t *buf, const Symbol &sym,
155 uint64_t pltEntryAddr) const {
156 unsigned relOff = ctx.in.relaPlt->entsize * sym.getPltIdx(ctx);
157 if (ctx.arg.isPic) {
158 const uint8_t inst[] = {
159 0xff, 0xa3, 0, 0, 0, 0, // jmp *foo@GOT(%ebx)
160 0x68, 0, 0, 0, 0, // pushl $reloc_offset
161 0xe9, 0, 0, 0, 0, // jmp .PLT0@PC
162 };
163 memcpy(dest: buf, src: inst, n: sizeof(inst));
164 write32le(P: buf + 2, V: sym.getGotPltVA(ctx) - ctx.in.gotPlt->getVA());
165 } else {
166 const uint8_t inst[] = {
167 0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
168 0x68, 0, 0, 0, 0, // pushl $reloc_offset
169 0xe9, 0, 0, 0, 0, // jmp .PLT0@PC
170 };
171 memcpy(dest: buf, src: inst, n: sizeof(inst));
172 write32le(P: buf + 2, V: sym.getGotPltVA(ctx));
173 }
174
175 write32le(P: buf + 7, V: relOff);
176 write32le(P: buf + 12, V: ctx.in.plt->getVA() - pltEntryAddr - 16);
177}
178
179template <class ELFT, class RelTy>
180void X86::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
181 unsigned shard) {
182 RelocScan rs(ctx, &sec, shard);
183 sec.relocations.reserve(N: rels.size());
184
185 for (auto it = rels.begin(); it != rels.end(); ++it) {
186 const RelTy &rel = *it;
187 uint32_t symIdx = rel.getSymbol(false);
188 Symbol &sym = sec.getFile<ELFT>()->getSymbol(symIdx);
189 uint64_t offset = rel.r_offset;
190 RelType type = rel.getType(false);
191 if (sym.isUndefined() && symIdx != 0 &&
192 rs.maybeReportUndefined(sym&: cast<Undefined>(Val&: sym), offset))
193 continue;
194 int64_t addend = rs.getAddend<ELFT>(rel, type);
195 RelExpr expr;
196 switch (type) {
197 case R_386_NONE:
198 continue;
199
200 // Absolute relocations:
201 case R_386_8:
202 case R_386_16:
203 case R_386_32:
204 expr = R_ABS;
205 break;
206
207 // PC-relative relocations:
208 case R_386_PC8:
209 case R_386_PC16:
210 case R_386_PC32:
211 rs.processR_PC(type, offset, addend, sym);
212 continue;
213
214 // PLT-generating relocation:
215 case R_386_PLT32:
216 rs.processR_PLT_PC(type, offset, addend, sym);
217 continue;
218
219 // GOT-related relocations:
220 case R_386_GOTPC:
221 ctx.in.gotPlt->hasGotPltOffRel.store(i: true, m: std::memory_order_relaxed);
222 expr = R_GOTPLTONLY_PC;
223 break;
224 case R_386_GOTOFF:
225 ctx.in.gotPlt->hasGotPltOffRel.store(i: true, m: std::memory_order_relaxed);
226 expr = R_GOTPLTREL;
227 break;
228 case R_386_GOT32:
229 case R_386_GOT32X:
230 // R_386_GOT32(X) is used for both absolute GOT access (foo@GOT,
231 // non-PIC, G + A => R_GOT) and register-relative GOT access
232 // (foo@GOT(%ebx), PIC, G + A - GOT => R_GOTPLT). Both use the same
233 // relocation type, so we check the ModRM byte to distinguish them.
234 expr = offset && (sec.content().data()[offset - 1] & 0xc7) == 0x5
235 ? R_GOT
236 : R_GOTPLT;
237 if (expr == R_GOTPLT)
238 ctx.in.gotPlt->hasGotPltOffRel.store(i: true, m: std::memory_order_relaxed);
239 break;
240
241 // TLS relocations:
242 case R_386_TLS_LE:
243 if (rs.checkTlsLe(offset, sym, type))
244 continue;
245 expr = R_TPREL;
246 break;
247 case R_386_TLS_LE_32:
248 if (rs.checkTlsLe(offset, sym, type))
249 continue;
250 expr = R_TPREL_NEG;
251 break;
252 case R_386_TLS_IE:
253 rs.handleTlsIe(ieExpr: R_GOT, type, offset, addend, sym);
254 continue;
255 case R_386_TLS_GOTIE:
256 ctx.in.gotPlt->hasGotPltOffRel.store(i: true, m: std::memory_order_relaxed);
257 rs.handleTlsIe(ieExpr: R_GOTPLT, type, offset, addend, sym);
258 continue;
259 case R_386_TLS_GD:
260 ctx.in.gotPlt->hasGotPltOffRel.store(i: true, m: std::memory_order_relaxed);
261 // Use R_TPREL_NEG for negative TP offset.
262 if (rs.handleTlsGd(sharedExpr: R_TLSGD_GOTPLT, ieExpr: R_GOTPLT, leExpr: R_TPREL_NEG, type, offset,
263 addend, sym))
264 ++it;
265 continue;
266 case R_386_TLS_LDM:
267 ctx.in.gotPlt->hasGotPltOffRel.store(i: true, m: std::memory_order_relaxed);
268 if (rs.handleTlsLd(sharedExpr: R_TLSLD_GOTPLT, type, offset, addend, sym))
269 ++it;
270 continue;
271 case R_386_TLS_LDO_32:
272 sec.addReloc(
273 r: {.expr: ctx.arg.shared ? R_DTPREL : R_TPREL, .type: type, .offset: offset, .addend: addend, .sym: &sym});
274 continue;
275 case R_386_TLS_GOTDESC:
276 ctx.in.gotPlt->hasGotPltOffRel.store(i: true, m: std::memory_order_relaxed);
277 rs.handleTlsDesc(sharedExpr: R_TLSDESC_GOTPLT, ieExpr: R_GOTPLT, type, offset, addend, sym);
278 continue;
279 case R_386_TLS_DESC_CALL:
280 // For executables, TLSDESC is optimized to IE or LE. Use R_TPREL as the
281 // rewrites for this relocation are identical.
282 if (!ctx.arg.shared)
283 sec.addReloc(r: {.expr: R_TPREL, .type: type, .offset: offset, .addend: addend, .sym: &sym});
284 continue;
285
286 default:
287 Err(ctx) << getErrorLoc(ctx, loc: sec.content().data() + offset)
288 << "unknown relocation (" << type.v << ") against symbol "
289 << &sym;
290 continue;
291 }
292 rs.process(expr, type, offset, sym, addend);
293 }
294}
295
296void X86::scanSection(InputSectionBase &sec, unsigned shard) {
297 elf::scanSection1<X86, ELF32LE>(target&: *this, sec, shard);
298}
299
300int64_t X86::getImplicitAddend(const uint8_t *buf, RelType type) const {
301 switch (type) {
302 case R_386_8:
303 case R_386_PC8:
304 return SignExtend64<8>(x: *buf);
305 case R_386_16:
306 case R_386_PC16:
307 return SignExtend64<16>(x: read16le(P: buf));
308 case R_386_32:
309 case R_386_GLOB_DAT:
310 case R_386_GOT32:
311 case R_386_GOT32X:
312 case R_386_GOTOFF:
313 case R_386_GOTPC:
314 case R_386_IRELATIVE:
315 case R_386_PC32:
316 case R_386_PLT32:
317 case R_386_RELATIVE:
318 case R_386_TLS_GOTDESC:
319 case R_386_TLS_DESC_CALL:
320 case R_386_TLS_DTPMOD32:
321 case R_386_TLS_DTPOFF32:
322 case R_386_TLS_LDO_32:
323 case R_386_TLS_LDM:
324 case R_386_TLS_IE:
325 case R_386_TLS_IE_32:
326 case R_386_TLS_LE:
327 case R_386_TLS_LE_32:
328 case R_386_TLS_GD:
329 case R_386_TLS_GD_32:
330 case R_386_TLS_GOTIE:
331 case R_386_TLS_TPOFF:
332 case R_386_TLS_TPOFF32:
333 return SignExtend64<32>(x: read32le(P: buf));
334 case R_386_TLS_DESC:
335 return SignExtend64<32>(x: read32le(P: buf + 4));
336 case R_386_NONE:
337 case R_386_JUMP_SLOT:
338 // These relocations are defined as not having an implicit addend.
339 return 0;
340 default:
341 InternalErr(ctx, buf) << "cannot read addend for relocation " << type;
342 return 0;
343 }
344}
345
346void X86::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
347 switch (rel.type) {
348 case R_386_8:
349 // R_386_{PC,}{8,16} are not part of the i386 psABI, but they are
350 // being used for some 16-bit programs such as boot loaders, so
351 // we want to support them.
352 checkIntUInt(ctx, loc, v: val, n: 8, rel);
353 *loc = val;
354 break;
355 case R_386_PC8:
356 checkInt(ctx, loc, v: val, n: 8, rel);
357 *loc = val;
358 break;
359 case R_386_16:
360 checkIntUInt(ctx, loc, v: val, n: 16, rel);
361 write16le(P: loc, V: val);
362 break;
363 case R_386_PC16:
364 // R_386_PC16 is normally used with 16 bit code. In that situation
365 // the PC is 16 bits, just like the addend. This means that it can
366 // point from any 16 bit address to any other if the possibility
367 // of wrapping is included.
368 // The only restriction we have to check then is that the destination
369 // address fits in 16 bits. That is impossible to do here. The problem is
370 // that we are passed the final value, which already had the
371 // current location subtracted from it.
372 // We just check that Val fits in 17 bits. This misses some cases, but
373 // should have no false positives.
374 checkInt(ctx, loc, v: val, n: 17, rel);
375 write16le(P: loc, V: val);
376 break;
377 case R_386_32:
378 case R_386_GOT32:
379 case R_386_GOT32X:
380 case R_386_GOTOFF:
381 case R_386_GOTPC:
382 case R_386_PC32:
383 case R_386_PLT32:
384 case R_386_RELATIVE:
385 case R_386_TLS_GOTDESC:
386 case R_386_TLS_DESC_CALL:
387 case R_386_TLS_DTPMOD32:
388 case R_386_TLS_DTPOFF32:
389 case R_386_TLS_GD:
390 case R_386_TLS_GOTIE:
391 case R_386_TLS_IE:
392 case R_386_TLS_LDM:
393 case R_386_TLS_LDO_32:
394 case R_386_TLS_LE:
395 case R_386_TLS_LE_32:
396 case R_386_TLS_TPOFF:
397 case R_386_TLS_TPOFF32:
398 checkInt(ctx, loc, v: val, n: 32, rel);
399 write32le(P: loc, V: val);
400 break;
401 case R_386_TLS_DESC:
402 // The addend is stored in the second 32-bit word.
403 write32le(P: loc + 4, V: val);
404 break;
405 default:
406 llvm_unreachable("unknown relocation");
407 }
408}
409
410void X86::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
411 uint64_t val) const {
412 if (rel.type == R_386_TLS_GD) {
413 // Convert (loc[-2] == 0x04)
414 // leal x@tlsgd(, %ebx, 1), %eax
415 // call ___tls_get_addr@plt
416 // or
417 // leal x@tlsgd(%reg), %eax
418 // call *___tls_get_addr@got(%reg)
419 // to
420 const uint8_t inst[] = {
421 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax
422 0x81, 0xe8, 0, 0, 0, 0, // subl x@ntpoff(%ebx), %eax
423 };
424 uint8_t *w = loc[-2] == 0x04 ? loc - 3 : loc - 2;
425 memcpy(dest: w, src: inst, n: sizeof(inst));
426 write32le(P: w + 8, V: val);
427 } else if (rel.type == R_386_TLS_GOTDESC) {
428 // Convert leal x@tlsdesc(%ebx), %eax to leal x@ntpoff, %eax.
429 //
430 // Note: call *x@tlsdesc(%eax) may not immediately follow this instruction.
431 if (memcmp(s1: loc - 2, s2: "\x8d\x83", n: 2)) {
432 ErrAlways(ctx)
433 << getErrorLoc(ctx, loc: loc - 2)
434 << "R_386_TLS_GOTDESC must be used in leal x@tlsdesc(%ebx), %eax";
435 return;
436 }
437 loc[-1] = 0x05;
438 write32le(P: loc, V: val);
439 } else {
440 // Convert call *x@tlsdesc(%eax) to xchg ax, ax.
441 assert(rel.type == R_386_TLS_DESC_CALL);
442 loc[0] = 0x66;
443 loc[1] = 0x90;
444 }
445}
446
447void X86::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
448 uint64_t val) const {
449 if (rel.type == R_386_TLS_GD) {
450 // Convert (loc[-2] == 0x04)
451 // leal x@tlsgd(, %ebx, 1), %eax
452 // call ___tls_get_addr@plt
453 // or
454 // leal x@tlsgd(%reg), %eax
455 // call *___tls_get_addr@got(%reg)
456 const uint8_t inst[] = {
457 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax
458 0x03, 0x83, 0, 0, 0, 0, // addl x@gottpoff(%ebx), %eax
459 };
460 uint8_t *w = loc[-2] == 0x04 ? loc - 3 : loc - 2;
461 memcpy(dest: w, src: inst, n: sizeof(inst));
462 write32le(P: w + 8, V: val);
463 } else if (rel.type == R_386_TLS_GOTDESC) {
464 // Convert leal x@tlsdesc(%ebx), %eax to movl x@gotntpoff(%ebx), %eax.
465 if (memcmp(s1: loc - 2, s2: "\x8d\x83", n: 2)) {
466 ErrAlways(ctx)
467 << getErrorLoc(ctx, loc: loc - 2)
468 << "R_386_TLS_GOTDESC must be used in leal x@tlsdesc(%ebx), %eax";
469 return;
470 }
471 loc[-2] = 0x8b;
472 write32le(P: loc, V: val);
473 }
474}
475
476// In some conditions, relocations can be optimized to avoid using GOT.
477// This function does that for Initial Exec to Local Exec case.
478void X86::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
479 uint64_t val) const {
480 // Ulrich's document section 6.2 says that @gotntpoff can
481 // be used with MOVL or ADDL instructions.
482 // @indntpoff is similar to @gotntpoff, but for use in
483 // position dependent code.
484 uint8_t reg = (loc[-1] >> 3) & 7;
485
486 if (rel.type == R_386_TLS_IE) {
487 if (loc[-1] == 0xa1) {
488 // "movl foo@indntpoff,%eax" -> "movl $foo,%eax"
489 // This case is different from the generic case below because
490 // this is a 5 byte instruction while below is 6 bytes.
491 loc[-1] = 0xb8;
492 } else if (loc[-2] == 0x8b) {
493 // "movl foo@indntpoff,%reg" -> "movl $foo,%reg"
494 loc[-2] = 0xc7;
495 loc[-1] = 0xc0 | reg;
496 } else {
497 // "addl foo@indntpoff,%reg" -> "addl $foo,%reg"
498 loc[-2] = 0x81;
499 loc[-1] = 0xc0 | reg;
500 }
501 } else {
502 assert(rel.type == R_386_TLS_GOTIE);
503 if (loc[-2] == 0x8b) {
504 // "movl foo@gottpoff(%rip),%reg" -> "movl $foo,%reg"
505 loc[-2] = 0xc7;
506 loc[-1] = 0xc0 | reg;
507 } else {
508 // "addl foo@gotntpoff(%rip),%reg" -> "leal foo(%reg),%reg"
509 loc[-2] = 0x8d;
510 loc[-1] = 0x80 | (reg << 3) | reg;
511 }
512 }
513 write32le(P: loc, V: val);
514}
515
516void X86::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
517 uint64_t val) const {
518 if (rel.type == R_386_TLS_LDO_32) {
519 write32le(P: loc, V: val);
520 return;
521 }
522
523 if (loc[4] == 0xe8) {
524 // Convert
525 // leal x(%reg),%eax
526 // call ___tls_get_addr@plt
527 // to
528 const uint8_t inst[] = {
529 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0,%eax
530 0x90, // nop
531 0x8d, 0x74, 0x26, 0x00, // leal 0(%esi,1),%esi
532 };
533 memcpy(dest: loc - 2, src: inst, n: sizeof(inst));
534 return;
535 }
536
537 // Convert
538 // leal x(%reg),%eax
539 // call *___tls_get_addr@got(%reg)
540 // to
541 const uint8_t inst[] = {
542 0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0,%eax
543 0x8d, 0xb6, 0x00, 0x00, 0x00, 0x00, // leal (%esi),%esi
544 };
545 memcpy(dest: loc - 2, src: inst, n: sizeof(inst));
546}
547
548void X86::relocateAlloc(InputSection &sec, uint8_t *buf) const {
549 uint64_t secAddr = sec.getOutputSection()->addr + sec.outSecOff;
550 for (const Relocation &rel : sec.relocs()) {
551 uint8_t *loc = buf + rel.offset;
552 const uint64_t val =
553 SignExtend64(X: sec.getRelocTargetVA(ctx, r: rel, p: secAddr + rel.offset), B: 32);
554 switch (rel.type) {
555 case R_386_TLS_GD:
556 case R_386_TLS_GOTDESC:
557 case R_386_TLS_DESC_CALL:
558 if (rel.expr == R_TPREL || rel.expr == R_TPREL_NEG)
559 relaxTlsGdToLe(loc, rel, val);
560 else if (rel.expr == R_GOTPLT)
561 relaxTlsGdToIe(loc, rel, val);
562 else
563 relocate(loc, rel, val);
564 continue;
565 case R_386_TLS_LDM:
566 case R_386_TLS_LDO_32:
567 if (rel.expr == R_TPREL)
568 relaxTlsLdToLe(loc, rel, val);
569 else
570 relocate(loc, rel, val);
571 continue;
572 case R_386_TLS_IE:
573 case R_386_TLS_GOTIE:
574 if (rel.expr == R_TPREL)
575 relaxTlsIeToLe(loc, rel, val);
576 else
577 relocate(loc, rel, val);
578 continue;
579 default:
580 relocate(loc, rel, val);
581 break;
582 }
583 }
584}
585
586// If Intel Indirect Branch Tracking is enabled, we have to emit special PLT
587// entries containing endbr32 instructions. A PLT entry will be split into two
588// parts, one in .plt.sec (writePlt), and the other in .plt (writeIBTPlt).
589namespace {
590class IntelIBT : public X86 {
591public:
592 IntelIBT(Ctx &ctx) : X86(ctx) { pltHeaderSize = 0; }
593 void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
594 void writePlt(uint8_t *buf, const Symbol &sym,
595 uint64_t pltEntryAddr) const override;
596 void writeIBTPlt(uint8_t *buf, size_t numEntries) const override;
597
598 static const unsigned IBTPltHeaderSize = 16;
599};
600} // namespace
601
602void IntelIBT::writeGotPlt(uint8_t *buf, const Symbol &s) const {
603 uint64_t va = ctx.in.ibtPlt->getVA() + IBTPltHeaderSize +
604 s.getPltIdx(ctx) * pltEntrySize;
605 write32le(P: buf, V: va);
606}
607
608void IntelIBT::writePlt(uint8_t *buf, const Symbol &sym,
609 uint64_t /*pltEntryAddr*/) const {
610 if (ctx.arg.isPic) {
611 const uint8_t inst[] = {
612 0xf3, 0x0f, 0x1e, 0xfb, // endbr32
613 0xff, 0xa3, 0, 0, 0, 0, // jmp *name@GOT(%ebx)
614 0x66, 0x0f, 0x1f, 0x44, 0, 0, // nop
615 };
616 memcpy(dest: buf, src: inst, n: sizeof(inst));
617 write32le(P: buf + 6, V: sym.getGotPltVA(ctx) - ctx.in.gotPlt->getVA());
618 return;
619 }
620
621 const uint8_t inst[] = {
622 0xf3, 0x0f, 0x1e, 0xfb, // endbr32
623 0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
624 0x66, 0x0f, 0x1f, 0x44, 0, 0, // nop
625 };
626 memcpy(dest: buf, src: inst, n: sizeof(inst));
627 write32le(P: buf + 6, V: sym.getGotPltVA(ctx));
628}
629
630void IntelIBT::writeIBTPlt(uint8_t *buf, size_t numEntries) const {
631 writePltHeader(buf);
632 buf += IBTPltHeaderSize;
633
634 const uint8_t inst[] = {
635 0xf3, 0x0f, 0x1e, 0xfb, // endbr32
636 0x68, 0, 0, 0, 0, // pushl $reloc_offset
637 0xe9, 0, 0, 0, 0, // jmpq .PLT0@PC
638 0x66, 0x90, // nop
639 };
640
641 for (size_t i = 0; i < numEntries; ++i) {
642 memcpy(dest: buf, src: inst, n: sizeof(inst));
643 write32le(P: buf + 5, V: i * sizeof(object::ELF32LE::Rel));
644 write32le(P: buf + 10, V: -pltHeaderSize - sizeof(inst) * i - 30);
645 buf += sizeof(inst);
646 }
647}
648
649namespace {
650class RetpolinePic : public X86 {
651public:
652 RetpolinePic(Ctx &);
653 void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
654 void writePltHeader(uint8_t *buf) const override;
655 void writePlt(uint8_t *buf, const Symbol &sym,
656 uint64_t pltEntryAddr) const override;
657};
658
659class RetpolineNoPic : public X86 {
660public:
661 RetpolineNoPic(Ctx &);
662 void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
663 void writePltHeader(uint8_t *buf) const override;
664 void writePlt(uint8_t *buf, const Symbol &sym,
665 uint64_t pltEntryAddr) const override;
666};
667} // namespace
668
669RetpolinePic::RetpolinePic(Ctx &ctx) : X86(ctx) {
670 pltHeaderSize = 48;
671 pltEntrySize = 32;
672 ipltEntrySize = 32;
673}
674
675void RetpolinePic::writeGotPlt(uint8_t *buf, const Symbol &s) const {
676 write32le(P: buf, V: s.getPltVA(ctx) + 17);
677}
678
679void RetpolinePic::writePltHeader(uint8_t *buf) const {
680 const uint8_t insn[] = {
681 0xff, 0xb3, 4, 0, 0, 0, // 0: pushl 4(%ebx)
682 0x50, // 6: pushl %eax
683 0x8b, 0x83, 8, 0, 0, 0, // 7: mov 8(%ebx), %eax
684 0xe8, 0x0e, 0x00, 0x00, 0x00, // d: call next
685 0xf3, 0x90, // 12: loop: pause
686 0x0f, 0xae, 0xe8, // 14: lfence
687 0xeb, 0xf9, // 17: jmp loop
688 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 19: int3; .align 16
689 0x89, 0x0c, 0x24, // 20: next: mov %ecx, (%esp)
690 0x8b, 0x4c, 0x24, 0x04, // 23: mov 0x4(%esp), %ecx
691 0x89, 0x44, 0x24, 0x04, // 27: mov %eax ,0x4(%esp)
692 0x89, 0xc8, // 2b: mov %ecx, %eax
693 0x59, // 2d: pop %ecx
694 0xc3, // 2e: ret
695 0xcc, // 2f: int3; padding
696 };
697 memcpy(dest: buf, src: insn, n: sizeof(insn));
698}
699
700void RetpolinePic::writePlt(uint8_t *buf, const Symbol &sym,
701 uint64_t pltEntryAddr) const {
702 unsigned relOff = ctx.in.relaPlt->entsize * sym.getPltIdx(ctx);
703 const uint8_t insn[] = {
704 0x50, // pushl %eax
705 0x8b, 0x83, 0, 0, 0, 0, // mov foo@GOT(%ebx), %eax
706 0xe8, 0, 0, 0, 0, // call plt+0x20
707 0xe9, 0, 0, 0, 0, // jmp plt+0x12
708 0x68, 0, 0, 0, 0, // pushl $reloc_offset
709 0xe9, 0, 0, 0, 0, // jmp plt+0
710 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // int3; padding
711 };
712 memcpy(dest: buf, src: insn, n: sizeof(insn));
713
714 uint32_t ebx = ctx.in.gotPlt->getVA();
715 unsigned off = pltEntryAddr - ctx.in.plt->getVA();
716 write32le(P: buf + 3, V: sym.getGotPltVA(ctx) - ebx);
717 write32le(P: buf + 8, V: -off - 12 + 32);
718 write32le(P: buf + 13, V: -off - 17 + 18);
719 write32le(P: buf + 18, V: relOff);
720 write32le(P: buf + 23, V: -off - 27);
721}
722
723RetpolineNoPic::RetpolineNoPic(Ctx &ctx) : X86(ctx) {
724 pltHeaderSize = 48;
725 pltEntrySize = 32;
726 ipltEntrySize = 32;
727}
728
729void RetpolineNoPic::writeGotPlt(uint8_t *buf, const Symbol &s) const {
730 write32le(P: buf, V: s.getPltVA(ctx) + 16);
731}
732
733void RetpolineNoPic::writePltHeader(uint8_t *buf) const {
734 const uint8_t insn[] = {
735 0xff, 0x35, 0, 0, 0, 0, // 0: pushl GOTPLT+4
736 0x50, // 6: pushl %eax
737 0xa1, 0, 0, 0, 0, // 7: mov GOTPLT+8, %eax
738 0xe8, 0x0f, 0x00, 0x00, 0x00, // c: call next
739 0xf3, 0x90, // 11: loop: pause
740 0x0f, 0xae, 0xe8, // 13: lfence
741 0xeb, 0xf9, // 16: jmp loop
742 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 18: int3
743 0xcc, 0xcc, 0xcc, // 1f: int3; .align 16
744 0x89, 0x0c, 0x24, // 20: next: mov %ecx, (%esp)
745 0x8b, 0x4c, 0x24, 0x04, // 23: mov 0x4(%esp), %ecx
746 0x89, 0x44, 0x24, 0x04, // 27: mov %eax ,0x4(%esp)
747 0x89, 0xc8, // 2b: mov %ecx, %eax
748 0x59, // 2d: pop %ecx
749 0xc3, // 2e: ret
750 0xcc, // 2f: int3; padding
751 };
752 memcpy(dest: buf, src: insn, n: sizeof(insn));
753
754 uint32_t gotPlt = ctx.in.gotPlt->getVA();
755 write32le(P: buf + 2, V: gotPlt + 4);
756 write32le(P: buf + 8, V: gotPlt + 8);
757}
758
759void RetpolineNoPic::writePlt(uint8_t *buf, const Symbol &sym,
760 uint64_t pltEntryAddr) const {
761 unsigned relOff = ctx.in.relaPlt->entsize * sym.getPltIdx(ctx);
762 const uint8_t insn[] = {
763 0x50, // 0: pushl %eax
764 0xa1, 0, 0, 0, 0, // 1: mov foo_in_GOT, %eax
765 0xe8, 0, 0, 0, 0, // 6: call plt+0x20
766 0xe9, 0, 0, 0, 0, // b: jmp plt+0x11
767 0x68, 0, 0, 0, 0, // 10: pushl $reloc_offset
768 0xe9, 0, 0, 0, 0, // 15: jmp plt+0
769 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1a: int3; padding
770 0xcc, // 1f: int3; padding
771 };
772 memcpy(dest: buf, src: insn, n: sizeof(insn));
773
774 unsigned off = pltEntryAddr - ctx.in.plt->getVA();
775 write32le(P: buf + 2, V: sym.getGotPltVA(ctx));
776 write32le(P: buf + 7, V: -off - 11 + 32);
777 write32le(P: buf + 12, V: -off - 16 + 17);
778 write32le(P: buf + 17, V: relOff);
779 write32le(P: buf + 22, V: -off - 26);
780}
781
782void elf::setX86TargetInfo(Ctx &ctx) {
783 if (ctx.arg.zRetpolineplt) {
784 if (ctx.arg.isPic)
785 ctx.target.reset(p: new RetpolinePic(ctx));
786 else
787 ctx.target.reset(p: new RetpolineNoPic(ctx));
788 return;
789 }
790
791 if (ctx.arg.andFeatures & GNU_PROPERTY_X86_FEATURE_1_IBT)
792 ctx.target.reset(p: new IntelIBT(ctx));
793 else
794 ctx.target.reset(p: new X86(ctx));
795}
796