kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

reloc.c (17470B)


      1 /* RISC-V relocation descriptors (width + classification), shared by the
      2  * rv64 and rv32 backends (their reloc kinds are identical).
      3  *
      4  * One row per relocation kind this backend applies.  Reached through
      5  * LinkArchDesc.reloc_desc (wired in link.c for both rv64 and rv32) and the
      6  * arch-aware reloc_desc() dispatcher.  Wire encoding + name live in
      7  * src/obj/<fmt>/reloc_riscv{32,64}.c.
      8  *
      9  * R_RV_CALL and the arch-neutral R_PLT32 (the kit-canonical kind
     10  * R_RISCV_CALL_PLT maps onto) are both branches: too-far targets route
     11  * through the JIT/range call-stub pass.  R_RV_CALL patches an 8-byte
     12  * AUIPC+JALR pair; R_PLT32 keeps its neutral 4-byte width (a gate value —
     13  * the apply path re-derives the real span from the kind).
     14  *
     15  * RELAX / TPREL_ADD / ALIGN are relaxation markers (RELOC_MARKER): no reloc
     16  * record is emitted for them — the link layout pass drops them via
     17  * reloc_kind_is_marker, so the width here is an unused gate value.
     18  * SET/SUB_ULEB128 are variable-width (RELOC_WIDTH_DYN): the apply path reads
     19  * the true field length from the bytes, so the width here is only nominal. */
     20 
     21 #include "obj/reloc.h"
     22 
     23 #include "core/bytes.h"
     24 #include "link/link_arch.h"
     25 
     26 static const RelocDescRow rv_rows[] = {
     27     {R_RV_HI20, {4, 0}},
     28     {R_RV_LO12_I, {4, 0}},
     29     {R_RV_LO12_S, {4, 0}},
     30     {R_RV_BRANCH, {4, 0}},
     31     {R_RV_JAL, {4, 0}},
     32     {R_RV_PCREL_HI20, {4, RELOC_IS_PCREL_ANCHOR}},
     33     {R_RV_PCREL_LO12_I, {4, 0}},
     34     {R_RV_PCREL_LO12_S, {4, 0}},
     35     {R_RV_GOT_HI20, {4, RELOC_USES_GOT | RELOC_IS_PCREL_ANCHOR}},
     36     {R_RV_TLS_GOT_HI20, {4, RELOC_IS_TLS_GOT}},
     37     {R_RV_TLS_GD_HI20, {4, RELOC_IS_TLS_LE}},
     38     {R_RV_TPREL_HI20, {4, RELOC_IS_TLS_LE}},
     39     {R_RV_TPREL_LO12_I, {4, RELOC_IS_TLS_LE}},
     40     {R_RV_TPREL_LO12_S, {4, RELOC_IS_TLS_LE}},
     41     {R_RV_CALL, {8, RELOC_IS_BRANCH}},
     42     {R_PLT32, {4, RELOC_IS_BRANCH}},
     43     {R_RV_RVC_BRANCH, {2, 0}},
     44     {R_RV_RVC_JUMP, {2, 0}},
     45     {R_RV_RELAX, {4, RELOC_MARKER}},
     46     {R_RV_TPREL_ADD, {4, RELOC_MARKER}},
     47     {R_RV_ALIGN, {4, RELOC_MARKER}},
     48 };
     49 
     50 const RelocDesc* rv_reloc_desc(RelocKind k) {
     51   return reloc_desc_row_find(rv_rows, (u32)(sizeof rv_rows / sizeof rv_rows[0]),
     52                              k);
     53 }
     54 
     55 #define RV_X_RA 1u
     56 #define RV_X_TP 4u
     57 #define RV_X_A0 10u
     58 
     59 typedef struct RvTlsgdSeq {
     60   u32 tmp_reg;
     61   u32 call_off;
     62 } RvTlsgdSeq;
     63 
     64 static u32 rv_rd(u32 instr) { return (instr >> 7) & 0x1fu; }
     65 
     66 static u32 rv_rs1(u32 instr) { return (instr >> 15) & 0x1fu; }
     67 
     68 static u32 rv_c_rd(u16 instr) { return ((u32)instr >> 7) & 0x1fu; }
     69 
     70 static u32 rv_c_rs2(u16 instr) { return ((u32)instr >> 2) & 0x1fu; }
     71 
     72 static int rv_is_32b_at(const u8* p) { return (rd_u16_le(p) & 0x3u) == 0x3u; }
     73 
     74 static int rv_is_addi_same_rd_rs1(u32 instr, u32 reg) {
     75   return (instr & 0x707fu) == 0x13u && rv_rd(instr) == reg &&
     76          rv_rs1(instr) == reg;
     77 }
     78 
     79 static int rv_is_addi_mv(u32 instr, u32* rd, u32* rs) {
     80   if ((instr & 0xfff0707fu) != 0x13u) return 0;
     81   *rd = rv_rd(instr);
     82   *rs = rv_rs1(instr);
     83   return *rd != 0 && *rs != 0;
     84 }
     85 
     86 static int rv_is_c_mv(u16 instr, u32* rd, u32* rs) {
     87   if ((instr & 0xf003u) != 0x8002u) return 0;
     88   *rd = rv_c_rd(instr);
     89   *rs = rv_c_rs2(instr);
     90   return *rd != 0 && *rs != 0;
     91 }
     92 
     93 static int rv_tlsgd_call_pair_at(const u8* p) {
     94   return rd_u32_le(p) == (0x00000017u | (RV_X_RA << 7)) &&
     95          rd_u32_le(p + 4) == 0x000080e7u;
     96 }
     97 
     98 static int rv_find_tlsgd_seq(const u8* p, RvTlsgdSeq* seq) {
     99   enum { RV_TLSGD_SCAN_LIMIT = 24u };
    100   u32 auipc = rd_u32_le(p);
    101   u32 addi = rd_u32_le(p + 4);
    102   u32 tmp;
    103   u32 off;
    104   int a0_ready;
    105 
    106   if ((auipc & 0x7fu) != 0x17u) return 0;
    107   tmp = rv_rd(auipc);
    108   if (tmp == 0) return 0;
    109   if (!rv_is_addi_same_rd_rs1(addi, tmp)) return 0;
    110 
    111   a0_ready = tmp == RV_X_A0;
    112   for (off = 8u; off <= RV_TLSGD_SCAN_LIMIT;) {
    113     if (rv_is_32b_at(p + off) && a0_ready && rv_tlsgd_call_pair_at(p + off)) {
    114       seq->tmp_reg = tmp;
    115       seq->call_off = off;
    116       return 1;
    117     }
    118 
    119     if (rv_is_32b_at(p + off)) {
    120       u32 rd = 0, rs = 0;
    121       if (!rv_is_addi_mv(rd_u32_le(p + off), &rd, &rs)) return 0;
    122       if (rd == RV_X_A0 && rs == tmp) {
    123         a0_ready = 1;
    124       } else if (rd == tmp || rd == RV_X_A0 || a0_ready) {
    125         return 0;
    126       }
    127       off += 4u;
    128       continue;
    129     } else {
    130       u32 rd = 0, rs = 0;
    131       if (!rv_is_c_mv(rd_u16_le(p + off), &rd, &rs)) return 0;
    132       if (rd == RV_X_A0 && rs == tmp) {
    133         a0_ready = 1;
    134       } else if (rd == tmp || rd == RV_X_A0 || a0_ready) {
    135         return 0;
    136       }
    137       off += 2u;
    138       continue;
    139     }
    140   }
    141   return 0;
    142 }
    143 
    144 static u32 rv_encode_lui(u32 rd, u32 hi20) {
    145   return 0x00000037u | (rd << 7) | (hi20 << 12);
    146 }
    147 
    148 static u32 rv_encode_addi(u32 rd, u32 rs1, u32 lo12) {
    149   return 0x00000013u | (rd << 7) | (rs1 << 15) | (lo12 << 20);
    150 }
    151 
    152 /* RISC-V instruction-immediate byte encoders (WS-C), shared by rv64 and rv32.
    153  * Moved verbatim from the format-neutral byte-patcher; reached via
    154  * LinkArchDesc.reloc_apply_insn.  Encoding references: "RISC-V ELF psABI" §3
    155  * and "The RISC-V Instruction Set Manual, Volume I" Ch.19.  The +0x800 bias on
    156  * HI20 / CALL compensates the sign-extension of the paired 12-bit immediate.
    157  * The data-word ADD/SUB/SET arms and the ULEB128 codec are arch-neutral byte
    158  * writes and stay in the obj-core neutral path.  Returns 1 if it owns `k`. */
    159 int rv_reloc_apply_insn(Compiler* c, RelocKind k, u8* P_bytes, u64 S, i64 A,
    160                         u64 P) {
    161   switch (k) {
    162     case R_RV_HI20:
    163     case R_RV_TPREL_HI20: {
    164       /* U-type (LUI/AUIPC) imm[31:12] = high 20 bits of (S + A + 0x800).
    165        * The 0x800 bias compensates the sign-extension of the paired
    166        * 12-bit ADDI/load/store immediate, so HI20 + signext12(LO12)
    167        * reconstructs the full value. */
    168       i64 v = (i64)S + A;
    169       u32 hi20 = (u32)(((u64)(v + 0x800)) >> 12) & 0xfffffu;
    170       u32 instr = rd_u32_le(P_bytes);
    171       instr = (instr & 0x00000fffu) | (hi20 << 12);
    172       wr_u32_le(P_bytes, instr);
    173       return 1;
    174     }
    175     case R_RV_PCREL_HI20:
    176     case R_RV_GOT_HI20:
    177     case R_RV_TLS_GOT_HI20: {
    178       /* AUIPC pc-relative HI20: same encoding as HI20 but the
    179        * displacement is (S + A) - P. The paired PCREL_LO12 reloc at
    180        * the ADDI/load below recovers the low 12 bits of the same
    181        * displacement via a lookup keyed on this AUIPC's site vaddr.
    182        * GOT_HI20 collapses to PCREL_HI20 in static-link with no
    183        * indirection: the symbol resolves to its own address. */
    184       i64 disp = (i64)S + A - (i64)P;
    185       u32 hi20 = (u32)(((u64)(disp + 0x800)) >> 12) & 0xfffffu;
    186       u32 instr = rd_u32_le(P_bytes);
    187       instr = (instr & 0x00000fffu) | (hi20 << 12);
    188       wr_u32_le(P_bytes, instr);
    189       return 1;
    190     }
    191     case R_RV_TLS_GD_HI20: {
    192       /* Relax a general-dynamic block:
    193        *   auipc t, %tls_gd_pcrel_hi(sym)
    194        *   addi  t, t, %pcrel_lo(.Lpcrel_hi)
    195        *   ...
    196        *   mv    a0, t        (if t is not already a0)
    197        *   call  __tls_get_addr
    198        *
    199        * to local-exec address materialization:
    200        *   lui   t, %tprel_hi(sym)
    201        *   addi  t, t, %tprel_lo(sym)
    202        *   ...
    203        *   mv    a0, t
    204        *   add   a0, a0, tp
    205        *   nop
    206        *
    207        * Rust/LLVM sometimes uses a non-a0 temporary and keeps live values with
    208        * compressed moves between the descriptor setup and the call.  Accept
    209        * only that narrow move-only shape; other dataflow stays a hard error.
    210        */
    211       RvTlsgdSeq seq;
    212       i64 v = (i64)S + A;
    213       u32 hi20 = (u32)(((u64)(v + 0x800)) >> 12) & 0xfffffu;
    214       u32 lo12 = (u32)((u64)v & 0xfffu);
    215       if (!rv_find_tlsgd_seq(P_bytes, &seq)) {
    216         compiler_panic(c, SRCLOC_NONE,
    217                        "link: unexpected RISC-V TLS_GD access sequence");
    218       }
    219       wr_u32_le(P_bytes, rv_encode_lui(seq.tmp_reg, hi20));
    220       wr_u32_le(P_bytes + 4, rv_encode_addi(seq.tmp_reg, seq.tmp_reg, lo12));
    221       wr_u32_le(P_bytes + seq.call_off, 0x00450533u);  /* add a0,a0,tp */
    222       wr_u32_le(P_bytes + seq.call_off + 4u, 0x00000013u); /* nop */
    223       return 1;
    224     }
    225     case R_RV_LO12_I:
    226     case R_RV_TPREL_LO12_I: {
    227       /* I-type imm[11:0] in instruction bits [31:20]. Low 12 bits of
    228        * (S + A); the sign-extension at execute time pairs with HI20's
    229        * 0x800 bias to reconstruct the full address. */
    230       u64 v = (u64)((i64)S + A);
    231       u32 lo12 = (u32)(v & 0xfffu);
    232       u32 instr = rd_u32_le(P_bytes);
    233       instr = (instr & 0x000fffffu) | (lo12 << 20);
    234       wr_u32_le(P_bytes, instr);
    235       return 1;
    236     }
    237     case R_RV_LO12_S:
    238     case R_RV_TPREL_LO12_S: {
    239       /* S-type imm[11:5] in bits [31:25], imm[4:0] in bits [11:7]. */
    240       u64 v = (u64)((i64)S + A);
    241       u32 lo12 = (u32)(v & 0xfffu);
    242       u32 instr = rd_u32_le(P_bytes);
    243       instr = (instr & 0x01fff07fu) | ((lo12 & 0xfe0u) << 20) |
    244               ((lo12 & 0x1fu) << 7);
    245       wr_u32_le(P_bytes, instr);
    246       return 1;
    247     }
    248     case R_RV_BRANCH: {
    249       /* B-type 12-bit signed displacement in 2-byte units (13-bit
    250        * range). imm[12] in bit 31, imm[10:5] in 30:25, imm[4:1] in
    251        * 11:8, imm[11] in bit 7. */
    252       i64 disp = (i64)S + A - (i64)P;
    253       u32 instr;
    254       u32 b;
    255       if (disp & 1)
    256         compiler_panic(c, SRCLOC_NONE,
    257                        "link: RV BRANCH misaligned displacement");
    258       if (disp < -(i64)(1 << 12) || disp >= (i64)(1 << 12))
    259         compiler_panic(c, SRCLOC_NONE,
    260                        "link: RV BRANCH out of range (need ±4KiB)");
    261       b = (u32)((u64)disp & 0x1ffeu) | ((u32)(((u64)disp >> 11) & 1u) << 11) |
    262           ((u32)(((u64)disp >> 12) & 1u) << 12);
    263       instr = rd_u32_le(P_bytes);
    264       instr &= 0x01fff07fu;
    265       instr |= ((b >> 12) & 1u) << 31;
    266       instr |= ((b >> 5) & 0x3fu) << 25;
    267       instr |= ((b >> 1) & 0xfu) << 8;
    268       instr |= ((b >> 11) & 1u) << 7;
    269       wr_u32_le(P_bytes, instr);
    270       return 1;
    271     }
    272     case R_RV_JAL: {
    273       /* J-type 20-bit signed displacement in 2-byte units (21-bit
    274        * range). imm[20] in bit 31, imm[10:1] in 30:21, imm[11] in bit
    275        * 20, imm[19:12] in bits 19:12. */
    276       i64 disp = (i64)S + A - (i64)P;
    277       u32 instr;
    278       u32 b;
    279       if (disp & 1)
    280         compiler_panic(c, SRCLOC_NONE, "link: RV JAL misaligned displacement");
    281       if (disp < -(i64)(1 << 20) || disp >= (i64)(1 << 20))
    282         compiler_panic(c, SRCLOC_NONE,
    283                        "link: RV JAL out of range (need ±1MiB)");
    284       b = (u32)((u64)disp & 0x1ffffeu) | ((u32)(((u64)disp >> 11) & 1u) << 11) |
    285           ((u32)(((u64)disp >> 20) & 1u) << 20);
    286       instr = rd_u32_le(P_bytes);
    287       instr &= 0x00000fffu;
    288       instr |= ((b >> 20) & 1u) << 31;
    289       instr |= ((b >> 1) & 0x3ffu) << 21;
    290       instr |= ((b >> 11) & 1u) << 20;
    291       instr |= ((b >> 12) & 0xffu) << 12;
    292       wr_u32_le(P_bytes, instr);
    293       return 1;
    294     }
    295     case R_RV_CALL:
    296     case R_PLT32: {
    297       /* AUIPC + JALR pair encoding the same 32-bit signed PC-relative
    298        * displacement. AUIPC at P, JALR at P+4. The 0x800 bias on the
    299        * AUIPC immediate compensates JALR's signed 12-bit imm so that
    300        * (auipc_imm << 12) + signext12(jalr_imm) == disp.
    301        *
    302        * R_PLT32 is the kit-canonical RelocKind that
    303        * elf_riscv64_reloc_from(R_RISCV_CALL_PLT) maps to; static-link
    304        * with no PLT collapses CALL_PLT to a direct CALL (no
    305        * indirection). */
    306       i64 disp = (i64)S + A - (i64)P;
    307       u32 hi20 = (u32)(((u64)(disp + 0x800)) >> 12) & 0xfffffu;
    308       u32 lo12 = (u32)((u64)disp & 0xfffu);
    309       u32 auipc = rd_u32_le(P_bytes);
    310       u32 jalr = rd_u32_le(P_bytes + 4);
    311       if (disp < -(i64)(1ll << 31) || disp >= (i64)(1ll << 31))
    312         compiler_panic(c, SRCLOC_NONE,
    313                        "link: RV CALL out of range (need ±2GiB)");
    314       auipc = (auipc & 0x00000fffu) | (hi20 << 12);
    315       jalr = (jalr & 0x000fffffu) | (lo12 << 20);
    316       wr_u32_le(P_bytes, auipc);
    317       wr_u32_le(P_bytes + 4, jalr);
    318       return 1;
    319     }
    320     case R_RV_RVC_BRANCH: {
    321       /* CB-type 8-bit signed displacement in 2-byte units (9-bit
    322        * range). c.beqz / c.bnez. Encoding (16-bit instruction):
    323        *   bit 12   = imm[8]
    324        *   bits 11:10 = imm[4:3]
    325        *   bits 9:7  = rs1' (untouched)
    326        *   bits 6:5  = imm[7:6]
    327        *   bits 4:3  = imm[2:1]
    328        *   bit 2    = imm[5] */
    329       i64 disp = (i64)S + A - (i64)P;
    330       u16 instr = (u16)(P_bytes[0] | ((u16)P_bytes[1] << 8));
    331       u32 b;
    332       if (disp & 1)
    333         compiler_panic(c, SRCLOC_NONE,
    334                        "link: RV RVC_BRANCH misaligned displacement");
    335       if (disp < -(i64)(1 << 8) || disp >= (i64)(1 << 8))
    336         compiler_panic(c, SRCLOC_NONE,
    337                        "link: RV RVC_BRANCH out of range (need ±256B)");
    338       b = (u32)((u64)disp & 0x1feu);
    339       instr = (u16)(instr & 0xe383u);
    340       instr = (u16)(instr | (((b >> 8) & 1u) << 12));
    341       instr = (u16)(instr | (((b >> 3) & 3u) << 10));
    342       instr = (u16)(instr | (((b >> 6) & 3u) << 5));
    343       instr = (u16)(instr | (((b >> 1) & 3u) << 3));
    344       instr = (u16)(instr | (((b >> 5) & 1u) << 2));
    345       P_bytes[0] = (u8)(instr & 0xffu);
    346       P_bytes[1] = (u8)((instr >> 8) & 0xffu);
    347       return 1;
    348     }
    349     case R_RV_RVC_JUMP: {
    350       /* CJ-type 11-bit signed displacement in 2-byte units (12-bit
    351        * range). c.j / c.jal. Encoding bits in the 16-bit instruction:
    352        *   12=imm[11], 11=imm[4], 10:9=imm[9:8], 8=imm[10],
    353        *   7=imm[6], 6=imm[7], 5:3=imm[3:1], 2=imm[5]. */
    354       i64 disp = (i64)S + A - (i64)P;
    355       u16 instr = (u16)(P_bytes[0] | ((u16)P_bytes[1] << 8));
    356       u32 b;
    357       if (disp & 1)
    358         compiler_panic(c, SRCLOC_NONE,
    359                        "link: RV RVC_JUMP misaligned displacement");
    360       if (disp < -(i64)(1 << 11) || disp >= (i64)(1 << 11))
    361         compiler_panic(c, SRCLOC_NONE,
    362                        "link: RV RVC_JUMP out of range (need ±2KiB)");
    363       b = (u32)((u64)disp & 0xffeu);
    364       instr = (u16)(instr & 0xe003u);
    365       instr = (u16)(instr | (((b >> 11) & 1u) << 12));
    366       instr = (u16)(instr | (((b >> 4) & 1u) << 11));
    367       instr = (u16)(instr | (((b >> 8) & 3u) << 9));
    368       instr = (u16)(instr | (((b >> 10) & 1u) << 8));
    369       instr = (u16)(instr | (((b >> 6) & 1u) << 7));
    370       instr = (u16)(instr | (((b >> 7) & 1u) << 6));
    371       instr = (u16)(instr | (((b >> 1) & 7u) << 3));
    372       instr = (u16)(instr | (((b >> 5) & 1u) << 2));
    373       P_bytes[0] = (u8)(instr & 0xffu);
    374       P_bytes[1] = (u8)((instr >> 8) & 0xffu);
    375       return 1;
    376     }
    377     case R_RV_RELAX:
    378     case R_RV_TPREL_ADD:
    379       /* Marker relocs only — RELAX permits the prior reloc to be
    380        * compressed, TPREL_ADD annotates a TLS thread-pointer ADD that
    381        * the linker may fold during relaxation. We don't relax, so
    382        * both are no-ops. */
    383       return 1;
    384     default:
    385       return 0;
    386   }
    387 }
    388 
    389 /* In-process JIT TLS Local-Exec relaxation (LinkArchDesc.jit_tls_le_relax).
    390  * Codegen emits, per access:
    391  *   lui  t, %tprel_hi(var)      R_RV_TPREL_HI20   <- `site`
    392  *   add  t, tp, t              (no reloc)
    393  *   addi rd, t, %tprel_lo(var)  R_RV_TPREL_LO12_I
    394  * Single-threaded JIT: address the in-image storage PC-relative, dropping the
    395  * tp add:
    396  *   auipc t, %pcrel_hi(&var) ; nop ; addi rd, t, %pcrel_lo(&var)
    397  * The HI20 reloc drives the whole rewrite; the LO12 half is then a no-op. The
    398  * riscv `tp` ABI register is x4. */
    399 void rv_jit_tls_le_relax(Compiler* c, RelocKind k, u8* site, u64 storage,
    400                          u64 site_pc) {
    401   u8* add;
    402   u8* addi;
    403   u32 rd_tmp, add_w, addi_w, hi20;
    404   i64 disp;
    405   i32 lo12;
    406   if (k == R_RV_TPREL_LO12_I || k == R_RV_TPREL_LO12_S)
    407     return; /* handled with HI20 */
    408   if (k != R_RV_TPREL_HI20)
    409     compiler_panic(c, SRCLOC_NONE, "riscv jit tls: unexpected reloc kind %u",
    410                    (unsigned)k);
    411   add = site + 4;  /* add t, tp, t -> nop */
    412   addi = site + 8; /* addi rd, t, %lo -> addi rd, t, %pcrel_lo */
    413   if ((rd_u32_le(site) & 0x7fu) != 0x37u) /* lui */
    414     compiler_panic(c, SRCLOC_NONE, "riscv jit tls: unexpected access sequence");
    415   add_w = rd_u32_le(add);
    416   if ((add_w & 0x7fu) != 0x33u ||
    417       ((add_w >> 15) & 0x1fu) != 4u) /* add ?,tp,? */
    418     compiler_panic(c, SRCLOC_NONE, "riscv jit tls: unexpected access sequence");
    419   rd_tmp = (rd_u32_le(site) >> 7) & 0x1fu;
    420   disp = (i64)storage - (i64)site_pc;
    421   lo12 = (i32)((u32)disp & 0xfffu);
    422   if (lo12 & 0x800) lo12 -= 0x1000; /* sign-extend 12-bit */
    423   hi20 = (u32)(((disp - (i64)lo12) >> 12) & 0xfffffu);
    424   wr_u32_le(site, 0x00000017u | (rd_tmp << 7) | (hi20 << 12)); /* auipc */
    425   wr_u32_le(add, 0x00000013u);                                 /* nop */
    426   addi_w = rd_u32_le(addi);
    427   wr_u32_le(addi, (addi_w & 0x000fffffu) | (((u32)lo12 & 0xfffu) << 20));
    428 }
    429 
    430 /* In-process JIT relaxation of RISC-V indirection idioms (LinkArchDesc
    431  * .jit_reloc_relax).  Today only PCREL_LO12: it has no S of its own — its
    432  * low-12 bits must match the paired AUIPC's PC-relative displacement, which
    433  * the JIT recomputes against its layout via ctx->resolve_pair.  Feed that
    434  * displacement to the LO12_I/S encoder (same instruction encoding; the addend
    435  * is unused per the psABI).  Returns 1 if it owned `k`, 0 otherwise. */
    436 int rv_jit_reloc_relax(Compiler* c, RelocKind k, const JitRelaxCtx* ctx) {
    437   if (k == R_RV_PCREL_LO12_I || k == R_RV_PCREL_LO12_S) {
    438     i64 disp = ctx->resolve_pair(ctx->resolve_user, ctx->anchor_vaddr);
    439     RelocKind alias = (k == R_RV_PCREL_LO12_I) ? R_RV_LO12_I : R_RV_LO12_S;
    440     rv_reloc_apply_insn(c, alias, ctx->site, (u64)disp, 0, ctx->site_pc);
    441     return 1;
    442   }
    443   return 0;
    444 }