reloc.c (5029B)
1 /* x86-64 relocation descriptors (width + classification). 2 * 3 * One row per relocation kind this backend applies. Reached through 4 * LinkArchDesc.reloc_desc (wired in link.c) and the arch-aware reloc_desc() 5 * dispatcher. Wire encoding + name live in src/obj/<fmt>/reloc_x86_64.c. 6 * 7 * R_PLT32 is the arch-neutral canonical PLT call kind; x86-64 classifies it 8 * as a branch (it shares x64's branch handling with R_X64_PLT32), so it 9 * gets a slice row that overrides the neutral table's flag-free entry while 10 * keeping the same 4-byte width. 11 * 12 * The general-/local-dynamic TLS kinds (TLSGD/TLSLD/DTP*), GOTOFF64, and 13 * COPY are never applied through the static reloc record path and carry no 14 * descriptor. */ 15 16 #include "obj/reloc.h" 17 18 #include "core/bytes.h" 19 20 static const RelocDescRow x64_rows[] = { 21 {R_X64_PC8, {1, 0}}, 22 {R_X64_32S, {4, 0}}, 23 {R_X64_PLT32, {4, RELOC_IS_BRANCH}}, 24 {R_PLT32, {4, RELOC_IS_BRANCH}}, 25 {R_X64_GOTPCREL, {4, RELOC_USES_GOT}}, 26 {R_X64_GOTPCRELX, {4, RELOC_USES_GOT}}, 27 {R_X64_REX_GOTPCRELX, {4, RELOC_USES_GOT}}, 28 {R_X64_GOTPC32, {4, 0}}, 29 {R_X64_GOTTPOFF, {4, RELOC_IS_TLS_GOT}}, 30 {R_X64_TPOFF32, {4, RELOC_IS_TLS_LE}}, 31 {R_X64_GLOB_DAT, {8, 0}}, 32 {R_X64_JUMP_SLOT, {8, 0}}, 33 {R_X64_RELATIVE, {8, 0}}, 34 }; 35 36 const RelocDesc* x64_reloc_desc(RelocKind k) { 37 return reloc_desc_row_find(x64_rows, 38 (u32)(sizeof x64_rows / sizeof x64_rows[0]), k); 39 } 40 41 /* x86-64 instruction-immediate byte encoders (WS-C). Moved verbatim from the 42 * format-neutral byte-patcher; reached via LinkArchDesc.reloc_apply_insn. The 43 * only instruction-embedded x86-64 kind is the pc-relative rel8 displacement 44 * (the wider GOT/PLT/TPOFF kinds are plain little-endian data words and stay 45 * in the obj-core neutral path). Returns 1 if it owns `k`. */ 46 int x64_reloc_apply_insn(Compiler* c, RelocKind k, u8* P_bytes, u64 S, i64 A, 47 u64 P) { 48 switch (k) { 49 case R_X64_PC8: { 50 i64 v = (i64)S + A - (i64)P; 51 if (v < -128 || v > 127) 52 compiler_panic(c, SRCLOC_NONE, "link: X64_PC8 out of range"); 53 P_bytes[0] = (u8)((u64)v & 0xffu); 54 return 1; 55 } 56 default: 57 return 0; 58 } 59 } 60 61 /* In-process JIT TLS Local-Exec relaxation (LinkArchDesc.jit_tls_le_relax). 62 * 63 * ELF (R_X64_TPOFF32), per access: 64 * 64 REX.W 8B modrm sib disp32 mov rd, fs:[0] (fixed 9 bytes) 65 * REX.W 8D modrm [sib] disp32 lea rd,[rd+tpoff] (7 or 8 bytes; `site` 66 * points at this disp32) 67 * 68 * Windows/COFF (R_COFF_SECREL to a TLS symbol), per access — the 4-instruction 69 * TEB idiom (see x64_tls_addr_of_win64), all heading the trailing lea: 70 * 65 REX.W 8B modrm sib disp32 mov rd, gs:[0x58] (9 bytes) 71 * 44 8B 1D disp32 mov r11d, [rip+_tls_index] (7 bytes) 72 * REX 8B modrm sib [disp8] mov rd, [rd+r11*8] (4 or 5 bytes) 73 * REX.W 8D modrm [sib] disp32 lea rd,[rd+sym@SECREL] (7 or 8 bytes; 74 * `site` points at this disp32) 75 * 76 * Single-threaded JIT: in both cases nop the whole block and emit 77 * `lea rd,[rip+&var]` so rd holds the in-image storage address, dropping the 78 * segment read (and, on Windows, the `_tls_index` / TEB indirection — its 79 * `_tls_index` reloc is separately dropped by the JIT reloc pass). */ 80 void x64_jit_tls_le_relax(Compiler* c, RelocKind k, u8* site, u64 storage, 81 u64 site_pc) { 82 u8* block_end = site + 4; /* disp32 ends the lea */ 83 u8* mov = NULL; 84 u8* lea; 85 u8* p; 86 u32 rd; 87 i64 disp; 88 if (k == R_X64_TPOFF32) { 89 /* The 9-byte fs-mov ends where the lea (7 or 8 bytes) begins. */ 90 p = block_end - 7 - 9; 91 if (p[0] == 0x64u && p[2] == 0x8Bu) 92 mov = p; 93 else { 94 p = block_end - 8 - 9; 95 if (p[0] == 0x64u && p[2] == 0x8Bu) mov = p; 96 } 97 } else if (k == R_COFF_SECREL) { 98 /* The gs-mov heads the idiom; its distance to block_end is 99 * 9 + 7 + (4|5) + (7|8) = 27..29 bytes. Match the gs-mov's strong 100 * signature: 65 <rex> 8B <modrm> 25 58 00 00 00. */ 101 int off; 102 for (off = 27; off <= 29 && !mov; ++off) { 103 p = block_end - off; 104 if (p[0] == 0x65u && p[2] == 0x8Bu && p[4] == 0x25u && p[5] == 0x58u && 105 p[6] == 0u && p[7] == 0u && p[8] == 0u) 106 mov = p; 107 } 108 } else { 109 compiler_panic(c, SRCLOC_NONE, "x64 jit tls: unexpected reloc kind %u", 110 (unsigned)k); 111 } 112 if (!mov) 113 compiler_panic(c, SRCLOC_NONE, "x64 jit tls: unexpected access sequence"); 114 rd = ((u32)(mov[3] >> 3) & 7u) | ((mov[1] & 0x04u) ? 8u : 0u); 115 for (p = mov; p < block_end; ++p) *p = 0x90u; /* nop the block */ 116 lea = block_end - 7; 117 disp = (i64)storage - (i64)(site_pc + 4u); /* rip = end of lea */ 118 lea[0] = (u8)(0x48u | ((rd >= 8u) ? 0x04u : 0x00u)); /* REX.W (+REX.R) */ 119 lea[1] = 0x8Du; /* lea */ 120 lea[2] = (u8)(((rd & 7u) << 3) | 5u); /* mod=00 reg=rd rm=101 (rip) */ 121 wr_u32_le(lea + 3, (u32)disp); 122 }