123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431 |
- //===----- RuntimeDyldMachOARM.h ---- MachO/ARM specific code. ----*- C++ -*-=//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #ifndef LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDMACHOARM_H
- #define LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDMACHOARM_H
- #include "../RuntimeDyldMachO.h"
- #define DEBUG_TYPE "dyld"
- namespace llvm {
- class RuntimeDyldMachOARM
- : public RuntimeDyldMachOCRTPBase<RuntimeDyldMachOARM> {
- private:
- typedef RuntimeDyldMachOCRTPBase<RuntimeDyldMachOARM> ParentT;
- public:
- typedef uint32_t TargetPtrT;
- RuntimeDyldMachOARM(RuntimeDyld::MemoryManager &MM,
- JITSymbolResolver &Resolver)
- : RuntimeDyldMachOCRTPBase(MM, Resolver) {}
- unsigned getMaxStubSize() const override { return 8; }
- unsigned getStubAlignment() override { return 4; }
- Expected<JITSymbolFlags> getJITSymbolFlags(const SymbolRef &SR) override {
- auto Flags = RuntimeDyldImpl::getJITSymbolFlags(SR);
- if (!Flags)
- return Flags.takeError();
- Flags->getTargetFlags() = ARMJITSymbolFlags::fromObjectSymbol(SR);
- return Flags;
- }
- uint64_t modifyAddressBasedOnFlags(uint64_t Addr,
- JITSymbolFlags Flags) const override {
- if (Flags.getTargetFlags() & ARMJITSymbolFlags::Thumb)
- Addr |= 0x1;
- return Addr;
- }
- bool isAddrTargetThumb(unsigned SectionID, uint64_t Offset) {
- auto TargetObjAddr = Sections[SectionID].getObjAddress() + Offset;
- for (auto &KV : GlobalSymbolTable) {
- auto &Entry = KV.second;
- auto SymbolObjAddr =
- Sections[Entry.getSectionID()].getObjAddress() + Entry.getOffset();
- if (TargetObjAddr == SymbolObjAddr)
- return (Entry.getFlags().getTargetFlags() & ARMJITSymbolFlags::Thumb);
- }
- return false;
- }
- Expected<int64_t> decodeAddend(const RelocationEntry &RE) const {
- const SectionEntry &Section = Sections[RE.SectionID];
- uint8_t *LocalAddress = Section.getAddressWithOffset(RE.Offset);
- switch (RE.RelType) {
- default:
- return memcpyAddend(RE);
- case MachO::ARM_RELOC_BR24: {
- uint32_t Temp = readBytesUnaligned(LocalAddress, 4);
- Temp &= 0x00ffffff; // Mask out the opcode.
- // Now we've got the shifted immediate, shift by 2, sign extend and ret.
- return SignExtend32<26>(Temp << 2);
- }
- case MachO::ARM_THUMB_RELOC_BR22: {
- // This is a pair of instructions whose operands combine to provide 22
- // bits of displacement:
- // Encoding for high bits 1111 0XXX XXXX XXXX
- // Encoding for low bits 1111 1XXX XXXX XXXX
- uint16_t HighInsn = readBytesUnaligned(LocalAddress, 2);
- if ((HighInsn & 0xf800) != 0xf000)
- return make_error<StringError>("Unrecognized thumb branch encoding "
- "(BR22 high bits)",
- inconvertibleErrorCode());
- uint16_t LowInsn = readBytesUnaligned(LocalAddress + 2, 2);
- if ((LowInsn & 0xf800) != 0xf800)
- return make_error<StringError>("Unrecognized thumb branch encoding "
- "(BR22 low bits)",
- inconvertibleErrorCode());
- return SignExtend64<23>(((HighInsn & 0x7ff) << 12) |
- ((LowInsn & 0x7ff) << 1));
- }
- }
- }
- Expected<relocation_iterator>
- processRelocationRef(unsigned SectionID, relocation_iterator RelI,
- const ObjectFile &BaseObjT,
- ObjSectionToIDMap &ObjSectionToID,
- StubMap &Stubs) override {
- const MachOObjectFile &Obj =
- static_cast<const MachOObjectFile &>(BaseObjT);
- MachO::any_relocation_info RelInfo =
- Obj.getRelocation(RelI->getRawDataRefImpl());
- uint32_t RelType = Obj.getAnyRelocationType(RelInfo);
- // Set to true for thumb functions in this (or previous) TUs.
- // Will be used to set the TargetIsThumbFunc member on the relocation entry.
- bool TargetIsLocalThumbFunc = false;
- if (Obj.getPlainRelocationExternal(RelInfo)) {
- auto Symbol = RelI->getSymbol();
- StringRef TargetName;
- if (auto TargetNameOrErr = Symbol->getName())
- TargetName = *TargetNameOrErr;
- else
- return TargetNameOrErr.takeError();
- // If the target is external but the value doesn't have a name then we've
- // converted the value to a section/offset pair, but we still need to set
- // the IsTargetThumbFunc bit, so look the value up in the globla symbol table.
- auto EntryItr = GlobalSymbolTable.find(TargetName);
- if (EntryItr != GlobalSymbolTable.end()) {
- TargetIsLocalThumbFunc =
- EntryItr->second.getFlags().getTargetFlags() &
- ARMJITSymbolFlags::Thumb;
- }
- }
- if (Obj.isRelocationScattered(RelInfo)) {
- if (RelType == MachO::ARM_RELOC_HALF_SECTDIFF)
- return processHALFSECTDIFFRelocation(SectionID, RelI, Obj,
- ObjSectionToID);
- else if (RelType == MachO::GENERIC_RELOC_VANILLA)
- return processScatteredVANILLA(SectionID, RelI, Obj, ObjSectionToID,
- TargetIsLocalThumbFunc);
- else
- return ++RelI;
- }
- // Validate the relocation type.
- switch (RelType) {
- UNIMPLEMENTED_RELOC(MachO::ARM_RELOC_PAIR);
- UNIMPLEMENTED_RELOC(MachO::ARM_RELOC_SECTDIFF);
- UNIMPLEMENTED_RELOC(MachO::ARM_RELOC_LOCAL_SECTDIFF);
- UNIMPLEMENTED_RELOC(MachO::ARM_RELOC_PB_LA_PTR);
- UNIMPLEMENTED_RELOC(MachO::ARM_THUMB_32BIT_BRANCH);
- UNIMPLEMENTED_RELOC(MachO::ARM_RELOC_HALF);
- default:
- if (RelType > MachO::ARM_RELOC_HALF_SECTDIFF)
- return make_error<RuntimeDyldError>(("MachO ARM relocation type " +
- Twine(RelType) +
- " is out of range").str());
- break;
- }
- RelocationEntry RE(getRelocationEntry(SectionID, Obj, RelI));
- if (auto AddendOrErr = decodeAddend(RE))
- RE.Addend = *AddendOrErr;
- else
- return AddendOrErr.takeError();
- RE.IsTargetThumbFunc = TargetIsLocalThumbFunc;
- RelocationValueRef Value;
- if (auto ValueOrErr = getRelocationValueRef(Obj, RelI, RE, ObjSectionToID))
- Value = *ValueOrErr;
- else
- return ValueOrErr.takeError();
- // If this is a branch from a thumb function (BR22) then make sure we mark
- // the value as being a thumb stub: we don't want to mix it up with an ARM
- // stub targeting the same function.
- if (RE.RelType == MachO::ARM_THUMB_RELOC_BR22)
- Value.IsStubThumb = true;
- if (RE.IsPCRel)
- makeValueAddendPCRel(Value, RelI,
- (RE.RelType == MachO::ARM_THUMB_RELOC_BR22) ? 4 : 8);
- // If this is a non-external branch target check whether Value points to a
- // thumb func.
- if (!Value.SymbolName && (RelType == MachO::ARM_RELOC_BR24 ||
- RelType == MachO::ARM_THUMB_RELOC_BR22))
- RE.IsTargetThumbFunc = isAddrTargetThumb(Value.SectionID, Value.Offset);
- if (RE.RelType == MachO::ARM_RELOC_BR24 ||
- RE.RelType == MachO::ARM_THUMB_RELOC_BR22)
- processBranchRelocation(RE, Value, Stubs);
- else {
- RE.Addend = Value.Offset;
- if (Value.SymbolName)
- addRelocationForSymbol(RE, Value.SymbolName);
- else
- addRelocationForSection(RE, Value.SectionID);
- }
- return ++RelI;
- }
- void resolveRelocation(const RelocationEntry &RE, uint64_t Value) override {
- LLVM_DEBUG(dumpRelocationToResolve(RE, Value));
- const SectionEntry &Section = Sections[RE.SectionID];
- uint8_t *LocalAddress = Section.getAddressWithOffset(RE.Offset);
- // If the relocation is PC-relative, the value to be encoded is the
- // pointer difference.
- if (RE.IsPCRel) {
- uint64_t FinalAddress = Section.getLoadAddressWithOffset(RE.Offset);
- Value -= FinalAddress;
- // ARM PCRel relocations have an effective-PC offset of two instructions
- // (four bytes in Thumb mode, 8 bytes in ARM mode).
- Value -= (RE.RelType == MachO::ARM_THUMB_RELOC_BR22) ? 4 : 8;
- }
- switch (RE.RelType) {
- case MachO::ARM_THUMB_RELOC_BR22: {
- Value += RE.Addend;
- uint16_t HighInsn = readBytesUnaligned(LocalAddress, 2);
- assert((HighInsn & 0xf800) == 0xf000 &&
- "Unrecognized thumb branch encoding (BR22 high bits)");
- HighInsn = (HighInsn & 0xf800) | ((Value >> 12) & 0x7ff);
- uint16_t LowInsn = readBytesUnaligned(LocalAddress + 2, 2);
- assert((LowInsn & 0xf800) == 0xf800 &&
- "Unrecognized thumb branch encoding (BR22 low bits)");
- LowInsn = (LowInsn & 0xf800) | ((Value >> 1) & 0x7ff);
- writeBytesUnaligned(HighInsn, LocalAddress, 2);
- writeBytesUnaligned(LowInsn, LocalAddress + 2, 2);
- break;
- }
- case MachO::ARM_RELOC_VANILLA:
- if (RE.IsTargetThumbFunc)
- Value |= 0x01;
- writeBytesUnaligned(Value + RE.Addend, LocalAddress, 1 << RE.Size);
- break;
- case MachO::ARM_RELOC_BR24: {
- // Mask the value into the target address. We know instructions are
- // 32-bit aligned, so we can do it all at once.
- Value += RE.Addend;
- // The low two bits of the value are not encoded.
- Value >>= 2;
- // Mask the value to 24 bits.
- uint64_t FinalValue = Value & 0xffffff;
- // FIXME: If the destination is a Thumb function (and the instruction
- // is a non-predicated BL instruction), we need to change it to a BLX
- // instruction instead.
- // Insert the value into the instruction.
- uint32_t Temp = readBytesUnaligned(LocalAddress, 4);
- writeBytesUnaligned((Temp & ~0xffffff) | FinalValue, LocalAddress, 4);
- break;
- }
- case MachO::ARM_RELOC_HALF_SECTDIFF: {
- uint64_t SectionABase = Sections[RE.Sections.SectionA].getLoadAddress();
- uint64_t SectionBBase = Sections[RE.Sections.SectionB].getLoadAddress();
- assert((Value == SectionABase || Value == SectionBBase) &&
- "Unexpected HALFSECTDIFF relocation value.");
- Value = SectionABase - SectionBBase + RE.Addend;
- if (RE.Size & 0x1) // :upper16:
- Value = (Value >> 16);
- bool IsThumb = RE.Size & 0x2;
- Value &= 0xffff;
- uint32_t Insn = readBytesUnaligned(LocalAddress, 4);
- if (IsThumb)
- Insn = (Insn & 0x8f00fbf0) | ((Value & 0xf000) >> 12) |
- ((Value & 0x0800) >> 1) | ((Value & 0x0700) << 20) |
- ((Value & 0x00ff) << 16);
- else
- Insn = (Insn & 0xfff0f000) | ((Value & 0xf000) << 4) | (Value & 0x0fff);
- writeBytesUnaligned(Insn, LocalAddress, 4);
- break;
- }
- default:
- llvm_unreachable("Invalid relocation type");
- }
- }
- Error finalizeSection(const ObjectFile &Obj, unsigned SectionID,
- const SectionRef &Section) {
- StringRef Name;
- if (Expected<StringRef> NameOrErr = Section.getName())
- Name = *NameOrErr;
- else
- consumeError(NameOrErr.takeError());
- if (Name == "__nl_symbol_ptr")
- return populateIndirectSymbolPointersSection(cast<MachOObjectFile>(Obj),
- Section, SectionID);
- return Error::success();
- }
- private:
- void processBranchRelocation(const RelocationEntry &RE,
- const RelocationValueRef &Value,
- StubMap &Stubs) {
- // This is an ARM branch relocation, need to use a stub function.
- // Look up for existing stub.
- SectionEntry &Section = Sections[RE.SectionID];
- RuntimeDyldMachO::StubMap::const_iterator i = Stubs.find(Value);
- uint8_t *Addr;
- if (i != Stubs.end()) {
- Addr = Section.getAddressWithOffset(i->second);
- } else {
- // Create a new stub function.
- assert(Section.getStubOffset() % 4 == 0 && "Misaligned stub");
- Stubs[Value] = Section.getStubOffset();
- uint32_t StubOpcode = 0;
- if (RE.RelType == MachO::ARM_RELOC_BR24)
- StubOpcode = 0xe51ff004; // ldr pc, [pc, #-4]
- else if (RE.RelType == MachO::ARM_THUMB_RELOC_BR22)
- StubOpcode = 0xf000f8df; // ldr pc, [pc]
- else
- llvm_unreachable("Unrecognized relocation");
- Addr = Section.getAddressWithOffset(Section.getStubOffset());
- writeBytesUnaligned(StubOpcode, Addr, 4);
- uint8_t *StubTargetAddr = Addr + 4;
- RelocationEntry StubRE(
- RE.SectionID, StubTargetAddr - Section.getAddress(),
- MachO::GENERIC_RELOC_VANILLA, Value.Offset, false, 2);
- StubRE.IsTargetThumbFunc = RE.IsTargetThumbFunc;
- if (Value.SymbolName)
- addRelocationForSymbol(StubRE, Value.SymbolName);
- else
- addRelocationForSection(StubRE, Value.SectionID);
- Section.advanceStubOffset(getMaxStubSize());
- }
- RelocationEntry TargetRE(RE.SectionID, RE.Offset, RE.RelType, 0,
- RE.IsPCRel, RE.Size);
- resolveRelocation(TargetRE, (uint64_t)Addr);
- }
- Expected<relocation_iterator>
- processHALFSECTDIFFRelocation(unsigned SectionID, relocation_iterator RelI,
- const ObjectFile &BaseTObj,
- ObjSectionToIDMap &ObjSectionToID) {
- const MachOObjectFile &MachO =
- static_cast<const MachOObjectFile&>(BaseTObj);
- MachO::any_relocation_info RE =
- MachO.getRelocation(RelI->getRawDataRefImpl());
- // For a half-diff relocation the length bits actually record whether this
- // is a movw/movt, and whether this is arm or thumb.
- // Bit 0 indicates movw (b0 == 0) or movt (b0 == 1).
- // Bit 1 indicates arm (b1 == 0) or thumb (b1 == 1).
- unsigned HalfDiffKindBits = MachO.getAnyRelocationLength(RE);
- bool IsThumb = HalfDiffKindBits & 0x2;
- SectionEntry &Section = Sections[SectionID];
- uint32_t RelocType = MachO.getAnyRelocationType(RE);
- bool IsPCRel = MachO.getAnyRelocationPCRel(RE);
- uint64_t Offset = RelI->getOffset();
- uint8_t *LocalAddress = Section.getAddressWithOffset(Offset);
- int64_t Immediate = readBytesUnaligned(LocalAddress, 4); // Copy the whole instruction out.
- if (IsThumb)
- Immediate = ((Immediate & 0x0000000f) << 12) |
- ((Immediate & 0x00000400) << 1) |
- ((Immediate & 0x70000000) >> 20) |
- ((Immediate & 0x00ff0000) >> 16);
- else
- Immediate = ((Immediate >> 4) & 0xf000) | (Immediate & 0xfff);
- ++RelI;
- MachO::any_relocation_info RE2 =
- MachO.getRelocation(RelI->getRawDataRefImpl());
- uint32_t AddrA = MachO.getScatteredRelocationValue(RE);
- section_iterator SAI = getSectionByAddress(MachO, AddrA);
- assert(SAI != MachO.section_end() && "Can't find section for address A");
- uint64_t SectionABase = SAI->getAddress();
- uint64_t SectionAOffset = AddrA - SectionABase;
- SectionRef SectionA = *SAI;
- bool IsCode = SectionA.isText();
- uint32_t SectionAID = ~0U;
- if (auto SectionAIDOrErr =
- findOrEmitSection(MachO, SectionA, IsCode, ObjSectionToID))
- SectionAID = *SectionAIDOrErr;
- else
- return SectionAIDOrErr.takeError();
- uint32_t AddrB = MachO.getScatteredRelocationValue(RE2);
- section_iterator SBI = getSectionByAddress(MachO, AddrB);
- assert(SBI != MachO.section_end() && "Can't find section for address B");
- uint64_t SectionBBase = SBI->getAddress();
- uint64_t SectionBOffset = AddrB - SectionBBase;
- SectionRef SectionB = *SBI;
- uint32_t SectionBID = ~0U;
- if (auto SectionBIDOrErr =
- findOrEmitSection(MachO, SectionB, IsCode, ObjSectionToID))
- SectionBID = *SectionBIDOrErr;
- else
- return SectionBIDOrErr.takeError();
- uint32_t OtherHalf = MachO.getAnyRelocationAddress(RE2) & 0xffff;
- unsigned Shift = (HalfDiffKindBits & 0x1) ? 16 : 0;
- uint32_t FullImmVal = (Immediate << Shift) | (OtherHalf << (16 - Shift));
- int64_t Addend = FullImmVal - (AddrA - AddrB);
- // addend = Encoded - Expected
- // = Encoded - (AddrA - AddrB)
- LLVM_DEBUG(dbgs() << "Found SECTDIFF: AddrA: " << AddrA
- << ", AddrB: " << AddrB << ", Addend: " << Addend
- << ", SectionA ID: " << SectionAID << ", SectionAOffset: "
- << SectionAOffset << ", SectionB ID: " << SectionBID
- << ", SectionBOffset: " << SectionBOffset << "\n");
- RelocationEntry R(SectionID, Offset, RelocType, Addend, SectionAID,
- SectionAOffset, SectionBID, SectionBOffset, IsPCRel,
- HalfDiffKindBits);
- addRelocationForSection(R, SectionAID);
- return ++RelI;
- }
- };
- }
- #undef DEBUG_TYPE
- #endif
|