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 }