Skip to content

Commit 6b1d151

Browse files
committed
[ELF] Fix displacement computation for intra-section branch after D127611
D127611 computed st_value is inaccurate: * For a backward branch, the destination address may be wrong if there is no relaxable relocation between it and the current location due to `if (remove)`. We may incorrectly relax a branch to c.j which ends up an overflow. * For a forward branch, the destination address may be overestimated and lose relaxation opportunities. To fix the issues, * Don't reset st_value to the original value. * Save the st_value delta from the previous iteration into valueDelta, and use `sa[0].d->value -= delta - valueDelta.find(sa[0].d)->second`.
1 parent aabfaf9 commit 6b1d151

File tree

2 files changed

+55
-11
lines changed

2 files changed

+55
-11
lines changed

lld/ELF/Arch/RISCV.cpp

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -572,18 +572,20 @@ static bool relax(InputSection &sec) {
572572
auto &aux = *sec.relaxAux;
573573
bool changed = false;
574574

575-
// Restore original st_value for symbols relative to this section.
575+
// Get st_value delta for symbols relative to this section from the previous
576+
// iteration.
577+
DenseMap<const Defined *, uint64_t> valueDelta;
576578
ArrayRef<SymbolAnchor> sa = makeArrayRef(aux.anchors);
577579
uint32_t delta = 0;
578580
for (auto it : llvm::enumerate(sec.relocations)) {
579581
for (; sa.size() && sa[0].offset <= it.value().offset; sa = sa.slice(1))
580582
if (!sa[0].end)
581-
sa[0].d->value += delta;
583+
valueDelta[sa[0].d] = delta;
582584
delta = aux.relocDeltas[it.index()];
583585
}
584586
for (const SymbolAnchor &sa : sa)
585587
if (!sa.end)
586-
sa.d->value += delta;
588+
valueDelta[sa.d] = delta;
587589
sa = makeArrayRef(aux.anchors);
588590
delta = 0;
589591

@@ -615,13 +617,11 @@ static bool relax(InputSection &sec) {
615617
// For all anchors whose offsets are <= r.offset, they are preceded by
616618
// the previous relocation whose `relocDeltas` value equals `delta`.
617619
// Decrease their st_value and update their st_size.
618-
if (remove) {
619-
for (; sa.size() && sa[0].offset <= r.offset; sa = sa.slice(1)) {
620-
if (sa[0].end)
621-
sa[0].d->size = sa[0].offset - delta - sa[0].d->value;
622-
else
623-
sa[0].d->value -= delta;
624-
}
620+
for (; sa.size() && sa[0].offset <= r.offset; sa = sa.slice(1)) {
621+
if (sa[0].end)
622+
sa[0].d->size = sa[0].offset - delta - sa[0].d->value;
623+
else
624+
sa[0].d->value -= delta - valueDelta.find(sa[0].d)->second;
625625
}
626626
delta += remove;
627627
if (delta != cur) {
@@ -634,7 +634,7 @@ static bool relax(InputSection &sec) {
634634
if (a.end)
635635
a.d->size = a.offset - delta - a.d->value;
636636
else
637-
a.d->value -= delta;
637+
a.d->value -= delta - valueDelta.find(a.d)->second;
638638
}
639639
// Inform assignAddresses that the size has changed.
640640
if (!isUInt<16>(delta))
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# REQUIRES: riscv
2+
## Test R_RISCV_CALL referencing the current input section with the displacement
3+
## close to the boundary.
4+
5+
# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c,+relax %s -o %t.o
6+
# RUN: ld.lld -Ttext=0x10000 %t.o -o %t
7+
# RUN: llvm-objdump -d --no-show-raw-insn -M no-aliases %t | FileCheck %s
8+
9+
# CHECK-LABEL: <_start>:
10+
# CHECK-NEXT: jal ra, {{.*}} <_start>
11+
# CHECK-NEXT: jal ra, {{.*}} <_start>
12+
# CHECK-EMPTY:
13+
# CHECK-NEXT: <a>:
14+
# CHECK-NEXT: c.jr ra
15+
16+
# CHECK-LABEL: <b>:
17+
# CHECK: jal zero, {{.*}} <a>
18+
# CHECK-NEXT: jal zero, {{.*}} <c>
19+
# CHECK-NEXT: c.j {{.*}} <c>
20+
21+
# CHECK-LABEL: <c>:
22+
# CHECK-NEXT: c.jr ra
23+
24+
#--- a.s
25+
.global _start
26+
_start:
27+
call _start
28+
call _start
29+
30+
a:
31+
ret
32+
b:
33+
.space 2048
34+
## Relaxed to jal. If we don't compute the precise value of a, we may consider
35+
## a reachable by c.j.
36+
tail a
37+
## Relaxed to jal. c.j is unreachable.
38+
tail c # c.j
39+
## Relaxed to c.j. If we don't compute the precise value of c, we may consider
40+
## c.j unreachable.
41+
tail c # c.j
42+
.space 2042
43+
c:
44+
ret

0 commit comments

Comments
 (0)