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