Skip to content

Commit 0b4a047

Browse files
committed
[LLD][ELF][ARM] Do not substitute BL/BLX for non STT_FUNC symbols.
D73474 disabled the generation of interworking thunks for branch relocations to non STT_FUNC symbols. This patch handles the case of BL and BLX instructions to non STT_FUNC symbols. LLD would normally look at the state of the caller and the callee and write a BL if the states are the same and a BLX if the states are different. This patch disables BL/BLX substitution when the destination symbol does not have type STT_FUNC. This brings our behavior in line with GNU ld which may prevent difficult to diagnose runtime errors when switching to lld. Differential Revision: https://reviews.llvm.org/D73542
1 parent 63c8972 commit 0b4a047

File tree

4 files changed

+98
-30
lines changed

4 files changed

+98
-30
lines changed

lld/ELF/Arch/ARM.cpp

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -402,23 +402,27 @@ void ARM::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
402402
checkInt(loc, val, 31, rel);
403403
write32le(loc, (read32le(loc) & 0x80000000) | (val & ~0x80000000));
404404
break;
405-
case R_ARM_CALL:
406-
// R_ARM_CALL is used for BL and BLX instructions, depending on the
407-
// value of bit 0 of Val, we must select a BL or BLX instruction
408-
if (val & 1) {
409-
// If bit 0 of Val is 1 the target is Thumb, we must select a BLX.
405+
case R_ARM_CALL: {
406+
// R_ARM_CALL is used for BL and BLX instructions, for symbols of type
407+
// STT_FUNC we choose whether to write a BL or BLX depending on the
408+
// value of bit 0 of Val. With bit 0 == 1 denoting Thumb. If the symbol is
409+
// not of type STT_FUNC then we must preserve the original instruction.
410+
// PLT entries are always ARM state so we know we don't need to interwork.
411+
bool isBlx = (read32le(loc) & 0xfe000000) == 0xfa000000;
412+
bool interwork = rel.sym && rel.sym->isFunc() && rel.type != R_PLT_PC;
413+
if (interwork ? val & 1 : isBlx) {
410414
// The BLX encoding is 0xfa:H:imm24 where Val = imm24:H:'1'
411415
checkInt(loc, val, 26, rel);
412416
write32le(loc, 0xfa000000 | // opcode
413417
((val & 2) << 23) | // H
414418
((val >> 2) & 0x00ffffff)); // imm24
415419
break;
416420
}
417-
if ((read32le(loc) & 0xfe000000) == 0xfa000000)
418-
// BLX (always unconditional) instruction to an ARM Target, select an
419-
// unconditional BL.
420-
write32le(loc, 0xeb000000 | (read32le(loc) & 0x00ffffff));
421+
// BLX (always unconditional) instruction to an ARM Target, select an
422+
// unconditional BL.
423+
write32le(loc, 0xeb000000 | (read32le(loc) & 0x00ffffff));
421424
// fall through as BL encoding is shared with B
425+
}
422426
LLVM_FALLTHROUGH;
423427
case R_ARM_JUMP24:
424428
case R_ARM_PC24:
@@ -443,16 +447,23 @@ void ARM::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
443447
((val >> 5) & 0x2000) | // J1
444448
((val >> 1) & 0x07ff)); // imm11
445449
break;
446-
case R_ARM_THM_CALL:
447-
// R_ARM_THM_CALL is used for BL and BLX instructions, depending on the
448-
// value of bit 0 of Val, we must select a BL or BLX instruction
449-
if ((val & 1) == 0) {
450-
// Ensure BLX destination is 4-byte aligned. As BLX instruction may
451-
// only be two byte aligned. This must be done before overflow check
450+
case R_ARM_THM_CALL: {
451+
// R_ARM_THM_CALL is used for BL and BLX instructions, for symbols of type
452+
// STT_FUNC we choose whether to write a BL or BLX depending on the
453+
// value of bit 0 of Val. With bit 0 == 0 denoting ARM, if the symbol is
454+
// not of type STT_FUNC then we must preserve the original instruction.
455+
// PLT entries are always ARM state so we know we need to interwork.
456+
bool isBlx = (read16le(loc + 2) & 0x1000) == 0;
457+
bool interwork = (rel.sym && rel.sym->isFunc()) || rel.type == R_PLT_PC;
458+
if (interwork ? (val & 1) == 0 : isBlx) {
459+
// We are writing a BLX. Ensure BLX destination is 4-byte aligned. As
460+
// the BLX instruction may only be two byte aligned. This must be done
461+
// before overflow check.
452462
val = alignTo(val, 4);
463+
write16le(loc + 2, read16le(loc + 2) & ~0x1000);
464+
} else {
465+
write16le(loc + 2, (read16le(loc + 2) & ~0x1000) | 1 << 12);
453466
}
454-
// Bit 12 is 0 for BLX, 1 for BL
455-
write16le(loc + 2, (read16le(loc + 2) & ~0x1000) | (val & 1) << 12);
456467
if (!config->armJ1J2BranchEncoding) {
457468
// Older Arm architectures do not support R_ARM_THM_JUMP24 and have
458469
// different encoding rules and range due to J1 and J2 always being 1.
@@ -466,6 +477,7 @@ void ARM::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
466477
((val >> 1) & 0x07ff)); // imm11
467478
break;
468479
}
480+
}
469481
// Fall through as rest of encoding is the same as B.W
470482
LLVM_FALLTHROUGH;
471483
case R_ARM_THM_JUMP24:

lld/test/ELF/arm-thumb-interwork-notfunc.s

