Skip to content

Commit 6f9ff1b

Browse files
committed
[lld][ARM] support position independent thunks for Armv4(T)
- Position independent thunks now work for both Armv4 and Armv4T - Armv4 arm->arm thunks don't emit a BX anymore, which doesn't exist for the arch. This fixes #50764. - Armv4 and Armv4T both have the same arm->arm behaviour. Which also is desirable for the above ticket. Reviewed By: MaskRay, peter.smith Differential Revision: https://reviews.llvm.org/D141272
1 parent d5bee44 commit 6f9ff1b

File tree

6 files changed

+292
-19
lines changed

6 files changed

+292
-19
lines changed

lld/ELF/Thunks.cpp

Lines changed: 106 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -199,17 +199,46 @@ class ARMV5LongLdrPcThunk final : public ARMThunk {
199199
void addSymbols(ThunkSection &isec) override;
200200
};
201201

202+
// Implementations of Thunks for v4. BLX is not supported, and loads
203+
// will not invoke Arm/Thumb state changes.
204+
class ARMV4PILongBXThunk final : public ARMThunk {
205+
public:
206+
ARMV4PILongBXThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {}
207+
208+
uint32_t sizeLong() override { return 16; }
209+
void writeLong(uint8_t *buf) override;
210+
void addSymbols(ThunkSection &isec) override;
211+
};
212+
202213
class ARMV4PILongThunk final : public ARMThunk {
203214
public:
204215
ARMV4PILongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {}
205216

217+
uint32_t sizeLong() override { return 12; }
218+
void writeLong(uint8_t *buf) override;
219+
void addSymbols(ThunkSection &isec) override;
220+
};
221+
222+
class ThumbV4PILongBXThunk final : public ThumbThunk {
223+
public:
224+
ThumbV4PILongBXThunk(Symbol &dest, int64_t addend)
225+
: ThumbThunk(dest, addend) {}
226+
206227
uint32_t sizeLong() override { return 16; }
207228
void writeLong(uint8_t *buf) override;
208229
void addSymbols(ThunkSection &isec) override;
209230
};
210231

