RISC-V: Relax PCREL to GPREL while doing other relaxations is dangerous.
authorNelson Chu <nelson.chu@sifive.com>
Wed, 18 Nov 2020 03:39:52 +0000 (19:39 -0800)
committerNelson Chu <nelson.chu@sifive.com>
Sat, 21 Nov 2020 01:41:58 +0000 (09:41 +0800)
commitabd20cb637008da9d32018b4b03973e119388a0a
treead7714c9a527ae258996c30a1f4dc0ee61039073
parentd4087e81506bb8c7dc50cfdf318f4343f89002f9
RISC-V: Relax PCREL to GPREL while doing other relaxations is dangerous.

I get the feedback recently that enable linker relaxations may fail to
build some program.  Consider the following case,

.text
foo:
addi a0, a0, %pcrel_lo(.L2)
call foo
.L1: auipc a1, %pcrel_hi(data_g)
addi a1, a1, %pcrel_lo(.L1)
lui a2, %hi(data_g)
addi a2, a2, %lo(data_g)
lui a3, %tprel_hi(data_t)
add a3, a3, tp, %tprel_add(data_t)
addi a3, a3, %tprel_lo(data_t)
.L2: auipc a0, %pcrel_hi(data_g)

.data
.word 0x0
.global data_g
data_g: .word 0x1

.section .tbss
data_t: .word 0x0

The current ld reports `dangerous relocation error` when doing the
pcgp relaxation,
test.o: in function `foo':
(.text+0x0): dangerous relocation: %pcrel_lo missing matching %pcrel_hi

The .L2 auipc should not be removed since it is behind the corresponding
addi, so we record the information in the pcgp_relocs table to avoid
removing the auipc later.  But current ld still remove it since we do not
update the pcgp_relocs table while doing other relaxations.  I have two
solutions to fix the problem,

1. Update the pcgp_relocs table once we actually delete the code.
2. Add new relax pass to do the pcgp relaxations

At first I tried to do the first solution, and we need to update at
least three information - hi_sec_off of riscv_pcgp_lo_reloc, hi_sec_off
and hi_addr (symbol value) of riscv_pcgp_hi_reloc.  Update the hi_sec_off
is simple, but it is more complicate to update the symbol value, since we
almost have to do parts the same works of _bfd_riscv_relax_call again in
the riscv_relax_delete_bytes to get the correct symbol value.

Compared with the first solution, the second one is more intuitive and
simple.  We add a new relax pass to do the pcgp relaxations later, so
we will get all the information correctly in the _bfd_riscv_relax_call,
including the symbol value, without changing so much code.  I do not see
any penalty by adding a new relax pass for now, so it should be fine
to delay the pcgp relaxations.

Besides, I have pass all riscv-gnu-toolchain regressions for this patch.

bfd/
* elfnn-riscv.c (_bfd_riscv_relax_section):  Add a new relax pass
to do the pcgp relaxation later, after the lui and call relaxations,
but before the delete and alignment relaxations.

ld/
* emultempl/riscvelf.em (riscv_elf_before_allocation): Change
link_info.relax_pass from 3 to 4.
* testsuite/ld-riscv-elf/pcgp-relax.d: New testcase.
* testsuite/ld-riscv-elf/pcgp-relax.s: Likewise.
* testsuite/ld-riscv-elf/ld-riscv-elf.exp: Updated.
bfd/ChangeLog
bfd/elfnn-riscv.c
ld/ChangeLog
ld/emultempl/riscvelf.em
ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
ld/testsuite/ld-riscv-elf/pcgp-relax.d [new file with mode: 0644]
ld/testsuite/ld-riscv-elf/pcgp-relax.s [new file with mode: 0644]