Issue |
134525
|
Summary |
[RISC-V] mcmodel=medany 'relocation R_RISCV_PCREL_HI20 out of range' for >2MiB relocations
|
Labels |
new issue
|
Assignees |
|
Reporter |
midnightveil
|
Compiling & linking the following code under `mcmodel=medany` creates a relocation failure:
```c
extern char ki_end[];
void _assert_fail(char *, const char *);
void example(void);
_Static_assert(sizeof(long) == 8, "64-bit ki_end");
void example(void) {
if (((long)ki_end) - (0xFFFFFFFF80200000ul) <= 0x20000ul) {
_assert_fail("(ELF_TOP - ELF_BASE) <= RISCV_GET_LVL_PGSIZE(0)", __func__);
}
}
```
When using a linker script (similar) to this one:
```ld
OUTPUT_ARCH(riscv)
ENTRY(_start)
/* This is very similar to our assertion */
/* KERNEL_OFFSET = (0xFFFFFFFF80000000 + (0x80200000 & ((1 << (30)) - 1))) - 0x80200000; */
KERNEL_OFFSET = 0xFFFFFFFF00000000;
SECTIONS
{
. = 0xFFFFFFFF80200000;
.text . : AT(ADDR(.text) - KERNEL_OFFSET)
{
/* Make the linker happy(ier) */
_start = .;
_assert_fail = .;
*(.text)
}
.data . : AT(ADDR(.data) - KERNEL_OFFSET)
{
*(.data)
*(.rodata)
*(.rodata.*)
}
.bss . (NOLOAD): AT(ADDR(.bss) - KERNEL_OFFSET)
{
*(.bss)
*(.sbss)
}
.bss.ki_clone_mem . (NOLOAD): AT(ADDR(.bss.ki_clone_mem) - KERNEL_OFFSET)
{
/* This moves the ki_end more than about 2MiB from the location of the
relocation that will be performed.
2M is 20 bits which is the size of the PCREL_HI20 relocation which fails.
Generally somewhere around 1.99MiB should make this link again.
*/
. = . + 4M;
}
ki_end = .;
}
```
This generates assembly in the object file (pre-linking) which looks something like:
```asm
0000000000000000 <example>:
; .L.str():
0: 00000517 auipc a0, 0x0
0000000000000000: R_RISCV_PCREL_HI20 ki_end+0x7fe00000
0000000000000000: R_RISCV_RELAX *ABS*
4: 00050513 mv a0, a0
0000000000000004: R_RISCV_PCREL_LO12_I .Lpcrel_hi0
0000000000000004: R_RISCV_RELAX *ABS*
8: 000205b7 lui a1, 0x20
c: 00a5e063 bltu a1, a0, 0xc <example+0xc>
000000000000000c: R_RISCV_BRANCH .L0
```
Using instead
```c
if (((long)ki_end) <= 0xFFFFFFFF80200000ul + 0x20000ul) {
```
works fine.
It appears to work fine for values up to 2MiB, but past a 2MiB offset the linking fails.
This is weird, because `auipc` should support 32 bit signed (±2GiB) offsets.
The same code compiled with `medlow` links fine (as these addresses are *technically* OK for medlow, as it's within ±2GiB of the 0 address).
Both gcc and clang seem to be doing some sort of "constant folding", moving around the 0xFFFF'FFFF'8020'0000 to the signed equivalent of (negative that becomes positive due to the subtraction) 0x7FE0'0000. Which is then out of bounds of a 2GiB relocation.
```
+ clang -target riscv64-none-elf --sysroot=<prefix>kernel-reduce -O2 -mabi=lp64d -march=rv64imafdc_zicsr_zifencei -fno-pic -fno-pie -nostdlib -mcmodel=medany -std=c99 -Wall -Werror -ffreestanding -fno-stack-protector -fno-asynchronous-unwind-tables -fno-common -o kernel.obj -c kernel.i
+ clang -target riscv64-none-elf --sysroot=<prefix>kernel-reduce -O2 -mabi=lp64d -march=rv64imafdc_zicsr_zifencei -fno-pic -fno-pie -nostdlib -mcmodel=medany -static -Wl,--build-id=none -Wl,-n -Wl,-T ./linker.lds kernel.obj -o kernel.elf
ld.lld: error: kernel.obj:(function example: .text+0x0): relocation R_RISCV_PCREL_HI20 out of range: 549376 is not in [-524288, 524287]; references 'ki_end'
```
"Longer" gist here: https://gist.github.com/midnightveil/263e6b08d1f50c58cfaee9d2215778a6, with full reproducer steps.
*This appears to be a bug in both GCC/LLVM, both as gcc+ld,clang+lld,gcc+lld,clang+ld*.
_______________________________________________
llvm-bugs mailing list
llvm-bugs@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs