Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/lld/MachO/Arch/X86_64.cpp
34889 views
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 "mach-o/compact_unwind_encoding.h"
16
#include "llvm/BinaryFormat/MachO.h"
17
#include "llvm/Support/Endian.h"
18
19
using namespace llvm::MachO;
20
using namespace llvm::support::endian;
21
using namespace lld;
22
using namespace lld::macho;
23
24
namespace {
25
26
struct X86_64 : TargetInfo {
27
X86_64();
28
29
int64_t getEmbeddedAddend(MemoryBufferRef, uint64_t offset,
30
const relocation_info) const override;
31
void relocateOne(uint8_t *loc, const Reloc &, uint64_t va,
32
uint64_t relocVA) const override;
33
34
void writeStub(uint8_t *buf, const Symbol &,
35
uint64_t pointerVA) const override;
36
void writeStubHelperHeader(uint8_t *buf) const override;
37
void writeStubHelperEntry(uint8_t *buf, const Symbol &,
38
uint64_t entryAddr) const override;
39
40
void writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
41
uint64_t &stubOffset, uint64_t selrefVA,
42
Symbol *objcMsgSend) const override;
43
44
void relaxGotLoad(uint8_t *loc, uint8_t type) const override;
45
uint64_t getPageSize() const override { return 4 * 1024; }
46
47
void handleDtraceReloc(const Symbol *sym, const Reloc &r,
48
uint8_t *loc) const override;
49
};
50
} // namespace
51
52
static constexpr std::array<RelocAttrs, 10> relocAttrsArray{{
53
#define B(x) RelocAttrBits::x
54
{"UNSIGNED",
55
B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4) | B(BYTE8)},
56
{"SIGNED", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},
57
{"BRANCH", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)},
58
{"GOT_LOAD", B(PCREL) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)},
59
{"GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)},
60
{"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4) | B(BYTE8)},
61
{"SIGNED_1", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},
62
{"SIGNED_2", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},
63
{"SIGNED_4", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},
64
{"TLV", B(PCREL) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)},
65
#undef B
66
}};
67
68
static int pcrelOffset(uint8_t type) {
69
switch (type) {
70
case X86_64_RELOC_SIGNED_1:
71
return 1;
72
case X86_64_RELOC_SIGNED_2:
73
return 2;
74
case X86_64_RELOC_SIGNED_4:
75
return 4;
76
default:
77
return 0;
78
}
79
}
80
81
int64_t X86_64::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset,
82
relocation_info rel) const {
83
auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
84
const uint8_t *loc = buf + offset + rel.r_address;
85
86
switch (rel.r_length) {
87
case 2:
88
return static_cast<int32_t>(read32le(loc)) + pcrelOffset(rel.r_type);
89
case 3:
90
return read64le(loc) + pcrelOffset(rel.r_type);
91
default:
92
llvm_unreachable("invalid r_length");
93
}
94
}
95
96
void X86_64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,
97
uint64_t relocVA) const {
98
if (r.pcrel) {
99
uint64_t pc = relocVA + 4 + pcrelOffset(r.type);
100
value -= pc;
101
}
102
103
switch (r.length) {
104
case 2:
105
if (r.type == X86_64_RELOC_UNSIGNED)
106
checkUInt(loc, r, value, 32);
107
else
108
checkInt(loc, r, value, 32);
109
write32le(loc, value);
110
break;
111
case 3:
112
write64le(loc, value);
113
break;
114
default:
115
llvm_unreachable("invalid r_length");
116
}
117
}
118
119
// The following methods emit a number of assembly sequences with RIP-relative
120
// addressing. Note that RIP-relative addressing on X86-64 has the RIP pointing
121
// to the next instruction, not the current instruction, so we always have to
122
// account for the current instruction's size when calculating offsets.
123
// writeRipRelative helps with that.
124
//
125
// bufAddr: The virtual address corresponding to buf[0].
126
// bufOff: The offset within buf of the next instruction.
127
// destAddr: The destination address that the current instruction references.
128
static void writeRipRelative(SymbolDiagnostic d, uint8_t *buf, uint64_t bufAddr,
129
uint64_t bufOff, uint64_t destAddr) {
130
uint64_t rip = bufAddr + bufOff;
131
checkInt(buf, d, destAddr - rip, 32);
132
// For the instructions we care about, the RIP-relative address is always
133
// stored in the last 4 bytes of the instruction.
134
write32le(buf + bufOff - 4, destAddr - rip);
135
}
136
137
static constexpr uint8_t stub[] = {
138
0xff, 0x25, 0, 0, 0, 0, // jmpq *__la_symbol_ptr(%rip)
139
};
140
141
void X86_64::writeStub(uint8_t *buf, const Symbol &sym,
142
uint64_t pointerVA) const {
143
memcpy(buf, stub, 2); // just copy the two nonzero bytes
144
uint64_t stubAddr = in.stubs->addr + sym.stubsIndex * sizeof(stub);
145
writeRipRelative({&sym, "stub"}, buf, stubAddr, sizeof(stub), pointerVA);
146
}
147
148
static constexpr uint8_t stubHelperHeader[] = {
149
0x4c, 0x8d, 0x1d, 0, 0, 0, 0, // 0x0: leaq ImageLoaderCache(%rip), %r11
150
0x41, 0x53, // 0x7: pushq %r11
151
0xff, 0x25, 0, 0, 0, 0, // 0x9: jmpq *dyld_stub_binder@GOT(%rip)
152
0x90, // 0xf: nop
153
};
154
155
void X86_64::writeStubHelperHeader(uint8_t *buf) const {
156
memcpy(buf, stubHelperHeader, sizeof(stubHelperHeader));
157
SymbolDiagnostic d = {nullptr, "stub helper header"};
158
writeRipRelative(d, buf, in.stubHelper->addr, 7,
159
in.imageLoaderCache->getVA());
160
writeRipRelative(d, buf, in.stubHelper->addr, 0xf,
161
in.got->addr +
162
in.stubHelper->stubBinder->gotIndex * LP64::wordSize);
163
}
164
165
static constexpr uint8_t stubHelperEntry[] = {
166
0x68, 0, 0, 0, 0, // 0x0: pushq <bind offset>
167
0xe9, 0, 0, 0, 0, // 0x5: jmp <__stub_helper>
168
};
169
170
void X86_64::writeStubHelperEntry(uint8_t *buf, const Symbol &sym,
171
uint64_t entryAddr) const {
172
memcpy(buf, stubHelperEntry, sizeof(stubHelperEntry));
173
write32le(buf + 1, sym.lazyBindOffset);
174
writeRipRelative({&sym, "stub helper"}, buf, entryAddr,
175
sizeof(stubHelperEntry), in.stubHelper->addr);
176
}
177
178
static constexpr uint8_t objcStubsFastCode[] = {
179
0x48, 0x8b, 0x35, 0, 0, 0, 0, // 0x0: movq selrefs@selector(%rip), %rsi
180
0xff, 0x25, 0, 0, 0, 0, // 0x7: jmpq *_objc_msgSend@GOT(%rip)
181
};
182
183
void X86_64::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
184
uint64_t &stubOffset, uint64_t selrefVA,
185
Symbol *objcMsgSend) const {
186
uint64_t objcMsgSendAddr = in.got->addr;
187
uint64_t objcMsgSendIndex = objcMsgSend->gotIndex;
188
189
memcpy(buf, objcStubsFastCode, sizeof(objcStubsFastCode));
190
SymbolDiagnostic d = {sym, sym->getName()};
191
uint64_t stubAddr = stubsAddr + stubOffset;
192
writeRipRelative(d, buf, stubAddr, 7, selrefVA);
193
writeRipRelative(d, buf, stubAddr, 0xd,
194
objcMsgSendAddr + objcMsgSendIndex * LP64::wordSize);
195
stubOffset += target->objcStubsFastSize;
196
}
197
198
void X86_64::relaxGotLoad(uint8_t *loc, uint8_t type) const {
199
// Convert MOVQ to LEAQ
200
if (loc[-2] != 0x8b)
201
error(getRelocAttrs(type).name + " reloc requires MOVQ instruction");
202
loc[-2] = 0x8d;
203
}
204
205
X86_64::X86_64() : TargetInfo(LP64()) {
206
cpuType = CPU_TYPE_X86_64;
207
cpuSubtype = CPU_SUBTYPE_X86_64_ALL;
208
209
modeDwarfEncoding = UNWIND_X86_MODE_DWARF;
210
subtractorRelocType = X86_64_RELOC_SUBTRACTOR;
211
unsignedRelocType = X86_64_RELOC_UNSIGNED;
212
213
stubSize = sizeof(stub);
214
stubHelperHeaderSize = sizeof(stubHelperHeader);
215
stubHelperEntrySize = sizeof(stubHelperEntry);
216
217
objcStubsFastSize = sizeof(objcStubsFastCode);
218
objcStubsFastAlignment = 1;
219
220
relocAttrs = {relocAttrsArray.data(), relocAttrsArray.size()};
221
}
222
223
TargetInfo *macho::createX86_64TargetInfo() {
224
static X86_64 t;
225
return &t;
226
}
227
228
void X86_64::handleDtraceReloc(const Symbol *sym, const Reloc &r,
229
uint8_t *loc) const {
230
assert(r.type == X86_64_RELOC_BRANCH);
231
232
if (config->outputType == MH_OBJECT)
233
return;
234
235
if (sym->getName().starts_with("___dtrace_probe")) {
236
// change call site to a NOP
237
loc[-1] = 0x90;
238
write32le(loc, 0x00401F0F);
239
} else if (sym->getName().starts_with("___dtrace_isenabled")) {
240
// change call site to a clear eax
241
loc[-1] = 0x33;
242
write32le(loc, 0x909090C0);
243
} else {
244
error("Unrecognized dtrace symbol prefix: " + toString(*sym));
245
}
246
}
247
248