blob: db7d03648cada55565bb048eb25ef2b075030fb7 [file] [log] [blame]
Nick Kledzik2458bec2014-07-16 19:49:02 +00001//===- lib/FileFormat/MachO/ArchHandler_x86.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 "ArchHandler.h"
11#include "Atoms.h"
12#include "MachONormalizedFileBinaryUtils.h"
13
14#include "llvm/ADT/StringRef.h"
15#include "llvm/ADT/StringSwitch.h"
16#include "llvm/ADT/Triple.h"
17
18#include "llvm/Support/ErrorHandling.h"
19
20using namespace llvm::MachO;
21using namespace lld::mach_o::normalized;
22
23namespace lld {
24namespace mach_o {
25
26class ArchHandler_x86 : public ArchHandler {
27public:
28 ArchHandler_x86();
29 virtual ~ArchHandler_x86();
30
31 const Registry::KindStrings *kindStrings() override { return _sKindStrings; }
32
33 Reference::KindArch kindArch() override { return Reference::KindArch::x86; }
34
35 const StubInfo &stubInfo() override { return _sStubInfo; }
36 bool isCallSite(const Reference &) override;
37 bool isPointer(const Reference &) override;
38 bool isPairedReloc(const normalized::Relocation &) override;
39 std::error_code getReferenceInfo(const normalized::Relocation &reloc,
40 const DefinedAtom *inAtom,
41 uint32_t offsetInAtom,
42 uint64_t fixupAddress, bool swap,
43 FindAtomBySectionAndAddress atomFromAddress,
44 FindAtomBySymbolIndex atomFromSymbolIndex,
45 Reference::KindValue *kind,
46 const lld::Atom **target,
47 Reference::Addend *addend) override;
48 std::error_code
49 getPairReferenceInfo(const normalized::Relocation &reloc1,
50 const normalized::Relocation &reloc2,
51 const DefinedAtom *inAtom,
52 uint32_t offsetInAtom,
53 uint64_t fixupAddress, bool swap,
54 FindAtomBySectionAndAddress atomFromAddress,
55 FindAtomBySymbolIndex atomFromSymbolIndex,
56 Reference::KindValue *kind,
57 const lld::Atom **target,
58 Reference::Addend *addend) override;
59
60 void applyFixup(Reference::KindNamespace ns, Reference::KindArch arch,
61 Reference::KindValue kindValue, uint64_t addend,
62 uint8_t *location, uint64_t fixupAddress,
63 uint64_t targetAddress, uint64_t inAtomAddress)
64 override;
65
66private:
67 static const Registry::KindStrings _sKindStrings[];
68 static const StubInfo _sStubInfo;
69
70 enum : Reference::KindValue {
71 invalid, /// for error condition
72
73 // Kinds found in mach-o .o files:
74 branch32, /// ex: call _foo
75 branch16, /// ex: callw _foo
76 abs32, /// ex: movl _foo, %eax
77 funcRel32, /// ex: movl _foo-L1(%eax), %eax
78 pointer32, /// ex: .long _foo
79 delta32, /// ex: .long _foo - .
80
81 // Kinds introduced by Passes:
82 lazyPointer, /// Location contains a lazy pointer.
83 lazyImmediateLocation, /// Location contains immediate value used in stub.
84 };
85
86 const bool _swap;
87};
88
89//===----------------------------------------------------------------------===//
90// ArchHandler_x86
91//===----------------------------------------------------------------------===//
92
93ArchHandler_x86::ArchHandler_x86() :
94 _swap(!MachOLinkingContext::isHostEndian(MachOLinkingContext::arch_x86)) {}
95
96ArchHandler_x86::~ArchHandler_x86() { }
97
98const Registry::KindStrings ArchHandler_x86::_sKindStrings[] = {
99 LLD_KIND_STRING_ENTRY(invalid),
100 LLD_KIND_STRING_ENTRY(branch32),
101 LLD_KIND_STRING_ENTRY(branch16),
102 LLD_KIND_STRING_ENTRY(abs32),
103 LLD_KIND_STRING_ENTRY(funcRel32),
104 LLD_KIND_STRING_ENTRY(pointer32),
105 LLD_KIND_STRING_ENTRY(delta32),
106 LLD_KIND_STRING_ENTRY(lazyPointer),
107 LLD_KIND_STRING_ENTRY(lazyImmediateLocation),
108 LLD_KIND_STRING_END
109};
110
111const ArchHandler::StubInfo ArchHandler_x86::_sStubInfo = {
112 "dyld_stub_binder",
113
114 // Lazy pointer references
115 { Reference::KindArch::x86, pointer32, 0, 0 },
116 { Reference::KindArch::x86, lazyPointer, 0, 0 },
117
118 // GOT pointer to dyld_stub_binder
119 { Reference::KindArch::x86, pointer32, 0, 0 },
120
121 // x86 code alignment
122 1,
123
124 // Stub size and code
125 6,
126 { 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 }, // jmp *lazyPointer
127 { Reference::KindArch::x86, abs32, 2, 0 },
128
129 // Stub Helper size and code
130 10,
131 { 0x68, 0x00, 0x00, 0x00, 0x00, // pushl $lazy-info-offset
132 0xE9, 0x00, 0x00, 0x00, 0x00 }, // jmp helperhelper
133 { Reference::KindArch::x86, lazyImmediateLocation, 1, 0 },
134 { Reference::KindArch::x86, branch32, 6, 0 },
135
136 // Stub Helper-Common size and code
137 12,
138 { 0x68, 0x00, 0x00, 0x00, 0x00, // pushl $dyld_ImageLoaderCache
139 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *_fast_lazy_bind
140 0x90 }, // nop
141 { Reference::KindArch::x86, abs32, 1, 0 },
142 { Reference::KindArch::x86, abs32, 7, 0 }
143};
144
145bool ArchHandler_x86::isCallSite(const Reference &ref) {
146 return (ref.kindValue() == branch32);
147}
148
149bool ArchHandler_x86::isPointer(const Reference &ref) {
150 return (ref.kindValue() == pointer32);
151}
152
153bool ArchHandler_x86::isPairedReloc(const Relocation &reloc) {
154 if (!reloc.scattered)
155 return false;
156 return (reloc.type == GENERIC_RELOC_LOCAL_SECTDIFF) ||
157 (reloc.type == GENERIC_RELOC_SECTDIFF);
158}
159
160std::error_code
161ArchHandler_x86::getReferenceInfo(const Relocation &reloc,
162 const DefinedAtom *inAtom,
163 uint32_t offsetInAtom,
164 uint64_t fixupAddress, bool swap,
165 FindAtomBySectionAndAddress atomFromAddress,
166 FindAtomBySymbolIndex atomFromSymbolIndex,
167 Reference::KindValue *kind,
168 const lld::Atom **target,
169 Reference::Addend *addend) {
170 typedef std::error_code E;
171 DefinedAtom::ContentPermissions perms;
172 const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
173 uint64_t targetAddress;
174 switch (relocPattern(reloc)) {
175 case GENERIC_RELOC_VANILLA | rPcRel | rExtern | rLength4:
176 // ex: call _foo (and _foo undefined)
177 *kind = branch32;
178 if (E ec = atomFromSymbolIndex(reloc.symbol, target))
179 return ec;
180 *addend = fixupAddress + 4 + readS32(swap, fixupContent);
181 break;
182 case GENERIC_RELOC_VANILLA | rPcRel | rLength4:
183 // ex: call _foo (and _foo defined)
184 *kind = branch32;
185 targetAddress = fixupAddress + 4 + readS32(swap, fixupContent);
186 return atomFromAddress(reloc.symbol, targetAddress, target, addend);
187 break;
188 case GENERIC_RELOC_VANILLA | rPcRel | rExtern | rLength2:
189 // ex: callw _foo (and _foo undefined)
190 *kind = branch16;
191 if (E ec = atomFromSymbolIndex(reloc.symbol, target))
192 return ec;
193 *addend = fixupAddress + 2 + readS16(swap, fixupContent);
194 break;
195 case GENERIC_RELOC_VANILLA | rPcRel | rLength2:
196 // ex: callw _foo (and _foo defined)
197 *kind = branch16;
198 targetAddress = fixupAddress + 2 + readS16(swap, fixupContent);
199 return atomFromAddress(reloc.symbol, targetAddress, target, addend);
200 break;
201 case GENERIC_RELOC_VANILLA | rExtern | rLength4:
202 // ex: movl _foo, %eax (and _foo undefined)
203 // ex: .long _foo (and _foo undefined)
204 perms = inAtom->permissions();
205 *kind =
206 ((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) ? abs32
207 : pointer32;
208 if (E ec = atomFromSymbolIndex(reloc.symbol, target))
209 return ec;
210 *addend = readU32(swap, fixupContent);
211 break;
212 case GENERIC_RELOC_VANILLA | rLength4:
213 // ex: movl _foo, %eax (and _foo defined)
214 // ex: .long _foo (and _foo defined)
215 perms = inAtom->permissions();
216 *kind =
217 ((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) ? abs32
218 : pointer32;
219 targetAddress = readU32(swap, fixupContent);
220 return atomFromAddress(reloc.symbol, targetAddress, target, addend);
221 break;
222 default:
223 return make_dynamic_error_code(Twine("unsupported i386 relocation type"));
224 }
225 return std::error_code();
226}
227
228std::error_code
229ArchHandler_x86::getPairReferenceInfo(const normalized::Relocation &reloc1,
230 const normalized::Relocation &reloc2,
231 const DefinedAtom *inAtom,
232 uint32_t offsetInAtom,
233 uint64_t fixupAddress, bool swap,
234 FindAtomBySectionAndAddress atomFromAddr,
235 FindAtomBySymbolIndex atomFromSymbolIndex,
236 Reference::KindValue *kind,
237 const lld::Atom **target,
238 Reference::Addend *addend) {
239 const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom];
240 std::error_code ec;
241 DefinedAtom::ContentPermissions perms = inAtom->permissions();
242 uint32_t fromAddress;
243 uint32_t toAddress;
244 uint32_t value;
245 const lld::Atom *fromTarget;
246 Reference::Addend offsetInTo;
247 Reference::Addend offsetInFrom;
248 switch (relocPattern(reloc1) << 16 | relocPattern(reloc2)) {
249 case((GENERIC_RELOC_SECTDIFF | rScattered | rLength4) << 16 |
250 GENERIC_RELOC_PAIR | rScattered | rLength4)
251 :
252 case((GENERIC_RELOC_LOCAL_SECTDIFF | rScattered | rLength4) << 16 |
253 GENERIC_RELOC_PAIR | rScattered | rLength4)
254 :
255 toAddress = reloc1.value;
256 fromAddress = reloc2.value;
257 value = readS32(swap, fixupContent);
258 ec = atomFromAddr(0, toAddress, target, &offsetInTo);
259 if (ec)
260 return ec;
261 ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom);
262 if (ec)
263 return ec;
264 if (fromTarget != inAtom)
265 return make_dynamic_error_code(Twine("SECTDIFF relocation where "
266 "subtrahend label is not in atom"));
267 *kind = ((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) ? funcRel32
268 : delta32;
269 if (*kind == funcRel32) {
270 // SECTDIFF relocations are used in i386 codegen where the function
271 // prolog does a CALL to the next instruction which POPs the return
272 // address into EBX which becomes the pic-base register. The POP
273 // instruction is label the used for the subtrahend in expressions.
274 // The funcRel32 kind represents the 32-bit delta to some symbol from
275 // the start of the function (atom) containing the funcRel32.
276 uint32_t ta = fromAddress + value - toAddress;
277 *addend = ta - offsetInFrom;
278 } else {
279 *addend = fromAddress + value - toAddress;
280 }
281 return std::error_code();
282 break;
283 default:
284 return make_dynamic_error_code(Twine("unsupported i386 relocation type"));
285 }
286}
287
288void ArchHandler_x86::applyFixup(Reference::KindNamespace ns,
289 Reference::KindArch arch,
290 Reference::KindValue kindValue,
291 uint64_t addend, uint8_t *location,
292 uint64_t fixupAddress, uint64_t targetAddress,
293 uint64_t inAtomAddress) {
294 if (ns != Reference::KindNamespace::mach_o)
295 return;
296 assert(arch == Reference::KindArch::x86);
297 int32_t *loc32 = reinterpret_cast<int32_t *>(location);
298 int16_t *loc16 = reinterpret_cast<int16_t *>(location);
299 switch (kindValue) {
300 case branch32:
301 write32(*loc32, _swap, (targetAddress - (fixupAddress + 4)) + addend);
302 break;
303 case branch16:
304 write16(*loc16, _swap, (targetAddress - (fixupAddress + 4)) + addend);
305 break;
306 case pointer32:
307 case abs32:
308 write32(*loc32, _swap, targetAddress + addend);
309 break;
310 case funcRel32:
311 write32(*loc32, _swap, targetAddress - inAtomAddress + addend); // FIXME
312 break;
313 case delta32:
314 write32(*loc32, _swap, targetAddress - fixupAddress + addend);
315 break;
316 case lazyPointer:
317 case lazyImmediateLocation:
318 // do nothing
319 break;
320 default:
321 llvm_unreachable("invalid x86 Reference Kind");
322 break;
323 }
324}
325
326std::unique_ptr<mach_o::ArchHandler> ArchHandler::create_x86() {
327 return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_x86());
328}
329
330} // namespace mach_o
331} // namespace lld