kit

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

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 }