Home | History | Annotate | Line # | Download | only in Targets
      1 //===----- RuntimeDyldMachOARM.h ---- MachO/ARM specific code. ----*- C++ -*-=//
      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 #ifndef LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDMACHOARM_H
     10 #define LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDMACHOARM_H
     11 
     12 #include "../RuntimeDyldMachO.h"
     13 #include <string>
     14 
     15 #define DEBUG_TYPE "dyld"
     16 
     17 namespace llvm {
     18 
     19 class RuntimeDyldMachOARM
     20     : public RuntimeDyldMachOCRTPBase<RuntimeDyldMachOARM> {
     21 private:
     22   typedef RuntimeDyldMachOCRTPBase<RuntimeDyldMachOARM> ParentT;
     23 
     24 public:
     25 
     26   typedef uint32_t TargetPtrT;
     27 
     28   RuntimeDyldMachOARM(RuntimeDyld::MemoryManager &MM,
     29                       JITSymbolResolver &Resolver)
     30     : RuntimeDyldMachOCRTPBase(MM, Resolver) {}
     31 
     32   unsigned getMaxStubSize() const override { return 8; }
     33 
     34   unsigned getStubAlignment() override { return 4; }
     35 
     36   Expected<JITSymbolFlags> getJITSymbolFlags(const SymbolRef &SR) override {
     37     auto Flags = RuntimeDyldImpl::getJITSymbolFlags(SR);
     38     if (!Flags)
     39       return Flags.takeError();
     40     Flags->getTargetFlags() = ARMJITSymbolFlags::fromObjectSymbol(SR);
     41     return Flags;
     42   }
     43 
     44   uint64_t modifyAddressBasedOnFlags(uint64_t Addr,
     45                                      JITSymbolFlags Flags) const override {
     46     if (Flags.getTargetFlags() & ARMJITSymbolFlags::Thumb)
     47       Addr |= 0x1;
     48     return Addr;
     49   }
     50 
     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;
     55       auto SymbolObjAddr =
     56           Sections[Entry.getSectionID()].getObjAddress() + Entry.getOffset();
     57       if (TargetObjAddr == SymbolObjAddr)
     58         return (Entry.getFlags().getTargetFlags() & ARMJITSymbolFlags::Thumb);
     59     }
     60     return false;
     61   }
     62 
     63   Expected<int64_t> decodeAddend(const RelocationEntry &RE) const {
     64     const SectionEntry &Section = Sections[RE.SectionID];
     65     uint8_t *LocalAddress = Section.getAddressWithOffset(RE.Offset);
     66 
     67     switch (RE.RelType) {
     68       default:
     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);
     75       }
     76 
     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 "
     85                                          "(BR22 high bits)",
     86                                          inconvertibleErrorCode());
     87 
     88         uint16_t LowInsn = readBytesUnaligned(LocalAddress + 2, 2);
     89         if ((LowInsn & 0xf800) != 0xf800)
     90           return make_error<StringError>("Unrecognized thumb branch encoding "
     91                                          "(BR22 low bits)",
     92                                          inconvertibleErrorCode());
     93 
     94         return SignExtend64<23>(((HighInsn & 0x7ff) << 12) |
     95                                 ((LowInsn & 0x7ff) << 1));
     96       }
     97     }
     98   }
     99 
    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);
    110 
    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;
    119       else
    120         return TargetNameOrErr.takeError();
    121 
    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;
    130       }
    131     }
    132 
    133     if (Obj.isRelocationScattered(RelInfo)) {
    134       if (RelType == MachO::ARM_RELOC_HALF_SECTDIFF)
    135         return processHALFSECTDIFFRelocation(SectionID, RelI, Obj,
    136                                              ObjSectionToID);
    137       else if (RelType == MachO::GENERIC_RELOC_VANILLA)
    138         return processScatteredVANILLA(SectionID, RelI, Obj, ObjSectionToID,
    139                                        TargetIsLocalThumbFunc);
    140       else
    141         return ++RelI;
    142     }
    143 
    144     // Sanity check relocation type.
    145     switch (RelType) {
    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);
    152     default:
    153       if (RelType > MachO::ARM_RELOC_HALF_SECTDIFF)
    154         return make_error<RuntimeDyldError>(("MachO ARM relocation type " +
    155                                              Twine(RelType) +
    156                                              " is out of range").str());
    157       break;
    158     }
    159 
    160     RelocationEntry RE(getRelocationEntry(SectionID, Obj, RelI));
    161     if (auto AddendOrErr = decodeAddend(RE))
    162       RE.Addend = *AddendOrErr;
    163     else
    164       return AddendOrErr.takeError();
    165     RE.IsTargetThumbFunc = TargetIsLocalThumbFunc;
    166 
    167     RelocationValueRef Value;
    168     if (auto ValueOrErr = getRelocationValueRef(Obj, RelI, RE, ObjSectionToID))
    169       Value = *ValueOrErr;
    170     else
    171       return ValueOrErr.takeError();
    172 
    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;
    178 
    179     if (RE.IsPCRel)
    180       makeValueAddendPCRel(Value, RelI,
    181                            (RE.RelType == MachO::ARM_THUMB_RELOC_BR22) ? 4 : 8);
    182 
    183     // If this is a non-external branch target check whether Value points to a
    184     // thumb func.
    185     if (!Value.SymbolName && (RelType == MachO::ARM_RELOC_BR24 ||
    186                               RelType == MachO::ARM_THUMB_RELOC_BR22))
    187       RE.IsTargetThumbFunc = isAddrTargetThumb(Value.SectionID, Value.Offset);
    188 
    189     if (RE.RelType == MachO::ARM_RELOC_BR24 ||
    190         RE.RelType == MachO::ARM_THUMB_RELOC_BR22)
    191       processBranchRelocation(RE, Value, Stubs);
    192     else {
    193       RE.Addend = Value.Offset;
    194       if (Value.SymbolName)
    195         addRelocationForSymbol(RE, Value.SymbolName);
    196       else
    197         addRelocationForSection(RE, Value.SectionID);
    198     }
    199 
    200     return ++RelI;
    201   }
    202 
    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);
    207 
    208     // If the relocation is PC-relative, the value to be encoded is the
    209     // pointer difference.
    210     if (RE.IsPCRel) {
    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;
    216     }
    217 
    218     switch (RE.RelType) {
    219     case MachO::ARM_THUMB_RELOC_BR22: {
    220       Value += RE.Addend;
    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);
    225 
    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);
    230 
    231       writeBytesUnaligned(HighInsn, LocalAddress, 2);
    232       writeBytesUnaligned(LowInsn, LocalAddress + 2, 2);
    233       break;
    234     }
    235 
    236     case MachO::ARM_RELOC_VANILLA:
    237       if (RE.IsTargetThumbFunc)
    238         Value |= 0x01;
    239       writeBytesUnaligned(Value + RE.Addend, LocalAddress, 1 << RE.Size);
    240       break;
    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.
    244       Value += RE.Addend;
    245       // The low two bits of the value are not encoded.
    246       Value >>= 2;
    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.
    252 
    253       // Insert the value into the instruction.
    254       uint32_t Temp = readBytesUnaligned(LocalAddress, 4);
    255       writeBytesUnaligned((Temp & ~0xffffff) | FinalValue, LocalAddress, 4);
    256 
    257       break;
    258     }
    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);
    267 
    268       bool IsThumb = RE.Size & 0x2;
    269 
    270       Value &= 0xffff;
    271 
    272       uint32_t Insn = readBytesUnaligned(LocalAddress, 4);
    273 
    274       if (IsThumb)
    275         Insn = (Insn & 0x8f00fbf0) | ((Value & 0xf000) >> 12) |
    276                ((Value & 0x0800) >> 1) | ((Value & 0x0700) << 20) |
    277                ((Value & 0x00ff) << 16);
    278       else
    279         Insn = (Insn & 0xfff0f000) | ((Value & 0xf000) << 4) | (Value & 0x0fff);
    280       writeBytesUnaligned(Insn, LocalAddress, 4);
    281       break;
    282     }
    283 
    284     default:
    285       llvm_unreachable("Invalid relocation type");
    286     }
    287   }
    288 
    289   Error finalizeSection(const ObjectFile &Obj, unsigned SectionID,
    290                        const SectionRef &Section) {
    291     StringRef Name;
    292     if (Expected<StringRef> NameOrErr = Section.getName())
    293       Name = *NameOrErr;
    294     else
    295       consumeError(NameOrErr.takeError());
    296 
    297     if (Name == "__nl_symbol_ptr")
    298       return populateIndirectSymbolPointersSection(cast<MachOObjectFile>(Obj),
    299                                                    Section, SectionID);
    300     return Error::success();
    301   }
    302 
    303 private:
    304 
    305   void processBranchRelocation(const RelocationEntry &RE,
    306                                const RelocationValueRef &Value,
    307                                StubMap &Stubs) {
    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);
    312     uint8_t *Addr;
    313     if (i != Stubs.end()) {
    314       Addr = Section.getAddressWithOffset(i->second);
    315     } else {
    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]
    324       else
    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);
    335       else
    336         addRelocationForSection(StubRE, Value.SectionID);
    337       Section.advanceStubOffset(getMaxStubSize());
    338     }
    339     RelocationEntry TargetRE(RE.SectionID, RE.Offset, RE.RelType, 0,
    340                              RE.IsPCRel, RE.Size);
    341     resolveRelocation(TargetRE, (uint64_t)Addr);
    342   }
    343 
    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());
    352 
    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;
    359 
    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.
    366 
    367     if (IsThumb)
    368       Immediate = ((Immediate & 0x0000000f) << 12) |
    369                   ((Immediate & 0x00000400) << 1) |
    370                   ((Immediate & 0x70000000) >> 20) |
    371                   ((Immediate & 0x00ff0000) >> 16);
    372     else
    373       Immediate = ((Immediate >> 4) & 0xf000) | (Immediate & 0xfff);
    374 
    375     ++RelI;
    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;
    389     else
    390       return SectionAIDOrErr.takeError();
    391 
    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;
    402     else
    403       return SectionBIDOrErr.takeError();
    404 
    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);
    409 
    410     // addend = Encoded - Expected
    411     //        = Encoded - (AddrA - AddrB)
    412 
    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,
    420                       HalfDiffKindBits);
    421 
    422     addRelocationForSection(R, SectionAID);
    423 
    424     return ++RelI;
    425   }
    426 
    427 };
    428 }
    429 
    430 #undef DEBUG_TYPE
    431 
    432 #endif
    433