Lines changed: 67 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ thumb_func_with_explicit_notype:
2323
/// All the symbols that are targets of the branch relocations do not have
2424
/// type STT_FUNC. LLD should not insert interworking thunks as non STT_FUNC
2525
/// symbols have no state information, the ABI assumes the user has manually
26-
/// done the interworking.
26+
/// done the interworking. For the BL and BLX instructions LLD should
27+
/// preserve the original instruction instead of writing out the correct one
28+
/// for the assumed state at the target.
2729
.section .arm_caller, "ax", %progbits
2830
.balign 4
2931
.arm
@@ -35,10 +37,24 @@ _start:
3537
b .thumb_target
3638
b thumb_func_with_notype
3739
b thumb_func_with_explicit_notype
40+
bl .arm_target
41+
bl arm_func_with_notype
42+
bl arm_func_with_explicit_notype
43+
bl .thumb_target
44+
bl thumb_func_with_notype
45+
bl thumb_func_with_explicit_notype
46+
blx .arm_target
47+
blx arm_func_with_notype
48+
blx arm_func_with_explicit_notype
49+
blx .thumb_target
50+
blx thumb_func_with_notype
51+
blx thumb_func_with_explicit_notype
3852

3953
.section .thumb_caller, "ax", %progbits
4054
.thumb
4155
.balign 4
56+
.global thumb_caller
57+
thumb_caller:
4258
b.w .arm_target
4359
b.w arm_func_with_notype
4460
b.w arm_func_with_explicit_notype
@@ -51,6 +67,18 @@ _start:
5167
beq.w .thumb_target
5268
beq.w thumb_func_with_notype
5369
beq.w thumb_func_with_explicit_notype
70+
bl .arm_target
71+
bl arm_func_with_notype
72+
bl arm_func_with_explicit_notype
73+
bl .thumb_target
74+
bl thumb_func_with_notype
75+
bl thumb_func_with_explicit_notype
76+
blx .arm_target
77+
blx arm_func_with_notype
78+
blx arm_func_with_explicit_notype
79+
blx .thumb_target
80+
blx thumb_func_with_notype
81+
blx thumb_func_with_explicit_notype
5482

5583
// CHECK: 00012008 _start:
5684
// CHECK-NEXT: 12008: b #-16
@@ -59,15 +87,41 @@ _start:
5987
// CHECK-NEXT: 12014: b #-24
6088
// CHECK-NEXT: 12018: b #-28
6189
// CHECK-NEXT: 1201c: b #-32
62-
// CHECK: 12020: b.w #-36
63-
// CHECK-NEXT: 12024: b.w #-40
64-
// CHECK-NEXT: 12028: b.w #-44
65-
// CHECK-NEXT: 1202c: b.w #-44
66-
// CHECK-NEXT: 12030: b.w #-48
67-
// CHECK-NEXT: 12034: b.w #-52
68-
// CHECK-NEXT: 12038: beq.w #-60
69-
// CHECK-NEXT: 1203c: beq.w #-64
70-
// CHECK-NEXT: 12040: beq.w #-68
71-
// CHECK-NEXT: 12044: beq.w #-68
72-
// CHECK-NEXT: 12048: beq.w #-72
73-
// CHECK-NEXT: 1204c: beq.w #-76
90+
// CHECK-NEXT: 12020: bl #-40
91+
// CHECK-NEXT: 12024: bl #-44
92+
// CHECK-NEXT: 12028: bl #-48
93+
// CHECK-NEXT: 1202c: bl #-48
94+
// CHECK-NEXT: 12030: bl #-52
95+
// CHECK-NEXT: 12034: bl #-56
96+
// CHECK-NEXT: 12038: blx #-64
97+
// CHECK-NEXT: 1203c: blx #-68
98+
// CHECK-NEXT: 12040: blx #-72
99+
// CHECK-NEXT: 12044: blx #-72
100+
// CHECK-NEXT: 12048: blx #-76
101+
// CHECK-NEXT: 1204c: blx #-80
102+
103+
// CHECK: 00012050 thumb_caller:
104+
// CHECK-NEXT: 12050: b.w #-84
105+
// CHECK-NEXT: 12054: b.w #-88
106+
// CHECK-NEXT: 12058: b.w #-92
107+
// CHECK-NEXT: 1205c: b.w #-92
108+
// CHECK-NEXT: 12060: b.w #-96
109+
// CHECK-NEXT: 12064: b.w #-100
110+
// CHECK-NEXT: 12068: beq.w #-108
111+
// CHECK-NEXT: 1206c: beq.w #-112
112+
// CHECK-NEXT: 12070: beq.w #-116
113+
// CHECK-NEXT: 12074: beq.w #-116
114+
// CHECK-NEXT: 12078: beq.w #-120
115+
// CHECK-NEXT: 1207c: beq.w #-124
116+
// CHECK-NEXT: 12080: bl #-132
117+
// CHECK-NEXT: 12084: bl #-136
118+
// CHECK-NEXT: 12088: bl #-140
119+
// CHECK-NEXT: 1208c: bl #-140
120+
// CHECK-NEXT: 12090: bl #-144
121+
// CHECK-NEXT: 12094: bl #-148
122+
// CHECK-NEXT: 12098: blx #-156
123+
// CHECK-NEXT: 1209c: blx #-160
124+
// CHECK-NEXT: 120a0: blx #-164
125+
// CHECK-NEXT: 120a4: blx #-164
126+
// CHECK-NEXT: 120a8: blx #-168
127+
// CHECK-NEXT: 120ac: blx #-172

lld/test/ELF/arm-thumb-undefined-weak.s

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
.syntax unified
1111

1212
.weak target
13+
.type target, %function
1314

1415
.text
1516
.global _start

lld/test/ELF/arm-undefined-weak.s

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
.syntax unified
1313

1414
.weak target
15+
.type target, %function
1516

1617
.text
1718
.global _start

0 commit comments

Comments
 (0)