Line data Source code
1 : //===----- RuntimeDyldMachOARM.h ---- MachO/ARM specific code. ----*- C++ -*-=//
2 : //
3 : // The LLVM Compiler Infrastructure
4 : //
5 : // This file is distributed under the University of Illinois Open Source
6 : // License. See LICENSE.TXT for details.
7 : //
8 : //===----------------------------------------------------------------------===//
9 :
10 : #ifndef LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDMACHOARM_H
11 : #define LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDMACHOARM_H
12 :
13 : #include "../RuntimeDyldMachO.h"
14 : #include <string>
15 :
16 : #define DEBUG_TYPE "dyld"
17 :
18 : namespace llvm {
19 :
20 : class RuntimeDyldMachOARM
21 : : public RuntimeDyldMachOCRTPBase<RuntimeDyldMachOARM> {
22 : private:
23 : typedef RuntimeDyldMachOCRTPBase<RuntimeDyldMachOARM> ParentT;
24 :
25 : public:
26 :
27 : typedef uint32_t TargetPtrT;
28 :
29 : RuntimeDyldMachOARM(RuntimeDyld::MemoryManager &MM,
30 : JITSymbolResolver &Resolver)
31 2 : : RuntimeDyldMachOCRTPBase(MM, Resolver) {}
32 :
33 9 : unsigned getMaxStubSize() override { return 8; }
34 :
35 10 : unsigned getStubAlignment() override { return 4; }
36 :
37 19 : Expected<JITSymbolFlags> getJITSymbolFlags(const SymbolRef &SR) override {
38 19 : auto Flags = RuntimeDyldImpl::getJITSymbolFlags(SR);
39 19 : if (!Flags)
40 : return Flags.takeError();
41 19 : Flags->getTargetFlags() = ARMJITSymbolFlags::fromObjectSymbol(SR);
42 : return Flags;
43 : }
44 :
45 29 : uint64_t modifyAddressBasedOnFlags(uint64_t Addr,
46 : JITSymbolFlags Flags) const override {
47 29 : if (Flags.getTargetFlags() & ARMJITSymbolFlags::Thumb)
48 4 : Addr |= 0x1;
49 29 : return Addr;
50 : }
51 :
52 5 : bool isAddrTargetThumb(unsigned SectionID, uint64_t Offset) {
53 5 : auto TargetObjAddr = Sections[SectionID].getObjAddress() + Offset;
54 32 : for (auto &KV : GlobalSymbolTable) {
55 : auto &Entry = KV.second;
56 : auto SymbolObjAddr =
57 27 : Sections[Entry.getSectionID()].getObjAddress() + Entry.getOffset();
58 27 : if (TargetObjAddr == SymbolObjAddr)
59 5 : return (Entry.getFlags().getTargetFlags() & ARMJITSymbolFlags::Thumb);
60 : }
61 : return false;
62 : }
63 :
64 5 : Expected<int64_t> decodeAddend(const RelocationEntry &RE) const {
65 5 : const SectionEntry &Section = Sections[RE.SectionID];
66 5 : uint8_t *LocalAddress = Section.getAddressWithOffset(RE.Offset);
67 :
68 5 : switch (RE.RelType) {
69 0 : default:
70 0 : return memcpyAddend(RE);
71 3 : case MachO::ARM_RELOC_BR24: {
72 3 : uint32_t Temp = readBytesUnaligned(LocalAddress, 4);
73 : Temp &= 0x00ffffff; // Mask out the opcode.
74 : // Now we've got the shifted immediate, shift by 2, sign extend and ret.
75 : return SignExtend32<26>(Temp << 2);
76 : }
77 :
78 2 : case MachO::ARM_THUMB_RELOC_BR22: {
79 : // This is a pair of instructions whose operands combine to provide 22
80 : // bits of displacement:
81 : // Encoding for high bits 1111 0XXX XXXX XXXX
82 : // Encoding for low bits 1111 1XXX XXXX XXXX
83 2 : uint16_t HighInsn = readBytesUnaligned(LocalAddress, 2);
84 2 : if ((HighInsn & 0xf800) != 0xf000)
85 0 : return make_error<StringError>("Unrecognized thumb branch encoding "
86 : "(BR22 high bits)",
87 0 : inconvertibleErrorCode());
88 :
89 2 : uint16_t LowInsn = readBytesUnaligned(LocalAddress + 2, 2);
90 2 : if ((LowInsn & 0xf800) != 0xf800)
91 0 : return make_error<StringError>("Unrecognized thumb branch encoding "
92 : "(BR22 low bits)",
93 0 : inconvertibleErrorCode());
94 :
95 4 : return SignExtend64<23>(((HighInsn & 0x7ff) << 12) |
96 2 : ((LowInsn & 0x7ff) << 1));
97 : }
98 : }
99 : }
100 :
101 : Expected<relocation_iterator>
102 8 : processRelocationRef(unsigned SectionID, relocation_iterator RelI,
103 : const ObjectFile &BaseObjT,
104 : ObjSectionToIDMap &ObjSectionToID,
105 : StubMap &Stubs) override {
106 : const MachOObjectFile &Obj =
107 : static_cast<const MachOObjectFile &>(BaseObjT);
108 : MachO::any_relocation_info RelInfo =
109 8 : Obj.getRelocation(RelI->getRawDataRefImpl());
110 8 : uint32_t RelType = Obj.getAnyRelocationType(RelInfo);
111 :
112 : // Set to true for thumb functions in this (or previous) TUs.
113 : // Will be used to set the TargetIsThumbFunc member on the relocation entry.
114 : bool TargetIsLocalThumbFunc = false;
115 8 : if (Obj.getPlainRelocationExternal(RelInfo)) {
116 3 : auto Symbol = RelI->getSymbol();
117 : StringRef TargetName;
118 3 : if (auto TargetNameOrErr = Symbol->getName())
119 3 : TargetName = *TargetNameOrErr;
120 : else
121 : return TargetNameOrErr.takeError();
122 :
123 : // If the target is external but the value doesn't have a name then we've
124 : // converted the value to a section/offset pair, but we still need to set
125 : // the IsTargetThumbFunc bit, so look the value up in the globla symbol table.
126 3 : auto EntryItr = GlobalSymbolTable.find(TargetName);
127 6 : if (EntryItr != GlobalSymbolTable.end()) {
128 3 : TargetIsLocalThumbFunc =
129 3 : EntryItr->second.getFlags().getTargetFlags() &
130 : ARMJITSymbolFlags::Thumb;
131 : }
132 : }
133 :
134 8 : if (Obj.isRelocationScattered(RelInfo)) {
135 3 : if (RelType == MachO::ARM_RELOC_HALF_SECTDIFF)
136 : return processHALFSECTDIFFRelocation(SectionID, RelI, Obj,
137 2 : ObjSectionToID);
138 1 : else if (RelType == MachO::GENERIC_RELOC_VANILLA)
139 : return processScatteredVANILLA(SectionID, RelI, Obj, ObjSectionToID,
140 1 : TargetIsLocalThumbFunc);
141 : else
142 : return ++RelI;
143 : }
144 :
145 : // Sanity check relocation type.
146 5 : switch (RelType) {
147 : UNIMPLEMENTED_RELOC(MachO::ARM_RELOC_PAIR);
148 : UNIMPLEMENTED_RELOC(MachO::ARM_RELOC_SECTDIFF);
149 : UNIMPLEMENTED_RELOC(MachO::ARM_RELOC_LOCAL_SECTDIFF);
150 : UNIMPLEMENTED_RELOC(MachO::ARM_RELOC_PB_LA_PTR);
151 : UNIMPLEMENTED_RELOC(MachO::ARM_THUMB_32BIT_BRANCH);
152 : UNIMPLEMENTED_RELOC(MachO::ARM_RELOC_HALF);
153 5 : default:
154 5 : if (RelType > MachO::ARM_RELOC_HALF_SECTDIFF)
155 0 : return make_error<RuntimeDyldError>(("MachO ARM relocation type " +
156 0 : Twine(RelType) +
157 0 : " is out of range").str());
158 : break;
159 : }
160 :
161 5 : RelocationEntry RE(getRelocationEntry(SectionID, Obj, RelI));
162 5 : if (auto AddendOrErr = decodeAddend(RE))
163 5 : RE.Addend = *AddendOrErr;
164 : else
165 : return AddendOrErr.takeError();
166 5 : RE.IsTargetThumbFunc = TargetIsLocalThumbFunc;
167 :
168 : RelocationValueRef Value;
169 5 : if (auto ValueOrErr = getRelocationValueRef(Obj, RelI, RE, ObjSectionToID))
170 5 : Value = *ValueOrErr;
171 : else
172 : return ValueOrErr.takeError();
173 :
174 : // If this is a branch from a thumb function (BR22) then make sure we mark
175 : // the value as being a thumb stub: we don't want to mix it up with an ARM
176 : // stub targeting the same function.
177 5 : if (RE.RelType == MachO::ARM_THUMB_RELOC_BR22)
178 2 : Value.IsStubThumb = true;
179 :
180 5 : if (RE.IsPCRel)
181 8 : makeValueAddendPCRel(Value, RelI,
182 : (RE.RelType == MachO::ARM_THUMB_RELOC_BR22) ? 4 : 8);
183 :
184 : // If this is a non-external branch target check whether Value points to a
185 : // thumb func.
186 5 : if (!Value.SymbolName && (RelType == MachO::ARM_RELOC_BR24 ||
187 : RelType == MachO::ARM_THUMB_RELOC_BR22))
188 5 : RE.IsTargetThumbFunc = isAddrTargetThumb(Value.SectionID, Value.Offset);
189 :
190 5 : if (RE.RelType == MachO::ARM_RELOC_BR24 ||
191 : RE.RelType == MachO::ARM_THUMB_RELOC_BR22)
192 5 : processBranchRelocation(RE, Value, Stubs);
193 : else {
194 0 : RE.Addend = Value.Offset;
195 0 : if (Value.SymbolName)
196 0 : addRelocationForSymbol(RE, Value.SymbolName);
197 : else
198 0 : addRelocationForSection(RE, Value.SectionID);
199 : }
200 :
201 : return ++RelI;
202 : }
203 :
204 14 : void resolveRelocation(const RelocationEntry &RE, uint64_t Value) override {
205 : LLVM_DEBUG(dumpRelocationToResolve(RE, Value));
206 14 : const SectionEntry &Section = Sections[RE.SectionID];
207 14 : uint8_t *LocalAddress = Section.getAddressWithOffset(RE.Offset);
208 :
209 : // If the relocation is PC-relative, the value to be encoded is the
210 : // pointer difference.
211 14 : if (RE.IsPCRel) {
212 5 : uint64_t FinalAddress = Section.getLoadAddressWithOffset(RE.Offset);
213 5 : Value -= FinalAddress;
214 : // ARM PCRel relocations have an effective-PC offset of two instructions
215 : // (four bytes in Thumb mode, 8 bytes in ARM mode).
216 8 : Value -= (RE.RelType == MachO::ARM_THUMB_RELOC_BR22) ? 4 : 8;
217 : }
218 :
219 14 : switch (RE.RelType) {
220 2 : case MachO::ARM_THUMB_RELOC_BR22: {
221 2 : Value += RE.Addend;
222 2 : uint16_t HighInsn = readBytesUnaligned(LocalAddress, 2);
223 : assert((HighInsn & 0xf800) == 0xf000 &&
224 : "Unrecognized thumb branch encoding (BR22 high bits)");
225 2 : HighInsn = (HighInsn & 0xf800) | ((Value >> 12) & 0x7ff);
226 :
227 2 : uint16_t LowInsn = readBytesUnaligned(LocalAddress + 2, 2);
228 : assert((LowInsn & 0xf800) != 0xf8000 &&
229 : "Unrecognized thumb branch encoding (BR22 low bits)");
230 2 : LowInsn = (LowInsn & 0xf800) | ((Value >> 1) & 0x7ff);
231 :
232 2 : writeBytesUnaligned(HighInsn, LocalAddress, 2);
233 2 : writeBytesUnaligned(LowInsn, LocalAddress + 2, 2);
234 2 : break;
235 : }
236 :
237 7 : case MachO::ARM_RELOC_VANILLA:
238 7 : if (RE.IsTargetThumbFunc)
239 2 : Value |= 0x01;
240 7 : writeBytesUnaligned(Value + RE.Addend, LocalAddress, 1 << RE.Size);
241 7 : break;
242 3 : case MachO::ARM_RELOC_BR24: {
243 : // Mask the value into the target address. We know instructions are
244 : // 32-bit aligned, so we can do it all at once.
245 3 : Value += RE.Addend;
246 : // The low two bits of the value are not encoded.
247 3 : Value >>= 2;
248 : // Mask the value to 24 bits.
249 3 : uint64_t FinalValue = Value & 0xffffff;
250 : // FIXME: If the destination is a Thumb function (and the instruction
251 : // is a non-predicated BL instruction), we need to change it to a BLX
252 : // instruction instead.
253 :
254 : // Insert the value into the instruction.
255 3 : uint32_t Temp = readBytesUnaligned(LocalAddress, 4);
256 3 : writeBytesUnaligned((Temp & ~0xffffff) | FinalValue, LocalAddress, 4);
257 :
258 3 : break;
259 : }
260 2 : case MachO::ARM_RELOC_HALF_SECTDIFF: {
261 2 : uint64_t SectionABase = Sections[RE.Sections.SectionA].getLoadAddress();
262 2 : uint64_t SectionBBase = Sections[RE.Sections.SectionB].getLoadAddress();
263 : assert((Value == SectionABase || Value == SectionBBase) &&
264 : "Unexpected HALFSECTDIFF relocation value.");
265 2 : Value = SectionABase - SectionBBase + RE.Addend;
266 2 : if (RE.Size & 0x1) // :upper16:
267 1 : Value = (Value >> 16);
268 :
269 2 : bool IsThumb = RE.Size & 0x2;
270 :
271 2 : Value &= 0xffff;
272 :
273 2 : uint32_t Insn = readBytesUnaligned(LocalAddress, 4);
274 :
275 2 : if (IsThumb)
276 0 : Insn = (Insn & 0x8f00fbf0) | ((Value & 0xf000) >> 12) |
277 0 : ((Value & 0x0800) >> 1) | ((Value & 0x0700) << 20) |
278 0 : ((Value & 0x00ff) << 16);
279 : else
280 2 : Insn = (Insn & 0xfff0f000) | ((Value & 0xf000) << 4) | (Value & 0x0fff);
281 2 : writeBytesUnaligned(Insn, LocalAddress, 4);
282 2 : break;
283 : }
284 :
285 0 : default:
286 0 : llvm_unreachable("Invalid relocation type");
287 : }
288 14 : }
289 :
290 2 : Error finalizeSection(const ObjectFile &Obj, unsigned SectionID,
291 : const SectionRef &Section) {
292 2 : StringRef Name;
293 2 : Section.getName(Name);
294 :
295 : if (Name == "__nl_symbol_ptr")
296 : return populateIndirectSymbolPointersSection(cast<MachOObjectFile>(Obj),
297 1 : Section, SectionID);
298 : return Error::success();
299 : }
300 :
301 : private:
302 :
303 5 : void processBranchRelocation(const RelocationEntry &RE,
304 : const RelocationValueRef &Value,
305 : StubMap &Stubs) {
306 : // This is an ARM branch relocation, need to use a stub function.
307 : // Look up for existing stub.
308 5 : SectionEntry &Section = Sections[RE.SectionID];
309 : RuntimeDyldMachO::StubMap::const_iterator i = Stubs.find(Value);
310 : uint8_t *Addr;
311 5 : if (i != Stubs.end()) {
312 0 : Addr = Section.getAddressWithOffset(i->second);
313 : } else {
314 : // Create a new stub function.
315 : assert(Section.getStubOffset() % 4 == 0 && "Misaligned stub");
316 5 : Stubs[Value] = Section.getStubOffset();
317 : uint32_t StubOpcode = 0;
318 5 : if (RE.RelType == MachO::ARM_RELOC_BR24)
319 : StubOpcode = 0xe51ff004; // ldr pc, [pc, #-4]
320 2 : else if (RE.RelType == MachO::ARM_THUMB_RELOC_BR22)
321 : StubOpcode = 0xf000f8df; // ldr pc, [pc]
322 : else
323 0 : llvm_unreachable("Unrecognized relocation");
324 5 : Addr = Section.getAddressWithOffset(Section.getStubOffset());
325 5 : writeBytesUnaligned(StubOpcode, Addr, 4);
326 5 : uint8_t *StubTargetAddr = Addr + 4;
327 : RelocationEntry StubRE(
328 10 : RE.SectionID, StubTargetAddr - Section.getAddress(),
329 5 : MachO::GENERIC_RELOC_VANILLA, Value.Offset, false, 2);
330 5 : StubRE.IsTargetThumbFunc = RE.IsTargetThumbFunc;
331 5 : if (Value.SymbolName)
332 0 : addRelocationForSymbol(StubRE, Value.SymbolName);
333 : else
334 5 : addRelocationForSection(StubRE, Value.SectionID);
335 5 : Section.advanceStubOffset(getMaxStubSize());
336 : }
337 5 : RelocationEntry TargetRE(RE.SectionID, RE.Offset, RE.RelType, 0,
338 5 : RE.IsPCRel, RE.Size);
339 5 : resolveRelocation(TargetRE, (uint64_t)Addr);
340 5 : }
341 :
342 : Expected<relocation_iterator>
343 2 : processHALFSECTDIFFRelocation(unsigned SectionID, relocation_iterator RelI,
344 : const ObjectFile &BaseTObj,
345 : ObjSectionToIDMap &ObjSectionToID) {
346 : const MachOObjectFile &MachO =
347 : static_cast<const MachOObjectFile&>(BaseTObj);
348 : MachO::any_relocation_info RE =
349 2 : MachO.getRelocation(RelI->getRawDataRefImpl());
350 :
351 : // For a half-diff relocation the length bits actually record whether this
352 : // is a movw/movt, and whether this is arm or thumb.
353 : // Bit 0 indicates movw (b0 == 0) or movt (b0 == 1).
354 : // Bit 1 indicates arm (b1 == 0) or thumb (b1 == 1).
355 2 : unsigned HalfDiffKindBits = MachO.getAnyRelocationLength(RE);
356 2 : bool IsThumb = HalfDiffKindBits & 0x2;
357 :
358 2 : SectionEntry &Section = Sections[SectionID];
359 2 : uint32_t RelocType = MachO.getAnyRelocationType(RE);
360 2 : bool IsPCRel = MachO.getAnyRelocationPCRel(RE);
361 2 : uint64_t Offset = RelI->getOffset();
362 2 : uint8_t *LocalAddress = Section.getAddressWithOffset(Offset);
363 2 : int64_t Immediate = readBytesUnaligned(LocalAddress, 4); // Copy the whole instruction out.
364 :
365 2 : if (IsThumb)
366 0 : Immediate = ((Immediate & 0x0000000f) << 12) |
367 0 : ((Immediate & 0x00000400) << 1) |
368 0 : ((Immediate & 0x70000000) >> 20) |
369 0 : ((Immediate & 0x00ff0000) >> 16);
370 : else
371 2 : Immediate = ((Immediate >> 4) & 0xf000) | (Immediate & 0xfff);
372 :
373 : ++RelI;
374 : MachO::any_relocation_info RE2 =
375 2 : MachO.getRelocation(RelI->getRawDataRefImpl());
376 2 : uint32_t AddrA = MachO.getScatteredRelocationValue(RE);
377 2 : section_iterator SAI = getSectionByAddress(MachO, AddrA);
378 : assert(SAI != MachO.section_end() && "Can't find section for address A");
379 2 : uint64_t SectionABase = SAI->getAddress();
380 2 : uint64_t SectionAOffset = AddrA - SectionABase;
381 2 : SectionRef SectionA = *SAI;
382 2 : bool IsCode = SectionA.isText();
383 : uint32_t SectionAID = ~0U;
384 2 : if (auto SectionAIDOrErr =
385 2 : findOrEmitSection(MachO, SectionA, IsCode, ObjSectionToID))
386 2 : SectionAID = *SectionAIDOrErr;
387 : else
388 : return SectionAIDOrErr.takeError();
389 :
390 2 : uint32_t AddrB = MachO.getScatteredRelocationValue(RE2);
391 2 : section_iterator SBI = getSectionByAddress(MachO, AddrB);
392 : assert(SBI != MachO.section_end() && "Can't find section for address B");
393 2 : uint64_t SectionBBase = SBI->getAddress();
394 : uint64_t SectionBOffset = AddrB - SectionBBase;
395 2 : SectionRef SectionB = *SBI;
396 : uint32_t SectionBID = ~0U;
397 2 : if (auto SectionBIDOrErr =
398 2 : findOrEmitSection(MachO, SectionB, IsCode, ObjSectionToID))
399 2 : SectionBID = *SectionBIDOrErr;
400 : else
401 : return SectionBIDOrErr.takeError();
402 :
403 2 : uint32_t OtherHalf = MachO.getAnyRelocationAddress(RE2) & 0xffff;
404 2 : unsigned Shift = (HalfDiffKindBits & 0x1) ? 16 : 0;
405 2 : uint32_t FullImmVal = (Immediate << Shift) | (OtherHalf << (16 - Shift));
406 2 : int64_t Addend = FullImmVal - (AddrA - AddrB);
407 :
408 : // addend = Encoded - Expected
409 : // = Encoded - (AddrA - AddrB)
410 :
411 : LLVM_DEBUG(dbgs() << "Found SECTDIFF: AddrA: " << AddrA
412 : << ", AddrB: " << AddrB << ", Addend: " << Addend
413 : << ", SectionA ID: " << SectionAID << ", SectionAOffset: "
414 : << SectionAOffset << ", SectionB ID: " << SectionBID
415 : << ", SectionBOffset: " << SectionBOffset << "\n");
416 : RelocationEntry R(SectionID, Offset, RelocType, Addend, SectionAID,
417 : SectionAOffset, SectionBID, SectionBOffset, IsPCRel,
418 : HalfDiffKindBits);
419 :
420 2 : addRelocationForSection(R, SectionAID);
421 :
422 : return ++RelI;
423 : }
424 :
425 : };
426 : }
427 :
428 : #undef DEBUG_TYPE
429 :
430 : #endif
|