diff --git a/lld/MachO/Arch/ARM.cpp b/lld/MachO/Arch/ARM.cpp --- a/lld/MachO/Arch/ARM.cpp +++ b/lld/MachO/Arch/ARM.cpp @@ -12,9 +12,11 @@ #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; @@ -28,7 +30,7 @@ int64_t getEmbeddedAddend(const char *loc, uint32_t relType, uint32_t relLength) const override; void relocateOne(uint8_t *loc, const Reloc &, uint64_t va, - uint64_t relocVA) const override; + uint64_t pc) const override; void writeStub(uint8_t *buf, const Symbol &) const override; void writeStubHelperHeader(uint8_t *buf) const override; @@ -50,8 +52,8 @@ {"SECTDIFF", /* FIXME populate this */ B(_0)}, {"LOCAL_SECTDIFF", /* FIXME populate this */ B(_0)}, {"PB_LA_PTR", /* FIXME populate this */ B(_0)}, - {"BR24", /* FIXME populate this */ B(_0)}, - {"BR22", /* 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)}, @@ -63,14 +65,79 @@ return relocAttrsArray[type]; } +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; + int64_t ARM::getEmbeddedAddend(const char *loc, uint32_t relType, uint32_t relLength) const { - fatal("TODO: implement this"); + // FIXME: implement this + return 0; } void ARM::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value, - uint64_t relocVA) const { - fatal("TODO: implement this"); + 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 { diff --git a/lld/test/MachO/arm-branch-relocs.s b/lld/test/MachO/arm-branch-relocs.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/arm-branch-relocs.s @@ -0,0 +1,33 @@ +# REQUIRES: arm +# RUN: llvm-mc -filetype=obj -triple=armv7-apple-watchos %s -o %t.o +# RUN: %lld-watchos -dylib -arch armv7 -lSystem -o %t %t.o +# RUN: llvm-objdump --macho -d %t | FileCheck %s + +# CHECK: _arm: +# CHECK-NEXT: blx _thumb_1 +# CHECK-NEXT: blx _thumb_2 +# CHECK-NEXT: bl _arm +# CHECK-NEXT: bl _arm + +.globl _arm, _thumb_1, _thumb_2 +.syntax unified +.thumb_func _thumb_1 +.thumb_func _thumb_2 + +.p2align 2 + +.code 16 +## These two functions are exactly 2 bytes apart in order to test that we set +## the H bit correctly in the BLX instruction. +_thumb_1: + nop + +_thumb_2: + nop + +.code 32 +_arm: + blx _thumb_1 + blx _thumb_2 + bl _arm + blx _arm