[mach-o] Implement interworking between thumb and arm code

All iOS arm processor support switching between arm and thumb mode at call sites
by using the BLX instruction (instead of BL).  But the compiler does not know
the implementation mode for extern functions, so the linker must update BL/BLX
instructions to match what is linked is actually linked together.  In addition,
pointers to functions (such as vtables) must have the low bit set if the target
of the pointer is a thumb mode function.

llvm-svn: 214140
This commit is contained in:
Nick Kledzik 2014-07-28 23:06:09 +00:00
parent bd1628a595
commit 54fd4e5fcb
4 changed files with 584 additions and 45 deletions

View File

@ -130,13 +130,15 @@ private:
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);
@ -144,12 +146,14 @@ private:
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;
};
@ -256,23 +260,7 @@ bool ArchHandler_arm::isPairedReloc(const Relocation &reloc) {
}
}
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;
@ -284,18 +272,88 @@ int32_t ArchHandler_arm::getDisplacementFromArmBranch(uint32_t instruction) {
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;
@ -392,19 +450,19 @@ std::error_code ArchHandler_arm::getReferenceInfo(
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;
@ -745,13 +803,14 @@ void ArchHandler_arm::applyFixupFinal(const Reference &ref, uint8_t *location,
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;
@ -764,7 +823,9 @@ void ArchHandler_arm::applyFixupFinal(const Reference &ref, uint8_t *location,
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);
@ -789,7 +850,8 @@ void ArchHandler_arm::applyFixupFinal(const Reference &ref, uint8_t *location,
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);
@ -812,7 +874,10 @@ void ArchHandler_arm::applyFixupFinal(const Reference &ref, uint8_t *location,
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());
@ -839,18 +904,20 @@ void ArchHandler_arm::generateAtomContent(const DefinedAtom &atom,
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);
}
}
}
@ -882,11 +949,13 @@ void ArchHandler_arm::applyFixupRelocatable(const Reference &ref,
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;
@ -902,7 +971,9 @@ void ArchHandler_arm::applyFixupRelocatable(const Reference &ref,
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);
@ -936,7 +1007,8 @@ void ArchHandler_arm::applyFixupRelocatable(const Reference &ref,
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);

View File

@ -563,7 +563,10 @@ void Util::copySections(NormalizedFile &file) {
void Util::copyEntryPointAddress(NormalizedFile &nFile) {
if (_context.outputTypeHasEntry()) {
nFile.entryAddress = _atomToAddress[_entryAtom];
if (_archHandler.isThumbFunction(*_entryAtom))
nFile.entryAddress = (_atomToAddress[_entryAtom] | 1);
else
nFile.entryAddress = _atomToAddress[_entryAtom];
}
}

View File

@ -0,0 +1,361 @@
# 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

View File

@ -0,0 +1,103 @@
# RUN: lld -flavor darwin -arch armv7 -ios_version_min 7.0 %s -o %t && \
# RUN: llvm-nm %t | FileCheck %s
#
# Test that armv7 (thumb) hello-world can be linked into a mach-o executable
#
--- !mach-o
arch: armv7
file-type: MH_OBJECT
flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
has-UUID: false
OS: unknown
sections:
- segment: __TEXT
section: __text
type: S_REGULAR
attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
alignment: 2
address: 0x0000000000000000
content: [ 0x80, 0xB5, 0x40, 0xF2, 0x06, 0x00, 0x6F, 0x46,
0xC0, 0xF2, 0x00, 0x00, 0x78, 0x44, 0xFF, 0xF7,
0xF8, 0xEF, 0x00, 0x20, 0x80, 0xBD ]
relocations:
- offset: 0x0000000E
type: ARM_THUMB_RELOC_BR22
length: 2
pc-rel: true
extern: true
symbol: 1
- offset: 0x00000008
scattered: true
type: ARM_RELOC_HALF_SECTDIFF
length: 3
pc-rel: false
value: 0x00000016
- offset: 0x00000006
scattered: true
type: ARM_RELOC_PAIR
length: 3
pc-rel: false
value: 0x0000000C
- offset: 0x00000002
scattered: true
type: ARM_RELOC_HALF_SECTDIFF
length: 2
pc-rel: false
value: 0x00000016
- offset: 0x00000000
scattered: true
type: ARM_RELOC_PAIR
length: 2
pc-rel: false
value: 0x0000000C
- segment: __TEXT
section: __cstring
type: S_CSTRING_LITERALS
attributes: [ ]
address: 0x0000000000000016
content: [ 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x00 ]
global-symbols:
- name: _main
type: N_SECT
scope: [ N_EXT ]
sect: 1
desc: [ N_ARM_THUMB_DEF ]
value: 0x0000000000000000
undefined-symbols:
- name: _printf
type: N_UNDF
scope: [ N_EXT ]
value: 0x0000000000000000
...
--- !mach-o
arch: armv7
file-type: MH_DYLIB
flags: [ ]
install-name: /usr/lib/libSystem.B.dylib
sections:
- segment: __TEXT
section: __text
type: S_REGULAR
attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
address: 0x0000000000000000
content: [ 0x55 ]
global-symbols:
- name: _printf
type: N_SECT
scope: [ N_EXT ]
sect: 1
value: 0x0000000000000001
- name: dyld_stub_binder
type: N_SECT
scope: [ N_EXT ]
sect: 1
value: 0x0000000000000001
...
# CHECK: {{[0-9a-f]+}} T _main
# CHECK: U _printf
# CHECK: U dyld_stub_binder