kit

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

reloc.c (6085B)


      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-dynamic TLS kinds (TLSGD/DTPMOD/DTP64), GOTOFF64, and COPY are
     13  * never applied through the static reloc record path and carry no descriptor.
     14  * Local-dynamic TLS (TLSLD + DTPOFF32) is relaxed to local-exec by the ELF
     15  * static linker and therefore does have apply rows here. */
     16 
     17 #include "obj/reloc.h"
     18 
     19 #include "core/bytes.h"
     20 
     21 static const RelocDescRow x64_rows[] = {
     22     {R_X64_PC8, {1, 0}},
     23     {R_X64_32S, {4, 0}},
     24     {R_X64_PLT32, {4, RELOC_IS_BRANCH}},
     25     {R_PLT32, {4, RELOC_IS_BRANCH}},
     26     {R_X64_GOTPCREL, {4, RELOC_USES_GOT}},
     27     {R_X64_GOTPCRELX, {4, RELOC_USES_GOT}},
     28     {R_X64_REX_GOTPCRELX, {4, RELOC_USES_GOT}},
     29     {R_X64_GOTPC32, {4, 0}},
     30     {R_X64_GOTTPOFF, {4, RELOC_IS_TLS_GOT}},
     31     {R_X64_TPOFF32, {4, RELOC_IS_TLS_LE}},
     32     {R_X64_DTPOFF32, {4, RELOC_IS_TLS_LE}},
     33     {R_X64_TLSLD, {4, 0}},
     34     {R_X64_TLV, {4, RELOC_IS_TLVP}},
     35     {R_X64_GLOB_DAT, {8, 0}},
     36     {R_X64_JUMP_SLOT, {8, 0}},
     37     {R_X64_RELATIVE, {8, 0}},
     38 };
     39 
     40 const RelocDesc* x64_reloc_desc(RelocKind k) {
     41   return reloc_desc_row_find(x64_rows,
     42                              (u32)(sizeof x64_rows / sizeof x64_rows[0]), k);
     43 }
     44 
     45 /* x86-64 instruction-immediate byte encoders (WS-C).  Moved verbatim from the
     46  * format-neutral byte-patcher; reached via LinkArchDesc.reloc_apply_insn.  The
     47  * only instruction-embedded x86-64 kind is the pc-relative rel8 displacement
     48  * (the wider GOT/PLT/TPOFF kinds are plain little-endian data words and stay
     49  * in the obj-core neutral path).  Returns 1 if it owns `k`. */
     50 int x64_reloc_apply_insn(Compiler* c, RelocKind k, u8* P_bytes, u64 S, i64 A,
     51                          u64 P) {
     52   switch (k) {
     53     case R_X64_PC8: {
     54       i64 v = (i64)S + A - (i64)P;
     55       if (v < -128 || v > 127)
     56         compiler_panic(c, SRCLOC_NONE, "link: X64_PC8 out of range");
     57       P_bytes[0] = (u8)((u64)v & 0xffu);
     58       return 1;
     59     }
     60     case R_X64_TLSLD: {
     61       u8* insn = P_bytes - 3;
     62       if (insn[0] != 0x48u || insn[1] != 0x8du || insn[2] != 0x3du ||
     63           insn[7] != 0xe8u) {
     64         compiler_panic(c, SRCLOC_NONE,
     65                        "link: unexpected x64 TLSLD access sequence");
     66       }
     67       /* Relax:
     68        *   leaq sym@TLSLD(%rip), %rdi; call __tls_get_addr
     69        * to:
     70        *   movq %fs:0, %rax; nop; nop; nop
     71        *
     72        * The paired DTPOFF32 access then applies as a TPOFF32 displacement
     73        * against %rax. */
     74       insn[0] = 0x64u;
     75       insn[1] = 0x48u;
     76       insn[2] = 0x8bu;
     77       insn[3] = 0x04u;
     78       insn[4] = 0x25u;
     79       insn[5] = 0x00u;
     80       insn[6] = 0x00u;
     81       insn[7] = 0x00u;
     82       insn[8] = 0x00u;
     83       insn[9] = 0x90u;
     84       insn[10] = 0x90u;
     85       insn[11] = 0x90u;
     86       return 1;
     87     }
     88     default:
     89       return 0;
     90   }
     91 }
     92 
     93 /* In-process JIT TLS Local-Exec relaxation (LinkArchDesc.jit_tls_le_relax).
     94  *
     95  * ELF (R_X64_TPOFF32), per access:
     96  *   64 REX.W 8B modrm sib disp32   mov rd, fs:[0]      (fixed 9 bytes)
     97  *   REX.W 8D modrm [sib] disp32    lea rd,[rd+tpoff]   (7 or 8 bytes; `site`
     98  *                                  points at this disp32)
     99  *
    100  * Windows/COFF (R_COFF_SECREL to a TLS symbol), per access — the 4-instruction
    101  * TEB idiom (see x64_tls_addr_of_win64), all heading the trailing lea:
    102  *   65 REX.W 8B modrm sib disp32   mov rd, gs:[0x58]            (9 bytes)
    103  *   44 8B 1D disp32                mov r11d, [rip+_tls_index]   (7 bytes)
    104  *   REX 8B modrm sib [disp8]       mov rd, [rd+r11*8]           (4 or 5 bytes)
    105  *   REX.W 8D modrm [sib] disp32    lea rd,[rd+sym@SECREL]       (7 or 8 bytes;
    106  *                                  `site` points at this disp32)
    107  *
    108  * Single-threaded JIT: in both cases nop the whole block and emit
    109  * `lea rd,[rip+&var]` so rd holds the in-image storage address, dropping the
    110  * segment read (and, on Windows, the `_tls_index` / TEB indirection — its
    111  * `_tls_index` reloc is separately dropped by the JIT reloc pass). */
    112 void x64_jit_tls_le_relax(Compiler* c, RelocKind k, u8* site, u64 storage,
    113                           u64 site_pc) {
    114   u8* block_end = site + 4; /* disp32 ends the lea */
    115   u8* mov = NULL;
    116   u8* lea;
    117   u8* p;
    118   u32 rd;
    119   i64 disp;
    120   if (k == R_X64_TPOFF32) {
    121     /* The 9-byte fs-mov ends where the lea (7 or 8 bytes) begins. */
    122     p = block_end - 7 - 9;
    123     if (p[0] == 0x64u && p[2] == 0x8Bu)
    124       mov = p;
    125     else {
    126       p = block_end - 8 - 9;
    127       if (p[0] == 0x64u && p[2] == 0x8Bu) mov = p;
    128     }
    129   } else if (k == R_COFF_SECREL) {
    130     /* The gs-mov heads the idiom; its distance to block_end is
    131      * 9 + 7 + (4|5) + (7|8) = 27..29 bytes. Match the gs-mov's strong
    132      * signature: 65 <rex> 8B <modrm> 25 58 00 00 00. */
    133     int off;
    134     for (off = 27; off <= 29 && !mov; ++off) {
    135       p = block_end - off;
    136       if (p[0] == 0x65u && p[2] == 0x8Bu && p[4] == 0x25u && p[5] == 0x58u &&
    137           p[6] == 0u && p[7] == 0u && p[8] == 0u)
    138         mov = p;
    139     }
    140   } else {
    141     compiler_panic(c, SRCLOC_NONE, "x64 jit tls: unexpected reloc kind %u",
    142                    (unsigned)k);
    143   }
    144   if (!mov)
    145     compiler_panic(c, SRCLOC_NONE, "x64 jit tls: unexpected access sequence");
    146   rd = ((u32)(mov[3] >> 3) & 7u) | ((mov[1] & 0x04u) ? 8u : 0u);
    147   for (p = mov; p < block_end; ++p) *p = 0x90u; /* nop the block */
    148   lea = block_end - 7;
    149   disp = (i64)storage - (i64)(site_pc + 4u);           /* rip = end of lea */
    150   lea[0] = (u8)(0x48u | ((rd >= 8u) ? 0x04u : 0x00u)); /* REX.W (+REX.R) */
    151   lea[1] = 0x8Du;                                      /* lea */
    152   lea[2] = (u8)(((rd & 7u) << 3) | 5u); /* mod=00 reg=rd rm=101 (rip) */
    153   wr_u32_le(lea + 3, (u32)disp);
    154 }