349 lines · c
1//===--- RuntimeDyldCOFFThumb.h --- COFF/Thumb 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-exception6//7//===----------------------------------------------------------------------===//8//9// COFF thumb support for MC-JIT runtime dynamic linker.10//11//===----------------------------------------------------------------------===//12 13#ifndef LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDCOFFTHUMB_H14#define LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDCOFFTHUMB_H15 16#include "../RuntimeDyldCOFF.h"17#include "llvm/ADT/SmallString.h"18#include "llvm/BinaryFormat/COFF.h"19#include "llvm/Object/COFF.h"20 21#define DEBUG_TYPE "dyld"22 23namespace llvm {24 25static bool isThumbFunc(object::symbol_iterator Symbol,26 const object::ObjectFile &Obj,27 object::section_iterator Section) {28 Expected<object::SymbolRef::Type> SymTypeOrErr = Symbol->getType();29 if (!SymTypeOrErr) {30 std::string Buf;31 raw_string_ostream OS(Buf);32 logAllUnhandledErrors(SymTypeOrErr.takeError(), OS);33 report_fatal_error(Twine(Buf));34 }35 36 if (*SymTypeOrErr != object::SymbolRef::ST_Function)37 return false;38 39 // We check the IMAGE_SCN_MEM_16BIT flag in the section of the symbol to tell40 // if it's thumb or not41 return cast<object::COFFObjectFile>(Obj)42 .getCOFFSection(*Section)43 ->Characteristics &44 COFF::IMAGE_SCN_MEM_16BIT;45}46 47class RuntimeDyldCOFFThumb : public RuntimeDyldCOFF {48public:49 RuntimeDyldCOFFThumb(RuntimeDyld::MemoryManager &MM,50 JITSymbolResolver &Resolver)51 : RuntimeDyldCOFF(MM, Resolver, 4, COFF::IMAGE_REL_ARM_ADDR32) {}52 53 unsigned getMaxStubSize() const override {54 return 16; // 8-byte load instructions, 4-byte jump, 4-byte padding55 }56 57 Expected<JITSymbolFlags> getJITSymbolFlags(const SymbolRef &SR) override {58 59 auto Flags = RuntimeDyldImpl::getJITSymbolFlags(SR);60 61 if (!Flags) {62 return Flags.takeError();63 }64 auto SectionIterOrErr = SR.getSection();65 if (!SectionIterOrErr) {66 return SectionIterOrErr.takeError();67 }68 SectionRef Sec = *SectionIterOrErr.get();69 const object::COFFObjectFile *COFFObjPtr =70 cast<object::COFFObjectFile>(Sec.getObject());71 const coff_section *CoffSec = COFFObjPtr->getCOFFSection(Sec);72 bool isThumb = CoffSec->Characteristics & COFF::IMAGE_SCN_MEM_16BIT;73 74 Flags->getTargetFlags() = isThumb;75 76 return Flags;77 }78 79 Align getStubAlignment() override { return Align(1); }80 81 Expected<object::relocation_iterator>82 processRelocationRef(unsigned SectionID,83 object::relocation_iterator RelI,84 const object::ObjectFile &Obj,85 ObjSectionToIDMap &ObjSectionToID,86 StubMap &Stubs) override {87 auto Symbol = RelI->getSymbol();88 if (Symbol == Obj.symbol_end())89 report_fatal_error("Unknown symbol in relocation");90 91 Expected<StringRef> TargetNameOrErr = Symbol->getName();92 if (!TargetNameOrErr)93 return TargetNameOrErr.takeError();94 StringRef TargetName = *TargetNameOrErr;95 96 auto SectionOrErr = Symbol->getSection();97 if (!SectionOrErr)98 return SectionOrErr.takeError();99 auto Section = *SectionOrErr;100 101 uint64_t RelType = RelI->getType();102 uint64_t Offset = RelI->getOffset();103 104 // Determine the Addend used to adjust the relocation value.105 uint64_t Addend = 0;106 SectionEntry &AddendSection = Sections[SectionID];107 uintptr_t ObjTarget = AddendSection.getObjAddress() + Offset;108 uint8_t *Displacement = (uint8_t *)ObjTarget;109 110 switch (RelType) {111 case COFF::IMAGE_REL_ARM_ADDR32:112 case COFF::IMAGE_REL_ARM_ADDR32NB:113 case COFF::IMAGE_REL_ARM_SECREL:114 Addend = readBytesUnaligned(Displacement, 4);115 break;116 default:117 break;118 }119 120#if !defined(NDEBUG)121 SmallString<32> RelTypeName;122 RelI->getTypeName(RelTypeName);123#endif124 LLVM_DEBUG(dbgs() << "\t\tIn Section " << SectionID << " Offset " << Offset125 << " RelType: " << RelTypeName << " TargetName: "126 << TargetName << " Addend " << Addend << "\n");127 128 bool IsExtern = Section == Obj.section_end();129 unsigned TargetSectionID = -1;130 uint64_t TargetOffset = -1;131 132 if (TargetName.starts_with(getImportSymbolPrefix())) {133 TargetSectionID = SectionID;134 TargetOffset = getDLLImportOffset(SectionID, Stubs, TargetName, true);135 TargetName = StringRef();136 IsExtern = false;137 } else if (!IsExtern) {138 if (auto TargetSectionIDOrErr =139 findOrEmitSection(Obj, *Section, Section->isText(), ObjSectionToID))140 TargetSectionID = *TargetSectionIDOrErr;141 else142 return TargetSectionIDOrErr.takeError();143 if (RelType != COFF::IMAGE_REL_ARM_SECTION)144 TargetOffset = getSymbolOffset(*Symbol);145 }146 147 if (IsExtern) {148 RelocationEntry RE(SectionID, Offset, RelType, 0, -1, 0, 0, 0, false, 0);149 addRelocationForSymbol(RE, TargetName);150 } else {151 152 // We need to find out if the relocation is relative to a thumb function153 // so that we include the ISA selection bit when resolve the relocation154 bool IsTargetThumbFunc = isThumbFunc(Symbol, Obj, Section);155 156 switch (RelType) {157 default: llvm_unreachable("unsupported relocation type");158 case COFF::IMAGE_REL_ARM_ABSOLUTE:159 // This relocation is ignored.160 break;161 case COFF::IMAGE_REL_ARM_ADDR32: {162 RelocationEntry RE =163 RelocationEntry(SectionID, Offset, RelType, Addend, TargetSectionID,164 TargetOffset, 0, 0, false, 0, IsTargetThumbFunc);165 addRelocationForSection(RE, TargetSectionID);166 break;167 }168 case COFF::IMAGE_REL_ARM_ADDR32NB: {169 RelocationEntry RE =170 RelocationEntry(SectionID, Offset, RelType, Addend, TargetSectionID,171 TargetOffset, 0, 0, false, 0);172 addRelocationForSection(RE, TargetSectionID);173 break;174 }175 case COFF::IMAGE_REL_ARM_SECTION: {176 RelocationEntry RE =177 RelocationEntry(TargetSectionID, Offset, RelType, 0);178 addRelocationForSection(RE, TargetSectionID);179 break;180 }181 case COFF::IMAGE_REL_ARM_SECREL: {182 RelocationEntry RE =183 RelocationEntry(SectionID, Offset, RelType, TargetOffset + Addend);184 addRelocationForSection(RE, TargetSectionID);185 break;186 }187 case COFF::IMAGE_REL_ARM_MOV32T: {188 RelocationEntry RE =189 RelocationEntry(SectionID, Offset, RelType, Addend, TargetSectionID,190 TargetOffset, 0, 0, false, 0, IsTargetThumbFunc);191 addRelocationForSection(RE, TargetSectionID);192 break;193 }194 case COFF::IMAGE_REL_ARM_BRANCH20T:195 case COFF::IMAGE_REL_ARM_BRANCH24T:196 case COFF::IMAGE_REL_ARM_BLX23T: {197 RelocationEntry RE = RelocationEntry(SectionID, Offset, RelType,198 TargetOffset + Addend, true, 0);199 addRelocationForSection(RE, TargetSectionID);200 break;201 }202 }203 }204 205 return ++RelI;206 }207 208 void resolveRelocation(const RelocationEntry &RE, uint64_t Value) override {209 const auto Section = Sections[RE.SectionID];210 uint8_t *Target = Section.getAddressWithOffset(RE.Offset);211 int ISASelectionBit = RE.IsTargetThumbFunc ? 1 : 0;212 213 switch (RE.RelType) {214 default: llvm_unreachable("unsupported relocation type");215 case COFF::IMAGE_REL_ARM_ABSOLUTE:216 // This relocation is ignored.217 break;218 case COFF::IMAGE_REL_ARM_ADDR32: {219 // The target's 32-bit VA.220 uint64_t Result =221 RE.Sections.SectionA == static_cast<uint32_t>(-1)222 ? Value223 : Sections[RE.Sections.SectionA].getLoadAddressWithOffset(RE.Addend);224 Result |= ISASelectionBit;225 assert(Result <= UINT32_MAX && "relocation overflow");226 LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset227 << " RelType: IMAGE_REL_ARM_ADDR32"228 << " TargetSection: " << RE.Sections.SectionA229 << " Value: " << format("0x%08" PRIx32, Result)230 << '\n');231 writeBytesUnaligned(Result, Target, 4);232 break;233 }234 case COFF::IMAGE_REL_ARM_ADDR32NB: {235 // The target's 32-bit RVA.236 // NOTE: use Section[0].getLoadAddress() as an approximation of ImageBase237 uint64_t Result = Sections[RE.Sections.SectionA].getLoadAddress() -238 Sections[0].getLoadAddress() + RE.Addend;239 assert(Result <= UINT32_MAX && "relocation overflow");240 LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset241 << " RelType: IMAGE_REL_ARM_ADDR32NB"242 << " TargetSection: " << RE.Sections.SectionA243 << " Value: " << format("0x%08" PRIx32, Result)244 << '\n');245 Result |= ISASelectionBit;246 writeBytesUnaligned(Result, Target, 4);247 break;248 }249 case COFF::IMAGE_REL_ARM_SECTION:250 // 16-bit section index of the section that contains the target.251 assert(static_cast<uint32_t>(RE.SectionID) <= UINT16_MAX &&252 "relocation overflow");253 LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset254 << " RelType: IMAGE_REL_ARM_SECTION Value: "255 << RE.SectionID << '\n');256 writeBytesUnaligned(RE.SectionID, Target, 2);257 break;258 case COFF::IMAGE_REL_ARM_SECREL:259 // 32-bit offset of the target from the beginning of its section.260 assert(static_cast<uint64_t>(RE.Addend) <= UINT32_MAX &&261 "relocation overflow");262 LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset263 << " RelType: IMAGE_REL_ARM_SECREL Value: " << RE.Addend264 << '\n');265 writeBytesUnaligned(RE.Addend, Target, 2);266 break;267 case COFF::IMAGE_REL_ARM_MOV32T: {268 // 32-bit VA of the target applied to a contiguous MOVW+MOVT pair.269 uint64_t Result =270 Sections[RE.Sections.SectionA].getLoadAddressWithOffset(RE.Addend);271 assert(Result <= UINT32_MAX && "relocation overflow");272 LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset273 << " RelType: IMAGE_REL_ARM_MOV32T"274 << " TargetSection: " << RE.Sections.SectionA275 << " Value: " << format("0x%08" PRIx32, Result)276 << '\n');277 278 // MOVW(T3): |11110|i|10|0|1|0|0|imm4|0|imm3|Rd|imm8|279 // imm32 = zext imm4:i:imm3:imm8280 // MOVT(T1): |11110|i|10|1|1|0|0|imm4|0|imm3|Rd|imm8|281 // imm16 = imm4:i:imm3:imm8282 283 auto EncodeImmediate = [](uint8_t *Bytes, uint16_t Immediate) {284 Bytes[0] |= ((Immediate & 0xf000) >> 12);285 Bytes[1] |= ((Immediate & 0x0800) >> 11);286 Bytes[2] |= ((Immediate & 0x00ff) >> 0);287 Bytes[3] |= (((Immediate & 0x0700) >> 8) << 4);288 };289 290 EncodeImmediate(&Target[0],291 (static_cast<uint32_t>(Result) >> 00) | ISASelectionBit);292 EncodeImmediate(&Target[4], static_cast<uint32_t>(Result) >> 16);293 break;294 }295 case COFF::IMAGE_REL_ARM_BRANCH20T: {296 // The most significant 20-bits of the signed 21-bit relative displacement297 uint64_t Value =298 RE.Addend - (Sections[RE.SectionID].getLoadAddress() + RE.Offset) - 4;299 assert(static_cast<int64_t>(RE.Addend) <= INT32_MAX &&300 "relocation overflow");301 assert(static_cast<int64_t>(RE.Addend) >= INT32_MIN &&302 "relocation underflow");303 LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset304 << " RelType: IMAGE_REL_ARM_BRANCH20T"305 << " Value: " << static_cast<int32_t>(Value) << '\n');306 static_cast<void>(Value);307 llvm_unreachable("unimplemented relocation");308 break;309 }310 case COFF::IMAGE_REL_ARM_BRANCH24T: {311 // The most significant 24-bits of the signed 25-bit relative displacement312 uint64_t Value =313 RE.Addend - (Sections[RE.SectionID].getLoadAddress() + RE.Offset) - 4;314 assert(static_cast<int64_t>(RE.Addend) <= INT32_MAX &&315 "relocation overflow");316 assert(static_cast<int64_t>(RE.Addend) >= INT32_MIN &&317 "relocation underflow");318 LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset319 << " RelType: IMAGE_REL_ARM_BRANCH24T"320 << " Value: " << static_cast<int32_t>(Value) << '\n');321 static_cast<void>(Value);322 llvm_unreachable("unimplemented relocation");323 break;324 }325 case COFF::IMAGE_REL_ARM_BLX23T: {326 // The most significant 24-bits of the signed 25-bit relative displacement327 uint64_t Value =328 RE.Addend - (Sections[RE.SectionID].getLoadAddress() + RE.Offset) - 4;329 assert(static_cast<int64_t>(RE.Addend) <= INT32_MAX &&330 "relocation overflow");331 assert(static_cast<int64_t>(RE.Addend) >= INT32_MIN &&332 "relocation underflow");333 LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset334 << " RelType: IMAGE_REL_ARM_BLX23T"335 << " Value: " << static_cast<int32_t>(Value) << '\n');336 static_cast<void>(Value);337 llvm_unreachable("unimplemented relocation");338 break;339 }340 }341 }342 343 void registerEHFrames() override {}344};345 346}347 348#endif349