From d6d8d2e494ff6babcc79fac213c00c26043ff52e Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Wed, 14 Aug 2024 13:21:00 +0900 Subject: [PATCH] [LoongArch] Fix symbol value computation for the extreme code model The LoongArch extreme code model uses the following instructions to materialize a 64-bit value in a regiter. pcalau12i $t1, %pc_hi20(g2) addi.d $t0, $zero, %pc_lo12(g2) lu32i.d $t0, %pc64_lo20(g2) lu52i.d $t0, $t0, %pc64_hi12(g2) The previous relocation formula expected that `page(pc)` would be consistent for all the instructions, but this assumption is incorrect if the instruction sequence crosses a 4 KiB boundary. Now, the LoongArch psABI requires that the machine instructions must be consecutive, and the relocations for lu32.d and lu52i.d uses `page(pc - 8)` and `page(pc - 16)` instead of `page(pc)`. This psABI change gave me an impression that the LoongArch's extreme code model was poorly designed and inadequately tested. If these instructions must be consecutive, only a single relocation referring to the beginning of the instructions would suffice, which relocates all the following four instructions at once. Maybe we do not need to support the relocations for the extreme code model because the code model was buggy at the spec level, which suggests that no one is using them. But I'm not sure if it is safe to remove them, so let's just follow the psABI change. --- elf/arch-loongarch.cc | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/elf/arch-loongarch.cc b/elf/arch-loongarch.cc index bc3a0033..64b5af17 100644 --- a/elf/arch-loongarch.cc +++ b/elf/arch-loongarch.cc @@ -51,7 +51,7 @@ static u64 hi20(u64 val, u64 pc) { return bits(page(val + 0x800) - page(pc), 31, 12); } -static u64 hi64(u64 val, u64 pc) { +static u64 higher20(u64 val, u64 pc) { // A PC-relative 64-bit address is materialized with the following // instructions for the large code model: // @@ -65,21 +65,15 @@ static u64 hi64(u64 val, u64 pc) { // ADDI.D adds a sign-extended 12 bit value to a register. LU32I.D and // LU52I.D simply set bits to [51:31] and to [63:53], respectively. // - // Compensating all the sign-extensions is a bit complicated. - u64 x = page(val) - page(pc); - if (val & 0x800) - x += 0x1000 - 0x1'0000'0000; - if (x & 0x8000'0000) - x += 0x1'0000'0000; - return x; -} - -static u64 higher20(u64 val, u64 pc) { - return bits(hi64(val, pc), 51, 32); + // Compensating all the sign-extensions is a bit complicated. The + // psABI gave the following formula. + val = val + 0x8000'0000 + ((val & 0x800) ? (0x1000 - 0x1'0000'0000) : 0); + return bits(page(val) - page(pc - 8), 51, 32); } static u64 highest12(u64 val, u64 pc) { - return bits(hi64(val, pc), 63, 52); + val = val + 0x8000'0000 + ((val & 0x800) ? (0x1000 - 0x1'0000'0000) : 0); + return bits(page(val) - page(pc - 12), 63, 52); } static void write_k12(u8 *loc, u32 val) {