//===--- RuntimeDyldCOFFThumb.h --- COFF/Thumb specific code ---*- C++ --*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // COFF thumb support for MC-JIT runtime dynamic linker. // //===----------------------------------------------------------------------===// #ifndef LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDCOFFTHUMB_H #define LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDCOFFTHUMB_H #include "../RuntimeDyldCOFF.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/Object/COFF.h" #define DEBUG_TYPE "dyld" namespace llvm { static bool isThumbFunc(symbol_iterator Symbol, const ObjectFile &Obj, section_iterator Section) { Expected SymTypeOrErr = Symbol->getType(); if (!SymTypeOrErr) { std::string Buf; raw_string_ostream OS(Buf); logAllUnhandledErrors(SymTypeOrErr.takeError(), OS, ""); OS.flush(); report_fatal_error(Buf); } if (*SymTypeOrErr != SymbolRef::ST_Function) return false; // We check the IMAGE_SCN_MEM_16BIT flag in the section of the symbol to tell // if it's thumb or not return cast(Obj).getCOFFSection(*Section)->Characteristics & COFF::IMAGE_SCN_MEM_16BIT; } class RuntimeDyldCOFFThumb : public RuntimeDyldCOFF { public: RuntimeDyldCOFFThumb(RuntimeDyld::MemoryManager &MM, JITSymbolResolver &Resolver) : RuntimeDyldCOFF(MM, Resolver) {} unsigned getMaxStubSize() override { return 16; // 8-byte load instructions, 4-byte jump, 4-byte padding } unsigned getStubAlignment() override { return 1; } Expected processRelocationRef(unsigned SectionID, relocation_iterator RelI, const ObjectFile &Obj, ObjSectionToIDMap &ObjSectionToID, StubMap &Stubs) override { auto Symbol = RelI->getSymbol(); if (Symbol == Obj.symbol_end()) report_fatal_error("Unknown symbol in relocation"); Expected TargetNameOrErr = Symbol->getName(); if (!TargetNameOrErr) return TargetNameOrErr.takeError(); StringRef TargetName = *TargetNameOrErr; auto SectionOrErr = Symbol->getSection(); if (!SectionOrErr) return SectionOrErr.takeError(); auto Section = *SectionOrErr; uint64_t RelType = RelI->getType(); uint64_t Offset = RelI->getOffset(); // Determine the Addend used to adjust the relocation value. uint64_t Addend = 0; SectionEntry &AddendSection = Sections[SectionID]; uintptr_t ObjTarget = AddendSection.getObjAddress() + Offset; uint8_t *Displacement = (uint8_t *)ObjTarget; switch (RelType) { case COFF::IMAGE_REL_ARM_ADDR32: case COFF::IMAGE_REL_ARM_ADDR32NB: case COFF::IMAGE_REL_ARM_SECREL: Addend = readBytesUnaligned(Displacement, 4); break; default: break; } #if !defined(NDEBUG) SmallString<32> RelTypeName; RelI->getTypeName(RelTypeName); #endif DEBUG(dbgs() << "\t\tIn Section " << SectionID << " Offset " << Offset << " RelType: " << RelTypeName << " TargetName: " << TargetName << " Addend " << Addend << "\n"); unsigned TargetSectionID = -1; if (Section == Obj.section_end()) { RelocationEntry RE(SectionID, Offset, RelType, 0, -1, 0, 0, 0, false, 0); addRelocationForSymbol(RE, TargetName); } else { if (auto TargetSectionIDOrErr = findOrEmitSection(Obj, *Section, Section->isText(), ObjSectionToID)) TargetSectionID = *TargetSectionIDOrErr; else return TargetSectionIDOrErr.takeError(); // We need to find out if the relocation is relative to a thumb function // so that we include the ISA selection bit when resolve the relocation bool IsTargetThumbFunc = isThumbFunc(Symbol, Obj, Section); switch (RelType) { default: llvm_unreachable("unsupported relocation type"); case COFF::IMAGE_REL_ARM_ABSOLUTE: // This relocation is ignored. break; case COFF::IMAGE_REL_ARM_ADDR32: { RelocationEntry RE = RelocationEntry( SectionID, Offset, RelType, Addend, TargetSectionID, getSymbolOffset(*Symbol), 0, 0, false, 0, IsTargetThumbFunc); addRelocationForSection(RE, TargetSectionID); break; } case COFF::IMAGE_REL_ARM_ADDR32NB: { RelocationEntry RE = RelocationEntry(SectionID, Offset, RelType, Addend, TargetSectionID, getSymbolOffset(*Symbol), 0, 0, false, 0); addRelocationForSection(RE, TargetSectionID); break; } case COFF::IMAGE_REL_ARM_SECTION: { RelocationEntry RE = RelocationEntry(TargetSectionID, Offset, RelType, 0); addRelocationForSection(RE, TargetSectionID); break; } case COFF::IMAGE_REL_ARM_SECREL: { RelocationEntry RE = RelocationEntry(SectionID, Offset, RelType, getSymbolOffset(*Symbol) + Addend); addRelocationForSection(RE, TargetSectionID); break; } case COFF::IMAGE_REL_ARM_MOV32T: { RelocationEntry RE = RelocationEntry( SectionID, Offset, RelType, Addend, TargetSectionID, getSymbolOffset(*Symbol), 0, 0, false, 0, IsTargetThumbFunc); addRelocationForSection(RE, TargetSectionID); break; } case COFF::IMAGE_REL_ARM_BRANCH20T: case COFF::IMAGE_REL_ARM_BRANCH24T: case COFF::IMAGE_REL_ARM_BLX23T: { RelocationEntry RE = RelocationEntry(SectionID, Offset, RelType, getSymbolOffset(*Symbol) + Addend, true, 0); addRelocationForSection(RE, TargetSectionID); break; } } } return ++RelI; } void resolveRelocation(const RelocationEntry &RE, uint64_t Value) override { const auto Section = Sections[RE.SectionID]; uint8_t *Target = Section.getAddressWithOffset(RE.Offset); int ISASelectionBit = RE.IsTargetThumbFunc ? 1 : 0; switch (RE.RelType) { default: llvm_unreachable("unsupported relocation type"); case COFF::IMAGE_REL_ARM_ABSOLUTE: // This relocation is ignored. break; case COFF::IMAGE_REL_ARM_ADDR32: { // The target's 32-bit VA. uint64_t Result = RE.Sections.SectionA == static_cast(-1) ? Value : Sections[RE.Sections.SectionA].getLoadAddressWithOffset(RE.Addend); Result |= ISASelectionBit; assert(static_cast(Result) <= INT32_MAX && "relocation overflow"); assert(static_cast(Result) >= INT32_MIN && "relocation underflow"); DEBUG(dbgs() << "\t\tOffset: " << RE.Offset << " RelType: IMAGE_REL_ARM_ADDR32" << " TargetSection: " << RE.Sections.SectionA << " Value: " << format("0x%08" PRIx32, Result) << '\n'); writeBytesUnaligned(Result, Target, 4); break; } case COFF::IMAGE_REL_ARM_ADDR32NB: { // The target's 32-bit RVA. // NOTE: use Section[0].getLoadAddress() as an approximation of ImageBase uint64_t Result = Sections[RE.Sections.SectionA].getLoadAddress() - Sections[0].getLoadAddress() + RE.Addend; assert(static_cast(Result) <= INT32_MAX && "relocation overflow"); assert(static_cast(Result) >= INT32_MIN && "relocation underflow"); DEBUG(dbgs() << "\t\tOffset: " << RE.Offset << " RelType: IMAGE_REL_ARM_ADDR32NB" << " TargetSection: " << RE.Sections.SectionA << " Value: " << format("0x%08" PRIx32, Result) << '\n'); Result |= ISASelectionBit; writeBytesUnaligned(Result, Target, 4); break; } case COFF::IMAGE_REL_ARM_SECTION: // 16-bit section index of the section that contains the target. assert(static_cast(RE.SectionID) <= INT16_MAX && "relocation overflow"); assert(static_cast(RE.SectionID) >= INT16_MIN && "relocation underflow"); DEBUG(dbgs() << "\t\tOffset: " << RE.Offset << " RelType: IMAGE_REL_ARM_SECTION Value: " << RE.SectionID << '\n'); writeBytesUnaligned(RE.SectionID, Target, 2); break; case COFF::IMAGE_REL_ARM_SECREL: // 32-bit offset of the target from the beginning of its section. assert(static_cast(RE.Addend) <= INT32_MAX && "relocation overflow"); assert(static_cast(RE.Addend) >= INT32_MIN && "relocation underflow"); DEBUG(dbgs() << "\t\tOffset: " << RE.Offset << " RelType: IMAGE_REL_ARM_SECREL Value: " << RE.Addend << '\n'); writeBytesUnaligned(RE.Addend, Target, 2); break; case COFF::IMAGE_REL_ARM_MOV32T: { // 32-bit VA of the target applied to a contiguous MOVW+MOVT pair. uint64_t Result = Sections[RE.Sections.SectionA].getLoadAddressWithOffset(RE.Addend); assert(static_cast(Result) <= INT32_MAX && "relocation overflow"); assert(static_cast(Result) >= INT32_MIN && "relocation underflow"); DEBUG(dbgs() << "\t\tOffset: " << RE.Offset << " RelType: IMAGE_REL_ARM_MOV32T" << " TargetSection: " << RE.Sections.SectionA << " Value: " << format("0x%08" PRIx32, Result) << '\n'); // MOVW(T3): |11110|i|10|0|1|0|0|imm4|0|imm3|Rd|imm8| // imm32 = zext imm4:i:imm3:imm8 // MOVT(T1): |11110|i|10|1|1|0|0|imm4|0|imm3|Rd|imm8| // imm16 = imm4:i:imm3:imm8 auto EncodeImmediate = [](uint8_t *Bytes, uint16_t Immediate) { Bytes[0] |= ((Immediate & 0xf000) >> 12); Bytes[1] |= ((Immediate & 0x0800) >> 11); Bytes[2] |= ((Immediate & 0x00ff) >> 0); Bytes[3] |= (((Immediate & 0x0700) >> 8) << 4); }; EncodeImmediate(&Target[0], (static_cast(Result) >> 00) | ISASelectionBit); EncodeImmediate(&Target[4], static_cast(Result) >> 16); break; } case COFF::IMAGE_REL_ARM_BRANCH20T: { // The most significant 20-bits of the signed 21-bit relative displacement uint64_t Value = RE.Addend - (Sections[RE.SectionID].getLoadAddress() + RE.Offset) - 4; assert(static_cast(RE.Addend) <= INT32_MAX && "relocation overflow"); assert(static_cast(RE.Addend) >= INT32_MIN && "relocation underflow"); DEBUG(dbgs() << "\t\tOffset: " << RE.Offset << " RelType: IMAGE_REL_ARM_BRANCH20T" << " Value: " << static_cast(Value) << '\n'); static_cast(Value); llvm_unreachable("unimplemented relocation"); break; } case COFF::IMAGE_REL_ARM_BRANCH24T: { // The most significant 24-bits of the signed 25-bit relative displacement uint64_t Value = RE.Addend - (Sections[RE.SectionID].getLoadAddress() + RE.Offset) - 4; assert(static_cast(RE.Addend) <= INT32_MAX && "relocation overflow"); assert(static_cast(RE.Addend) >= INT32_MIN && "relocation underflow"); DEBUG(dbgs() << "\t\tOffset: " << RE.Offset << " RelType: IMAGE_REL_ARM_BRANCH24T" << " Value: " << static_cast(Value) << '\n'); static_cast(Value); llvm_unreachable("unimplemented relocation"); break; } case COFF::IMAGE_REL_ARM_BLX23T: { // The most significant 24-bits of the signed 25-bit relative displacement uint64_t Value = RE.Addend - (Sections[RE.SectionID].getLoadAddress() + RE.Offset) - 4; assert(static_cast(RE.Addend) <= INT32_MAX && "relocation overflow"); assert(static_cast(RE.Addend) >= INT32_MIN && "relocation underflow"); DEBUG(dbgs() << "\t\tOffset: " << RE.Offset << " RelType: IMAGE_REL_ARM_BLX23T" << " Value: " << static_cast(Value) << '\n'); static_cast(Value); llvm_unreachable("unimplemented relocation"); break; } } } void registerEHFrames() override {} }; } #endif