211-
// Implementations of Thunks for v4. BLX is not supported, and loads
212-
// will not invoke Arm/Thumb state changes.
232+
class ThumbV4PILongThunk final : public ThumbThunk {
233+
public:
234+
ThumbV4PILongThunk(Symbol &dest, int64_t addend)
235+
: ThumbThunk(dest, addend) {}
236+
237+
uint32_t sizeLong() override { return 20; }
238+
void writeLong(uint8_t *buf) override;
239+
void addSymbols(ThunkSection &isec) override;
240+
};
241+
213242
class ARMV4ABSLongBXThunk final : public ARMThunk {
214243
public:
215244
ARMV4ABSLongBXThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {}
@@ -788,7 +817,7 @@ void ThumbV4ABSLongThunk::addSymbols(ThunkSection &isec) {
788817
addSymbol("$d", STT_NOTYPE, 12, isec);
789818
}
790819

791-
void ARMV4PILongThunk::writeLong(uint8_t *buf) {
820+
void ARMV4PILongBXThunk::writeLong(uint8_t *buf) {
792821
const uint8_t data[] = {
793822
0x04, 0xc0, 0x9f, 0xe5, // P: ldr ip, [pc,#4] ; L2
794823
0x0c, 0xc0, 0x8f, 0xe0, // L1: add ip, pc, ip
@@ -801,13 +830,77 @@ void ARMV4PILongThunk::writeLong(uint8_t *buf) {
801830
target->relocateNoSym(buf + 12, R_ARM_REL32, s - p - 12);
802831
}
803832

833+
void ARMV4PILongBXThunk::addSymbols(ThunkSection &isec) {
834+
addSymbol(saver().save("__ARMv4PILongBXThunk_" + destination.getName()),
835+
STT_FUNC, 0, isec);
836+
addSymbol("$a", STT_NOTYPE, 0, isec);
837+
addSymbol("$d", STT_NOTYPE, 12, isec);
838+
}
839+
840+
void ARMV4PILongThunk::writeLong(uint8_t *buf) {
841+
const uint8_t data[] = {
842+
0x00, 0xc0, 0x9f, 0xe5, // P: ldr ip, [pc] ; L2
843+
0x0c, 0xf0, 0x8f, 0xe0, // L1: add pc, pc, r12
844+
0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 8)
845+
};
846+
uint64_t s = getARMThunkDestVA(destination);
847+
uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
848+
memcpy(buf, data, sizeof(data));
849+
target->relocateNoSym(buf + 8, R_ARM_REL32, s - p - 12);
850+
}
851+
804852
void ARMV4PILongThunk::addSymbols(ThunkSection &isec) {
805853
addSymbol(saver().save("__ARMv4PILongThunk_" + destination.getName()),
806854
STT_FUNC, 0, isec);
807855
addSymbol("$a", STT_NOTYPE, 0, isec);
856+
addSymbol("$d", STT_NOTYPE, 8, isec);
857+
}
858+
859+
void ThumbV4PILongBXThunk::writeLong(uint8_t *buf) {
860+
const uint8_t data[] = {
861+
0x78, 0x47, // P: bx pc
862+
0xfd, 0xe7, // b #-6 ; Arm recommended sequence to follow bx pc
863+
0x00, 0xc0, 0x9f, 0xe5, // ldr r12, [pc] ; L2
864+
0x0f, 0xf0, 0x8c, 0xe0, // L1: add pc, r12, pc
865+
0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 8)
866+
};
867+
uint64_t s = getARMThunkDestVA(destination);
868+
uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
869+
memcpy(buf, data, sizeof(data));
870+
target->relocateNoSym(buf + 12, R_ARM_REL32, s - p - 16);
871+
}
872+
873+
void ThumbV4PILongBXThunk::addSymbols(ThunkSection &isec) {
874+
addSymbol(saver().save("__Thumbv4PILongBXThunk_" + destination.getName()),
875+
STT_FUNC, 1, isec);
876+
addSymbol("$t", STT_NOTYPE, 0, isec);
877+
addSymbol("$a", STT_NOTYPE, 4, isec);
808878
addSymbol("$d", STT_NOTYPE, 12, isec);
809879
}
810880

881+
void ThumbV4PILongThunk::writeLong(uint8_t *buf) {
882+
const uint8_t data[] = {
883+
0x78, 0x47, // P: bx pc
884+
0xfd, 0xe7, // b #-6 ; Arm recommended sequence to follow bx pc
885+
0x04, 0xc0, 0x9f, 0xe5, // ldr ip, [pc,#4] ; L2
886+
0x0c, 0xc0, 0x8f, 0xe0, // L1: add ip, pc, ip
887+
0x1c, 0xff, 0x2f, 0xe1, // bx ip
888+
0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 8)
889+
};
890+
uint64_t s = getARMThunkDestVA(destination);
891+
uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
892+
memcpy(buf, data, sizeof(data));
893+
target->relocateNoSym(buf + 16, R_ARM_REL32, s - p - 16);
894+
}
895+
896+
void ThumbV4PILongThunk::addSymbols(ThunkSection &isec) {
897+
addSymbol(saver().save("__Thumbv4PILongThunk_" + destination.getName()),
898+
STT_FUNC, 1, isec);
899+
addSymbol("$t", STT_NOTYPE, 0, isec);
900+
addSymbol("$a", STT_NOTYPE, 4, isec);
901+
addSymbol("$d", STT_NOTYPE, 16, isec);
902+
}
903+
811904
// Write MIPS LA25 thunk code to call PIC function from the non-PIC one.
812905
void MipsThunk::writeTo(uint8_t *buf) {
813906
uint64_t s = destination.getVA();
@@ -1143,8 +1236,6 @@ static Thunk *addThunkAArch64(RelType type, Symbol &s, int64_t a) {
11431236
// - MOVT and MOVW instructions cannot be used.
11441237
// - We can't rewrite BL in place to BLX. We will need thunks.
11451238
//
1146-
// TODO: Support PIC interworking thunks for V4T.
1147-
// TODO: More efficient PIC non-interworking thunks for V4T.
11481239
// TODO: use B for short Thumb->Arm thunks instead of LDR (this doesn't work for
11491240
// Arm->Thumb, as in Arm state no BX PC trick; it doesn't switch state).
11501241
static Thunk *addThunkArmv4(RelType reloc, Symbol &s, int64_t a) {
@@ -1155,17 +1246,20 @@ static Thunk *addThunkArmv4(RelType reloc, Symbol &s, int64_t a) {
11551246
case R_ARM_PLT32:
11561247
case R_ARM_JUMP24:
11571248
case R_ARM_CALL:
1158-
if (config->picThunk)
1159-
// can be used for both Arm->Arm and Arm->Thumb
1249+
if (config->picThunk) {
1250+
if (thumb_target)
1251+
return make<ARMV4PILongBXThunk>(s, a);
11601252
return make<ARMV4PILongThunk>(s, a);
1253+
}
11611254
if (thumb_target)
11621255
return make<ARMV4ABSLongBXThunk>(s, a);
11631256
return make<ARMV5LongLdrPcThunk>(s, a);
11641257
case R_ARM_THM_CALL:
1165-
if (config->picThunk && !thumb_target)
1166-
fatal("PIC relocations across state change not supported for Armv4T");
1167-
if (config->picThunk && thumb_target)
1168-
return make<ThumbV6MPILongThunk>(s, a);
1258+
if (config->picThunk) {
1259+
if (thumb_target)
1260+
return make<ThumbV4PILongThunk>(s, a);
1261+
return make<ThumbV4PILongBXThunk>(s, a);
1262+
}
11691263
if (thumb_target)
11701264
return make<ThumbV4ABSLongThunk>(s, a);
11711265
return make<ThumbV4ABSLongBXThunk>(s, a);
@@ -1187,7 +1281,7 @@ static Thunk *addThunkArmv5v6(RelType reloc, Symbol &s, int64_t a) {
11871281
case R_ARM_CALL:
11881282
case R_ARM_THM_CALL:
11891283
if (config->picThunk)
1190-
return make<ARMV4PILongThunk>(s, a);
1284+
return make<ARMV4PILongBXThunk>(s, a);
11911285
return make<ARMV5LongLdrPcThunk>(s, a);
11921286
}
11931287
fatal("relocation " + toString(reloc) + " to " + toString(s) +

lld/test/ELF/arm-bl-v4.s

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// REQUIRES: arm
2+
// RUN: rm -rf %t && split-file %s %t
3+
// RUN: llvm-mc -arm-add-build-attributes -filetype=obj -triple=armv4-none-linux-gnueabi %t/a.s -o %t/a.o
4+
// RUN: ld.lld %t/a.o --script %t/far.lds -o %t/a-far
5+
// RUN: llvm-objdump --no-print-imm-hex -d --no-show-raw-insn --triple=armv4-none-linux-gnueabi %t/a-far | FileCheck %s --check-prefixes=FAR
6+
// RUN: ld.lld %t/a.o --script %t/near.lds -o %t/a-near
7+
// RUN: llvm-objdump --no-print-imm-hex -d --no-show-raw-insn --triple=armv4-none-linux-gnueabi %t/a-near | FileCheck %s --check-prefixes=NEAR
8+
// RUN: ld.lld %t/a.o -pie --script %t/far.lds -o %t/a-far-pie
9+
// RUN: llvm-objdump --no-print-imm-hex -d --no-show-raw-insn --triple=armv4-none-linux-gnueabi %t/a-far-pie | FileCheck %s --check-prefixes=FAR-PIE
10+
// RUN: ld.lld %t/a.o -pie --script %t/near.lds -o %t/a-near-pie
11+
// RUN: llvm-objdump --no-print-imm-hex -d --no-show-raw-insn --triple=armv4-none-linux-gnueabi %t/a-near-pie | FileCheck %s --check-prefixes=NEAR
12+
13+
/// On Armv4 there is no blx instruction so long branch/exchange looks slightly
14+
/// different.
15+
16+
//--- a.s
17+
.text
18+
.syntax unified
19+
.cpu arm7tdmi
20+
21+
.section .low, "ax", %progbits
22+
.arm
23+
.globl _start
24+
.type _start,%function
25+
.p2align 2
26+
_start:
27+
bl target
28+
mov pc, lr
29+
30+
// FAR-LABEL: <_start>:
31+
// FAR-NEXT: 1000000: bl 0x1000008 <__ARMv5LongLdrPcThunk_target> @ imm = #0
32+
// FAR-NEXT: mov pc, lr
33+
// FAR-EMPTY:
34+
// FAR-NEXT: <__ARMv5LongLdrPcThunk_target>:
35+
// FAR-NEXT: 1000008: ldr pc, [pc, #-4] @ 0x100000c <__ARMv5LongLdrPcThunk_target+0x4>
36+
// FAR-EMPTY:
37+
// FAR-NEXT: <$d>:
38+
// FAR-NEXT: 100000c: 00 00 00 06 .word 0x06000000
39+
40+
// FAR-PIE-LABEL: <_start>:
41+
// FAR-PIE-NEXT: 1000000: bl 0x1000008 <__ARMv4PILongThunk_target> @ imm = #0
42+
// FAR-PIE-NEXT: mov pc, lr
43+
// FAR-PIE-EMPTY:
44+
// FAR-PIE-NEXT: <__ARMv4PILongThunk_target>:
45+
// FAR-PIE-NEXT: 1000008: ldr r12, [pc] @ 0x1000010 <__ARMv4PILongThunk_target+0x8>
46+
// FAR-PIE-NEXT: add pc, pc, r12
47+
// FAR-PIE-EMPTY:
48+
// FAR-PIE-NEXT: <$d>:
49+
// FAR-PIE-NEXT: 1000010: ec ff ff 04 .word 0x04ffffec
50+
51+
// NEAR-LABEL: <_start>:
52+
// NEAR-NEXT: 1000000: bl 0x1000008 <target> @ imm = #0
53+
// NEAR-NEXT: mov pc, lr
54+
55+
.section .high, "ax", %progbits
56+
.arm
57+
.globl target
58+
.type target,%function
59+
target:
60+
mov pc, lr
61+
62+
// FAR-LABEL: <target>:
63+
// FAR-NEXT: 6000000: mov pc, lr
64+
65+
// FAR-PIE-LABEL: <target>:
66+
// FAR-PIE-NEXT: 6000000: mov pc, lr
67+
68+
// NEAR-LABEL: <target>:
69+
// NEAR-LABEL: 1000008: mov pc, lr
70+
71+
//--- far.lds
72+
SECTIONS {
73+
. = SIZEOF_HEADERS;
74+
.low 0x01000000 : { *(.low) }
75+
.high 0x06000000 : { *(.high) }
76+
}
77+
78+
//--- near.lds
79+
SECTIONS {
80+
. = SIZEOF_HEADERS;
81+
.all 0x01000000 : { *(.low) *(.high) }
82+
}

lld/test/ELF/arm-bl-v4t.s

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
// RUN: llvm-objdump --no-print-imm-hex -d --no-show-raw-insn --triple=armv4t-none-linux-gnueabi %t/a-far | FileCheck %s --check-prefixes=FAR
66
// RUN: ld.lld %t/a.o --script %t/near.lds -o %t/a-near
77
// RUN: llvm-objdump --no-print-imm-hex -d --no-show-raw-insn --triple=armv4t-none-linux-gnueabi %t/a-near | FileCheck %s --check-prefixes=NEAR
8+
// RUN: ld.lld %t/a.o -pie --script %t/far.lds -o %t/a-far-pie
9+
// RUN: llvm-objdump --no-print-imm-hex -d --no-show-raw-insn --triple=armv4t-none-linux-gnueabi %t/a-far-pie | FileCheck %s --check-prefixes=FAR-PIE
10+
// RUN: ld.lld %t/a.o -pie --script %t/near.lds -o %t/a-near-pie
11+
// RUN: llvm-objdump --no-print-imm-hex -d --no-show-raw-insn --triple=armv4t-none-linux-gnueabi %t/a-near-pie | FileCheck %s --check-prefixes=NEAR
812

913
/// On Armv4T there is no blx instruction so long branch/exchange looks slightly
1014
/// different.
@@ -57,6 +61,34 @@ thumb_start:
5761
// FAR-NEXT: <$d>:
5862
// FAR-NEXT: 1000024: 05 00 00 06 .word 0x06000005
5963

64+
// FAR-PIE-LABEL: <_start>:
65+
// FAR-PIE-NEXT: 1000000: bl 0x1000010 <__ARMv4PILongThunk_target> @ imm = #8
66+
// FAR-PIE-NEXT: bx lr
67+
// FAR-PIE-EMPTY:
68+
// FAR-PIE-NEXT: <thumb_start>:
69+
// FAR-PIE-NEXT: 1000008: bl 0x100001c <__Thumbv4PILongThunk_thumb_target> @ imm = #16
70+
// FAR-PIE-NEXT: bx lr
71+
// FAR-PIE-NEXT: bmi 0xffffba @ imm = #-88
72+
// FAR-PIE-EMPTY:
73+
// FAR-PIE-NEXT: <__ARMv4PILongThunk_target>:
74+
// FAR-PIE-NEXT: 1000010: ldr r12, [pc] @ 0x1000018 <__ARMv4PILongThunk_target+0x8>
75+
// FAR-PIE-NEXT: add pc, pc, r12
76+
// FAR-PIE-EMPTY:
77+
// FAR-PIE-NEXT: <$d>:
78+
// FAR-PIE-NEXT: 1000018: e4 ff ff 04 .word 0x04ffffe4
79+
// FAR-PIE-EMPTY:
80+
// FAR-PIE-NEXT: <__Thumbv4PILongThunk_thumb_target>:
81+
// FAR-PIE-NEXT: 100001c: bx pc
82+
// FAR-PIE-NEXT: b 0x100001c <__Thumbv4PILongThunk_thumb_target> @ imm = #-6
83+
// FAR-PIE-EMPTY:
84+
// FAR-PIE-NEXT: <$a>:
85+
// FAR-PIE-NEXT: 1000020: ldr r12, [pc, #4] @ 0x100002c <__Thumbv4PILongThunk_thumb_target+0x10>
86+
// FAR-PIE-NEXT: add r12, pc, r12
87+
// FAR-PIE-NEXT: bx r12
88+
// FAR-PIE-EMPTY:
89+
// FAR-PIE-NEXT: <$d>:
90+
// FAR-PIE-NEXT: 100002c: d9 ff ff 04 .word 0x04ffffd9
91+
6092
// NEAR-LABEL: <_start>:
6193
// NEAR-NEXT: 1000000: bl 0x100000c <thumb_start+0x4> @ imm = #4
6294
// NEAR-NEXT: bx lr
@@ -84,6 +116,12 @@ thumb_target:
84116
// FAR-LABEL: <thumb_target>:
85117
// FAR-NEXT: 6000004: bx lr
86118

119+
// FAR-PIE-LABEL: <target>:
120+
// FAR-PIE-NEXT: 6000000: bx lr
121+
// FAR-PIE-EMPTY:
122+
// FAR-PIE-LABEL: <thumb_target>:
123+
// FAR-PIE-NEXT: 6000004: bx lr
124+
87125
// NEAR-LABEL: <target>:
88126
// NEAR-LABEL: 100000e: bx lr
89127
// NEAR-EMPTY:

0 commit comments

Comments
 (0)