diff --git a/elf/arch-loongarch.cc b/elf/arch-loongarch.cc index bc7aa258c9..54210cec28 100644 --- a/elf/arch-loongarch.cc +++ b/elf/arch-loongarch.cc @@ -471,6 +471,20 @@ void InputSection::apply_reloc_alloc(Context &ctx, u8 *base) { case R_LARCH_64_PCREL: *(ul64 *)loc = S + A - P; break; + case R_LARCH_CALL36: + if (removed_bytes == 0) { + write_j20(loc, (S + A - P + 0x20000) >> 18); + write_k16(loc + 4, (S + A - P) >> 2); + } else { + // Rewrite PCADDU18I + JIRL to B or BL + assert(removed_bytes == 4); + if (get_rd(*(ul32 *)(contents.data() + rel.r_offset + 4)) == 0) + *(ul32 *)loc = 0x5000'0000; // B + else + *(ul32 *)loc = 0x5400'0000; // BL + write_k16(loc, (S + A - P) >> 2); + } + break; case R_LARCH_ADD_ULEB128: overwrite_uleb(loc, read_uleb(loc) + S + A); break; @@ -626,6 +640,7 @@ void InputSection::scan_relocations(Context &ctx) { break; case R_LARCH_B26: case R_LARCH_PCALA_HI20: + case R_LARCH_CALL36: if (sym.is_imported) sym.flags |= NEEDS_PLT; break; @@ -792,6 +807,21 @@ void shrink_section(Context &ctx, InputSection &isec, bool use_rvc) { delta += 4; } break; + case R_LARCH_CALL36: + // A CALL36 relocation referes to the following instruction pair + // to jump to PC ± 128 GiB. + // + // pcaddu18i $t0, 0 # R_LARCH_CALL36 + // jirl $zero/$ra, $t0, 0 + // + // If the displacement is PC ± 128 MiB, we can use B or BL instead. + // Note that $zero is $r0 and $ra is $r1. + if (i64 dist = compute_distance(ctx, sym, isec, r); + -(1 << 27) <= dist && dist < (1 << 27)) + if (u32 jirl = *(ul32 *)(isec.contents.data() + rels[i].r_offset + 4); + get_rd(jirl) == 0 || get_rd(jirl) == 1) + delta += 4; + break; } } diff --git a/test/elf/loongarch64_relax-call36.sh b/test/elf/loongarch64_relax-call36.sh new file mode 100755 index 0000000000..5873114607 --- /dev/null +++ b/test/elf/loongarch64_relax-call36.sh @@ -0,0 +1,47 @@ +#!/bin/bash +. $(dirname $0)/common.inc + +cat <<'EOF' | $CC -o $t/a.o -c -xassembler - +.globl foo, bar +foo: + move $s0, $ra + .reloc ., R_LARCH_CALL36, foo2 + .reloc ., R_LARCH_RELAX + pcaddu18i $t0, 0 + jirl $ra, $t0, 0 + move $ra, $s0 + ret +bar: + .reloc ., R_LARCH_CALL36, bar2 + .reloc ., R_LARCH_RELAX + pcaddu18i $t0, 0 + jirl $zero, $t0, 0 +EOF + +cat < +void foo(); +void bar(); +void foo2() { printf("foo"); } +void bar2() { printf("bar"); } + +int main() { + foo(); + bar(); + printf("\n"); +} +EOF + +$CC -B. -o $t/exe1 $t/a.o $t/b.o -Wl,--no-relax +$QEMU $t/exe1 | grep -q foobar + +$OBJDUMP -d $t/exe1 > $t/exe1.objdump +grep -A2 ':' $t/exe1.objdump | grep -wq pcaddu18i +grep -A2 ':' $t/exe1.objdump | grep -wq pcaddu18i + +$CC -B. -o $t/exe2 $t/a.o $t/b.o -Wl,--relax +$QEMU $t/exe2 | grep -q foobar + +$OBJDUMP -d $t/exe2 > $t/exe2.objdump +grep -A2 ':' $t/exe2.objdump | grep -wq bl +grep -A2 ':' $t/exe2.objdump | grep -wq b diff --git a/test/elf/loongarch64_relax-pcala-addi.sh b/test/elf/loongarch64_relax-pcala-addi.sh old mode 100644 new mode 100755