static bool isThumbMovt(uint32_t instruction);
static bool isArmMovw(uint32_t instruction);
static bool isArmMovt(uint32_t instruction);
- static int32_t getDisplacementFromThumbBranch(uint32_t instruction);
+ static int32_t getDisplacementFromThumbBranch(uint32_t instruction, uint32_t);
static int32_t getDisplacementFromArmBranch(uint32_t instruction);
static uint16_t getWordFromThumbMov(uint32_t instruction);
static uint16_t getWordFromArmMov(uint32_t instruction);
static uint32_t clearThumbBit(uint32_t value, const Atom *target);
- static uint32_t setDisplacementInArmBranch(uint32_t instr, int32_t disp);
- static uint32_t setDisplacementInThumbBranch(uint32_t instr, int32_t disp);
+ static uint32_t setDisplacementInArmBranch(uint32_t instr, int32_t disp,
+ bool targetIsThumb);
+ static uint32_t setDisplacementInThumbBranch(uint32_t instr, uint32_t ia,
+ int32_t disp, bool targetThumb);
static uint32_t setWordFromThumbMov(uint32_t instruction, uint16_t word);
static uint32_t setWordFromArmMov(uint32_t instruction, uint16_t word);
void applyFixupFinal(const Reference &ref, uint8_t *location,
uint64_t fixupAddress, uint64_t targetAddress,
- uint64_t inAtomAddress, bool &thumbMode);
+ uint64_t inAtomAddress, bool &thumbMode,
+ bool targetIsThumb);
void applyFixupRelocatable(const Reference &ref, uint8_t *location,
uint64_t fixupAddress,
uint64_t targetAddress,
- uint64_t inAtomAddress, bool &thumbMode);
+ uint64_t inAtomAddress, bool &thumbMode,
+ bool targetIsThumb);
const bool _swap;
};
}
}
-int32_t ArchHandler_arm::getDisplacementFromThumbBranch(uint32_t instruction) {
- uint32_t s = (instruction >> 10) & 0x1;
- uint32_t j1 = (instruction >> 29) & 0x1;
- uint32_t j2 = (instruction >> 27) & 0x1;
- uint32_t imm10 = instruction & 0x3FF;
- uint32_t imm11 = (instruction >> 16) & 0x7FF;
- uint32_t i1 = (j1 == s);
- uint32_t i2 = (j2 == s);
- uint32_t dis =
- (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1);
- int32_t sdis = dis;
- if (s)
- return (sdis | 0xFE000000);
- else
- return sdis;
-}
-
+/// Extract displacement from an ARM b/bl/blx instruction.
int32_t ArchHandler_arm::getDisplacementFromArmBranch(uint32_t instruction) {
// Sign-extend imm24
int32_t displacement = (instruction & 0x00FFFFFF) << 2;
return displacement;
}
+/// Update an ARM b/bl/blx instruction, switching bl <-> blx as needed.
uint32_t ArchHandler_arm::setDisplacementInArmBranch(uint32_t instruction,
- int32_t displacement) {
- // FIXME: handle BLX and out-of-range.
+ int32_t displacement,
+ bool targetIsThumb) {
+ assert((displacement <= 33554428) && (displacement > (-33554432))
+ && "arm branch out of range");
+ bool is_blx = ((instruction & 0xF0000000) == 0xF0000000);
uint32_t newInstruction = (instruction & 0xFF000000);
- newInstruction |= ((displacement >> 2) & 0x00FFFFFF);
+ uint32_t h = 0;
+ if (targetIsThumb) {
+ // Force use of BLX.
+ newInstruction = 0xFA000000;
+ if (!is_blx) {
+ bool isConditionalBranch = ((instruction & 0xF0000000) != 0xE0000000);
+ assert(!isConditionalBranch && "no conditional arm blx");
+ bool is_bl = ((instruction & 0xFF000000) == 0xEB000000);
+ assert(is_bl && "no arm pc-rel BX instruction");
+ }
+ if (displacement & 2)
+ h = 1;
+ }
+ else {
+ // Force use of B/BL.
+ if (is_blx)
+ newInstruction = 0xEB000000;
+ }
+ newInstruction |= (h << 24) | ((displacement >> 2) & 0x00FFFFFF);
return newInstruction;
}
+/// Extract displacement from a thumb b/bl/blx instruction.
+int32_t ArchHandler_arm::getDisplacementFromThumbBranch(uint32_t instruction,
+ uint32_t instrAddr) {
+ bool is_blx = ((instruction & 0xD000F800) == 0xC000F000);
+ uint32_t s = (instruction >> 10) & 0x1;
+ uint32_t j1 = (instruction >> 29) & 0x1;
+ uint32_t j2 = (instruction >> 27) & 0x1;
+ uint32_t imm10 = instruction & 0x3FF;
+ uint32_t imm11 = (instruction >> 16) & 0x7FF;
+ uint32_t i1 = (j1 == s);
+ uint32_t i2 = (j2 == s);
+ uint32_t dis =
+ (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1);
+ int32_t sdis = dis;
+ int32_t result = s ? (sdis | 0xFE000000) : sdis;
+ if (is_blx && (instrAddr & 0x2)) {
+ // The thumb blx instruction always has low bit of imm11 as zero. The way
+ // a 2-byte aligned blx can branch to a 4-byte aligned ARM target is that
+ // the blx instruction always 4-byte aligns the pc before adding the
+ // displacement from the blx. We must emulate that when decoding this.
+ result -= 2;
+ }
+ return result;
+}
+
+/// Update a thumb b/bl/blx instruction, switching bl <-> blx as needed.
uint32_t ArchHandler_arm::setDisplacementInThumbBranch(uint32_t instruction,
- int32_t displacement) {
- // FIXME: handle BLX and out-of-range.
+ uint32_t instrAddr,
+ int32_t displacement,
+ bool targetIsThumb) {
+ assert((displacement <= 16777214) && (displacement > (-16777216))
+ && "thumb branch out of range");
+ bool is_bl = ((instruction & 0xD000F800) == 0xD000F000);
+ bool is_blx = ((instruction & 0xD000F800) == 0xC000F000);
+ bool is_b = ((instruction & 0xD000F800) == 0x9000F000);
uint32_t newInstruction = (instruction & 0xF800D000);
+ if (is_bl || is_blx) {
+ if (targetIsThumb) {
+ newInstruction = 0xD000F000; // Use bl
+ } else {
+ newInstruction = 0xC000F000; // Use blx
+ // See note in getDisplacementFromThumbBranch() about blx.
+ if (instrAddr & 0x2)
+ displacement += 2;
+ }
+ } else if (is_b) {
+ assert(!targetIsThumb && "no pc-rel thumb branch instruction that "
+ "switches to arm mode");
+ }
+ else {
+ llvm_unreachable("thumb branch22 reloc on a non-branch instruction");
+ }
uint32_t s = (uint32_t)(displacement >> 24) & 0x1;
uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1;
uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1;
if (E ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
// Instruction contains branch to addend.
- displacement = getDisplacementFromThumbBranch(instruction);
+ displacement = getDisplacementFromThumbBranch(instruction, fixupAddress);
*addend = fixupAddress + 4 + displacement;
return std::error_code();
case ARM_THUMB_RELOC_BR22 | rPcRel | rLength4:
// ex: bl _foo (and _foo is defined)
*kind = thumb_b22;
- displacement = getDisplacementFromThumbBranch(instruction);
+ displacement = getDisplacementFromThumbBranch(instruction, fixupAddress);
targetAddress = fixupAddress + 4 + displacement;
return atomFromAddress(reloc.symbol, targetAddress, target, addend);
case ARM_THUMB_RELOC_BR22 | rScattered | rPcRel | rLength4:
// ex: bl _foo+4 (and _foo is defined)
*kind = thumb_b22;
- displacement = getDisplacementFromThumbBranch(instruction);
+ displacement = getDisplacementFromThumbBranch(instruction, fixupAddress);
targetAddress = fixupAddress + 4 + displacement;
if (E ec = atomFromAddress(0, reloc.value, target, addend))
return ec;
uint64_t fixupAddress,
uint64_t targetAddress,
uint64_t inAtomAddress,
- bool &thumbMode) {
+ bool &thumbMode, bool targetIsThumb) {
if (ref.kindNamespace() != Reference::KindNamespace::mach_o)
return;
assert(ref.kindArch() == Reference::KindArch::ARM);
int32_t *loc32 = reinterpret_cast<int32_t *>(location);
int32_t displacement;
uint16_t value16;
+ uint32_t value32;
switch (ref.kindValue()) {
case modeThumbCode:
thumbMode = true;
case thumb_b22:
assert(thumbMode);
displacement = (targetAddress - (fixupAddress + 4)) + ref.addend();
- write32(*loc32, _swap, setDisplacementInThumbBranch(*loc32, displacement));
+ value32 = setDisplacementInThumbBranch(*loc32, fixupAddress, displacement,
+ targetIsThumb);
+ write32(*loc32, _swap, value32);
break;
case thumb_movw:
assert(thumbMode);
case arm_b24:
assert(!thumbMode);
displacement = (targetAddress - (fixupAddress + 8)) + ref.addend();
- *loc32 = setDisplacementInArmBranch(*loc32, displacement);
+ value32 = setDisplacementInArmBranch(*loc32, displacement, targetIsThumb);
+ write32(*loc32, _swap, value32);
break;
case arm_movw:
assert(!thumbMode);
write32(*loc32, _swap, setWordFromArmMov(*loc32, value16));
break;
case pointer32:
- write32(*loc32, _swap, targetAddress + ref.addend());
+ if (targetIsThumb)
+ write32(*loc32, _swap, targetAddress + ref.addend() + 1);
+ else
+ write32(*loc32, _swap, targetAddress + ref.addend());
break;
case delta32:
write32(*loc32, _swap, targetAddress - fixupAddress + ref.addend());
uint32_t offset = ref->offsetInAtom();
const Atom *target = ref->target();
uint64_t targetAddress = 0;
- if (isa<DefinedAtom>(target))
+ bool targetIsThumb = false;
+ if (const DefinedAtom *defTarg = dyn_cast<DefinedAtom>(target)) {
targetAddress = findAddress(*target);
+ targetIsThumb = isThumbFunction(*defTarg);
+ }
uint64_t atomAddress = findAddress(atom);
uint64_t fixupAddress = atomAddress + offset;
if (relocatable) {
- applyFixupRelocatable(*ref, &atomContentBuffer[offset],
- fixupAddress, targetAddress,
- atomAddress, thumbMode);
+ applyFixupRelocatable(*ref, &atomContentBuffer[offset], fixupAddress,
+ targetAddress, atomAddress, thumbMode,
+ targetIsThumb);
} else {
- applyFixupFinal(*ref, &atomContentBuffer[offset],
- fixupAddress, targetAddress,
- atomAddress, thumbMode);
+ applyFixupFinal(*ref, &atomContentBuffer[offset], fixupAddress,
+ targetAddress, atomAddress, thumbMode, targetIsThumb);
}
}
}
uint64_t fixupAddress,
uint64_t targetAddress,
uint64_t inAtomAddress,
- bool &thumbMode) {
+ bool &thumbMode,
+ bool targetIsThumb) {
bool useExternalReloc = useExternalRelocationTo(*ref.target());
int32_t *loc32 = reinterpret_cast<int32_t *>(location);
int32_t displacement;
uint16_t value16;
+ uint32_t value32;
switch (ref.kindValue()) {
case modeThumbCode:
thumbMode = true;
displacement = (ref.addend() - (fixupAddress + 4));
else
displacement = (targetAddress - (fixupAddress + 4)) + ref.addend();
- write32(*loc32, _swap, setDisplacementInThumbBranch(*loc32, displacement));
+ value32 = setDisplacementInThumbBranch(*loc32, fixupAddress, displacement,
+ targetIsThumb);
+ write32(*loc32, _swap, value32);
break;
case thumb_movw:
assert(thumbMode);
displacement = (ref.addend() - (fixupAddress + 8));
else
displacement = (targetAddress - (fixupAddress + 8)) + ref.addend();
- write32(*loc32, _swap, setDisplacementInArmBranch(*loc32, displacement));
+ value32 = setDisplacementInArmBranch(*loc32, displacement, targetIsThumb);
+ write32(*loc32, _swap, value32);
break;
case arm_movw:
assert(!thumbMode);
--- /dev/null
+# RUN: lld -flavor darwin -arch armv7 -r -print_atoms %s -o %t | FileCheck %s \
+# RUN: && lld -flavor darwin -arch armv7 -dylib -print_atoms %t -o %t2 | FileCheck %s \
+# RUN: && macho-dump --dump-section-data %t2 | FileCheck -check-prefix=CODE %s
+#
+# Test thumb and arm branches round trip through -r.
+# Test bl/blx instructions are fixed up properly.
+#
+#
+
+--- !mach-o
+arch: armv7
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 2
+ address: 0x0000000000000000
+ content: [ 0xFF, 0xF7, 0xFE, 0xFF, 0xC0, 0x46, 0xFF, 0xF7,
+ 0xFC, 0xEF, 0xC0, 0x46, 0xFF, 0xF7, 0xF8, 0xEF,
+ 0xFF, 0xF7, 0xF6, 0xFF, 0xC0, 0x46, 0xFF, 0xF7,
+ 0xF3, 0xFF, 0xC0, 0x46, 0x00, 0xF0, 0x06, 0xE8,
+ 0xC0, 0x46, 0x00, 0xF0, 0x03, 0xF8, 0x00, 0xF0,
+ 0x02, 0xF8, 0x70, 0x47, 0x70, 0x47, 0x70, 0x47 ]
+ relocations:
+ - offset: 0x00000026
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: false
+ symbol: 1
+ - offset: 0x00000022
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: false
+ symbol: 1
+ - offset: 0x0000001C
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: false
+ symbol: 1
+ - offset: 0x00000016
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: false
+ symbol: 1
+ - offset: 0x00000010
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: false
+ symbol: 1
+ - offset: 0x0000000C
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 5
+ - offset: 0x00000006
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 5
+ - offset: 0x00000000
+ type: ARM_THUMB_RELOC_BR22
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 4
+ - segment: __DATA
+ section: __data
+ type: S_REGULAR
+ attributes: [ ]
+ address: 0x0000000000000030
+ content: [ 0x2D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000004
+ type: ARM_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 4
+ - offset: 0x00000000
+ type: ARM_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 1
+local-symbols:
+ - name: _t3
+ type: N_SECT
+ sect: 1
+ desc: [ N_ARM_THUMB_DEF ]
+ value: 0x000000000000002E
+ - name: _d1
+ type: N_SECT
+ sect: 2
+ value: 0x0000000000000030
+global-symbols:
+ - name: _t1
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ desc: [ N_ARM_THUMB_DEF ]
+ value: 0x0000000000000000
+ - name: _t2
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ desc: [ N_ARM_THUMB_DEF ]
+ value: 0x000000000000002C
+undefined-symbols:
+ - name: _a1
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _a2
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+
+--- !mach-o
+arch: armv7
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 2
+ address: 0x0000000000000000
+ content: [ 0xFE, 0xFF, 0xFF, 0xEB, 0x02, 0x00, 0x00, 0xFA,
+ 0xFC, 0xFF, 0xFF, 0xEB, 0xFB, 0xFF, 0xFF, 0xFA,
+ 0x1E, 0xFF, 0x2F, 0xE1, 0x1E, 0xFF, 0x2F, 0xE1 ]
+ relocations:
+ - offset: 0x0000000C
+ type: ARM_RELOC_BR24
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 4
+ - offset: 0x00000008
+ type: ARM_RELOC_BR24
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 3
+ - offset: 0x00000004
+ type: ARM_RELOC_BR24
+ length: 2
+ pc-rel: true
+ extern: false
+ symbol: 1
+ - offset: 0x00000000
+ type: ARM_RELOC_BR24
+ length: 2
+ pc-rel: true
+ extern: false
+ symbol: 1
+ - segment: __DATA
+ section: __data
+ type: S_REGULAR
+ attributes: [ ]
+ address: 0x0000000000000018
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000004
+ type: ARM_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x00000000
+ type: ARM_RELOC_VANILLA
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 3
+local-symbols:
+ - name: _d2
+ type: N_SECT
+ sect: 2
+ value: 0x0000000000000018
+global-symbols:
+ - name: _a1
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+ - name: _a2
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000014
+undefined-symbols:
+ - name: _t1
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _t2
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+...
+
+
+# CHECK: defined-atoms:
+# CHECK: - name: _d1
+# CHECK: type: data
+# CHECK: references:
+# CHECK: - kind: pointer32
+# CHECK: offset: 0
+# CHECK: target: _t2
+# CHECK: - kind: pointer32
+# CHECK: offset: 4
+# CHECK: target: _a1
+# CHECK: - name: _d2
+# CHECK: type: data
+# CHECK: references:
+# CHECK: - kind: pointer32
+# CHECK: offset: 0
+# CHECK: target: _t1
+# CHECK: - kind: pointer32
+# CHECK: offset: 4
+# CHECK: target: _a1
+# CHECK: - name: _t1
+# CHECK: scope: global
+# CHECK: references:
+# CHECK: - kind: modeThumbCode
+# CHECK: offset: 0
+# CHECK: target: _t1
+# CHECK: - kind: thumb_b22
+# CHECK: offset: 0
+# CHECK: target: _a1
+# CHECK: - kind: thumb_b22
+# CHECK: offset: 6
+# CHECK: target: _a2
+# CHECK: - kind: thumb_b22
+# CHECK: offset: 12
+# CHECK: target: _a2
+# CHECK: - kind: thumb_b22
+# CHECK: offset: 16
+# CHECK: target: _t1
+# CHECK: - kind: thumb_b22
+# CHECK: offset: 22
+# CHECK: target: _t1
+# CHECK: - kind: thumb_b22
+# CHECK: offset: 28
+# CHECK: target: _t2
+# CHECK: - kind: thumb_b22
+# CHECK: offset: 34
+# CHECK: target: _t2
+# CHECK: - kind: thumb_b22
+# CHECK: offset: 38
+# CHECK: target: _t3
+# CHECK: - name: _t2
+# CHECK: scope: global
+# CHECK: content: [ 70, 47 ]
+# CHECK: references:
+# CHECK: - kind: modeThumbCode
+# CHECK: offset: 0
+# CHECK: target: _t2
+# CHECK: - name: _t3
+# CHECK: content: [ 70, 47 ]
+# CHECK: references:
+# CHECK: - kind: modeThumbCode
+# CHECK: offset: 0
+# CHECK: target: _t3
+# CHECK: - name: _a1
+# CHECK: scope: global
+# CHECK: references:
+# CHECK: - kind: arm_b24
+# CHECK: offset: 0
+# CHECK: target: _a1
+# CHECK: - kind: arm_b24
+# CHECK: offset: 4
+# CHECK: target: _a2
+# CHECK: - kind: arm_b24
+# CHECK: offset: 8
+# CHECK: target: _t1
+# CHECK: - kind: arm_b24
+# CHECK: offset: 12
+# CHECK: target: _t2
+# CHECK: - name: _a2
+# CHECK: scope: global
+
+
+# CODE: (('section_name', '__text\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
+# CODE: ('segment_name', '__TEXT\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
+# CODE: ('_section_data', '00f016e8 c04600f0 1ee8c046 00f01ae8 fff7f6ff c046fff7 f3ffc046 00f006f8 c04600f0 03f800f0 02f87047 70477047 feffffeb 020000eb f0fffffa fafffffa 1eff2fe1 1eff2fe1')
+# When we get a good mach-o disassembler the above __text section content check can be change to be symbolic.
+
+# CODE: (('section_name', '__data\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
+# CODE: ('segment_name', '__DATA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
+# CODE: ('_section_data', '{{[0-9a-f]}}{{[13579bdf]}}{{[0-9a-f]+}} {{[0-9a-f]}}{{[02468ade]}}{{[0-9a-f]+}} {{[0-9a-f]}}{{[13579bdf]}}{{[0-9a-f]+}} {{[0-9a-f]}}{{[02468ade]}}{{[0-9a-f]+}}')
+# Verify the low (thumb) bit is set on the first and third pointers but not the second and fourth.
+
+
+
+# Input file one:
+#
+# .align 2
+# .code 16
+# .globl _t1
+# .thumb_func _t1
+#_t1:
+# bl _a1
+# nop
+# blx _a2
+# nop
+# blx _a2
+# bl _t1
+# nop
+# bl _t1
+# nop
+# blx _t2
+# nop
+# blx _t2
+# bx lr
+#
+# .globl _t2
+# .thumb_func _t2
+#_t2:
+# bx lr
+#
+# .data
+#_d1: .long _t2
+# .long _a1
+
+
+
+# Input file two:
+#
+# .align 2
+# .code 32
+# .globl _a1
+#_a1:
+# bl _a1
+# blx _a2
+# bl _t1
+# blx _t2
+# bx lr
+#
+# .globl _a2
+#_a2:
+# bx lr
+#
+# .data
+#_d2: .long _t1
+# .long _a1
+
+
+
+
\ No newline at end of file