//===- ARM.cpp ------------------------------------------------------------===// // // 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 // //===----------------------------------------------------------------------===// #include "InputFiles.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" #include "llvm/ADT/Bitfields.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::MachO; using namespace llvm::support::endian; using namespace lld; using namespace lld::macho; namespace { struct ARM : TargetInfo { ARM(uint32_t cpuSubtype); int64_t getEmbeddedAddend(MemoryBufferRef, uint64_t offset, const relocation_info) const override; void relocateOne(uint8_t *loc, const Reloc &, uint64_t va, uint64_t pc) const override; void writeStub(uint8_t *buf, const Symbol &) const override; void writeStubHelperHeader(uint8_t *buf) const override; void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &, uint64_t entryAddr) const override; void relaxGotLoad(uint8_t *loc, uint8_t type) const override; const RelocAttrs &getRelocAttrs(uint8_t type) const override; uint64_t getPageSize() const override { return 4 * 1024; } }; } // namespace const RelocAttrs &ARM::getRelocAttrs(uint8_t type) const { static const std::array relocAttrsArray{{ #define B(x) RelocAttrBits::x {"VANILLA", /* FIXME populate this */ B(_0)}, {"PAIR", /* FIXME populate this */ B(_0)}, {"SECTDIFF", /* FIXME populate this */ B(_0)}, {"LOCAL_SECTDIFF", /* FIXME populate this */ B(_0)}, {"PB_LA_PTR", /* FIXME populate this */ B(_0)}, {"BR24", B(PCREL) | B(LOCAL) | B(EXTERN) | B(BRANCH) | B(BYTE4)}, {"BR22", B(PCREL) | B(LOCAL) | B(EXTERN) | B(BRANCH) | B(BYTE4)}, {"32BIT_BRANCH", /* FIXME populate this */ B(_0)}, {"HALF", /* FIXME populate this */ B(_0)}, {"HALF_SECTDIFF", /* FIXME populate this */ B(_0)}, #undef B }}; assert(type < relocAttrsArray.size() && "invalid relocation type"); if (type >= relocAttrsArray.size()) return invalidRelocAttrs; return relocAttrsArray[type]; } int64_t ARM::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset, relocation_info rel) const { // FIXME: implement this return 0; } template using BitfieldFlag = Bitfield::Element; // ARM BL encoding: // // 30 28 24 0 // +---------+---------+----------------------------------------------+ // | cond | 1 0 1 1 | imm24 | // +---------+---------+----------------------------------------------+ // // `cond` here varies depending on whether we have bleq, blne, etc. // `imm24` encodes a 26-bit pcrel offset -- last 2 bits are zero as ARM // functions are 4-byte-aligned. // // ARM BLX encoding: // // 30 28 24 0 // +---------+---------+----------------------------------------------+ // | 1 1 1 1 | 1 0 1 H | imm24 | // +---------+---------+----------------------------------------------+ // // Since Thumb functions are 2-byte-aligned, we need one extra bit to encode // the offset -- that is the H bit. // // BLX is always unconditional, so while we can convert directly from BLX to BL, // we need to insert a shim if a BL's target is a Thumb function. // // Helper aliases for decoding BL / BLX: using Cond = Bitfield::Element; using Imm24 = Bitfield::Element; void ARM::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value, uint64_t pc) const { switch (r.type) { case ARM_RELOC_BR24: { uint32_t base = read32le(loc); bool isBlx = Bitfield::get(base) == 0xf; const Symbol *sym = r.referent.get(); int32_t offset = value - (pc + 8); if (auto *defined = dyn_cast(sym)) { if (!isBlx && defined->thumb) { error("TODO: implement interworking shim"); return; } else if (isBlx && !defined->thumb) { Bitfield::set(base, 0xe); // unconditional BL Bitfield::set>(base, 1); isBlx = false; } } else { error("TODO: Implement ARM_RELOC_BR24 for dylib symbols"); return; } if (isBlx) { assert((0x1 & value) == 0); Bitfield::set(base, offset >> 2); Bitfield::set>(base, (offset >> 1) & 1); // H bit } else { assert((0x3 & value) == 0); Bitfield::set(base, offset >> 2); } write32le(loc, base); break; } default: fatal("unhandled relocation type"); } } void ARM::writeStub(uint8_t *buf, const Symbol &sym) const { fatal("TODO: implement this"); } void ARM::writeStubHelperHeader(uint8_t *buf) const { fatal("TODO: implement this"); } void ARM::writeStubHelperEntry(uint8_t *buf, const DylibSymbol &sym, uint64_t entryAddr) const { fatal("TODO: implement this"); } void ARM::relaxGotLoad(uint8_t *loc, uint8_t type) const { fatal("TODO: implement this"); } ARM::ARM(uint32_t cpuSubtype) : TargetInfo(ILP32()) { cpuType = CPU_TYPE_ARM; this->cpuSubtype = cpuSubtype; stubSize = 0 /* FIXME */; stubHelperHeaderSize = 0 /* FIXME */; stubHelperEntrySize = 0 /* FIXME */; } TargetInfo *macho::createARMTargetInfo(uint32_t cpuSubtype) { static ARM t(cpuSubtype); return &t; }