kit

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

reloc_aarch64.c (6409B)


      1 /* RelocKind <-> arm64 Mach-O reloc-type mapping.  Mirror of
      2  * elf_reloc_aarch64.c for Mach-O.
      3  *
      4  * Mach-O relocations carry several independent fields that the kit
      5  * RelocKind enum collapses into a single value: r_type (the 4-bit
      6  * ARM64_RELOC_* code), r_pcrel, and r_length.  The writer (macho_emit.c)
      7  * consults the reloc_to / reloc_pcrel / reloc_length accessors per Reloc;
      8  * the reader (macho_read.c) inverts them in macho_aarch64_reloc_decode,
      9  * which also classifies the entry's structural role (addend carrier,
     10  * subtractor) and inspects the patched instruction for the lo12 width. */
     11 
     12 #include "core/bytes.h"
     13 #include "core/util.h"
     14 #include "obj/macho/macho.h"
     15 
     16 u32 macho_aarch64_reloc_to(u32 kind /* RelocKind */) {
     17   switch (kind) {
     18     case R_NONE:
     19       return (u32)-1;
     20     case R_ABS64:
     21     case R_ABS32:
     22       return ARM64_RELOC_UNSIGNED;
     23     case R_REL64:
     24     case R_REL32:
     25     case R_PC64:
     26     case R_PC32:
     27       /* PC-relative absolute pointer-difference; encoded as
     28        * UNSIGNED with r_pcrel=1, length=3/2. */
     29       return ARM64_RELOC_UNSIGNED;
     30     case R_AARCH64_JUMP26:
     31     case R_AARCH64_CALL26:
     32       return ARM64_RELOC_BRANCH26;
     33     case R_AARCH64_ADR_PREL_PG_HI21:
     34     case R_AARCH64_ADR_PREL_PG_HI21_NC:
     35       return ARM64_RELOC_PAGE21;
     36     case R_AARCH64_ADD_ABS_LO12_NC:
     37     case R_AARCH64_LDST8_ABS_LO12_NC:
     38     case R_AARCH64_LDST16_ABS_LO12_NC:
     39     case R_AARCH64_LDST32_ABS_LO12_NC:
     40     case R_AARCH64_LDST64_ABS_LO12_NC:
     41     case R_AARCH64_LDST128_ABS_LO12_NC:
     42       return ARM64_RELOC_PAGEOFF12;
     43     case R_AARCH64_ADR_GOT_PAGE:
     44       return ARM64_RELOC_GOT_LOAD_PAGE21;
     45     case R_AARCH64_LD64_GOT_LO12_NC:
     46       return ARM64_RELOC_GOT_LOAD_PAGEOFF12;
     47     case R_AARCH64_POINTER_TO_GOT:
     48       return ARM64_RELOC_POINTER_TO_GOT;
     49     case R_AARCH64_TLVP_LOAD_PAGE21:
     50       return ARM64_RELOC_TLVP_LOAD_PAGE21;
     51     case R_AARCH64_TLVP_LOAD_PAGEOFF12:
     52       return ARM64_RELOC_TLVP_LOAD_PAGEOFF12;
     53     default:
     54       return (u32)-1;
     55   }
     56 }
     57 
     58 u32 macho_aarch64_reloc_pcrel(u32 kind /* RelocKind */) {
     59   switch (kind) {
     60     case R_REL64:
     61     case R_REL32:
     62     case R_PC64:
     63     case R_PC32:
     64     case R_AARCH64_JUMP26:
     65     case R_AARCH64_CALL26:
     66     case R_AARCH64_ADR_PREL_PG_HI21:
     67     case R_AARCH64_ADR_PREL_PG_HI21_NC:
     68     case R_AARCH64_ADR_GOT_PAGE:
     69     case R_AARCH64_POINTER_TO_GOT:
     70     case R_AARCH64_TLVP_LOAD_PAGE21:
     71       return 1;
     72     default:
     73       return 0;
     74   }
     75 }
     76 
     77 u32 macho_aarch64_reloc_length(u32 kind /* RelocKind */) {
     78   /* log2 of the patch width in bytes: 0=byte, 1=hword, 2=word, 3=quad.
     79    * AArch64 instructions are 4 bytes and Mach-O encodes any 32-bit fixup
     80    * (BRANCH26, PAGE21, PAGEOFF12, ...) with length=2. */
     81   switch (kind) {
     82     case R_ABS64:
     83     case R_REL64:
     84     case R_PC64:
     85       return 3;
     86     default:
     87       return 2;
     88   }
     89 }
     90 
     91 /* ARM64_RELOC_PAGEOFF12 is access-size-agnostic on the wire; the linker
     92  * applier needs the load/store width (or ADD) to scale the lo12 immediate, so
     93  * inspect the patched instruction to pick the precise RelocKind. Defaults to
     94  * the ADD form when the patch bytes are unavailable. */
     95 static u32 macho_aarch64_pageoff12_kind(const u8* insn, u32 insn_len) {
     96   if (insn_len < 4) return R_AARCH64_ADD_ABS_LO12_NC;
     97   u32 ins = rd_u32_le(insn);
     98   /* ADD (immediate): bits 30:24 = 0010001 (W=0x11000000 / X=0x91000000);
     99    * the 0x7f800000 mask isolates the pattern leaving sf free. */
    100   if ((ins & 0x7f800000u) == 0x11000000u) return R_AARCH64_ADD_ABS_LO12_NC;
    101   /* LDR/STR (immediate, unsigned offset): bits 29:27=111, 25:24=01. size in
    102    * [31:30]; V=bit26 (SIMD/FP); opc bit23 distinguishes the 128-bit case. */
    103   if ((ins & 0x3b000000u) == 0x39000000u) {
    104     u32 sz = (ins >> 30) & 3u;
    105     u32 v_bit = (ins >> 26) & 1u;
    106     u32 opc1 = (ins >> 23) & 1u;
    107     if (v_bit && sz == 0 && opc1) return R_AARCH64_LDST128_ABS_LO12_NC;
    108     return (sz == 0)   ? R_AARCH64_LDST8_ABS_LO12_NC
    109            : (sz == 1) ? R_AARCH64_LDST16_ABS_LO12_NC
    110            : (sz == 2) ? R_AARCH64_LDST32_ABS_LO12_NC
    111                        : R_AARCH64_LDST64_ABS_LO12_NC;
    112   }
    113   return R_AARCH64_ADD_ABS_LO12_NC;
    114 }
    115 
    116 int macho_aarch64_reloc_decode(const MachoRelocEntry* in,
    117                                MachoRelocDecoded* out) {
    118   out->kind = R_NONE;
    119   out->role = MACHO_RELOC_NORMAL;
    120   out->addend = 0;
    121   out->inplace_bias = 0;
    122   out->inplace_addend = 0;
    123   out->inplace_signed = 0;
    124   switch (in->r_type) {
    125     case ARM64_RELOC_ADDEND: {
    126       /* Addend carrier: a signed 24-bit immediate in r_symbolnum (not a
    127        * symbol-table reference). Seeds the following reloc's addend. */
    128       i32 ad = (i32)(in->r_symbolnum & 0x00ffffffu);
    129       if (ad & 0x00800000) ad |= ~0x00ffffff;
    130       out->role = MACHO_RELOC_ADDEND;
    131       out->addend = (i64)ad;
    132       return 1;
    133     }
    134     case ARM64_RELOC_SUBTRACTOR:
    135       out->role = MACHO_RELOC_SUBTRACTOR;
    136       out->kind = (in->r_length == 3)   ? R_SUB64
    137                   : (in->r_length == 2) ? R_SUB32
    138                   : (in->r_length == 1) ? R_SUB16
    139                                         : R_SUB8;
    140       return 1;
    141     case ARM64_RELOC_UNSIGNED:
    142       /* The unsigned datum: pcrel + length pick the kit kind; the shared
    143        * reader rewrites it to R_ADD* when it completes a pending subtractor.
    144        * Its addend rides in the patched field. */
    145       out->role = MACHO_RELOC_ABSOLUTE;
    146       out->inplace_addend = 1;
    147       if (in->r_pcrel)
    148         out->kind = (in->r_length == 3) ? R_PC64 : R_PC32;
    149       else
    150         out->kind = (in->r_length == 3) ? R_ABS64 : R_ABS32;
    151       return 1;
    152     case ARM64_RELOC_BRANCH26:
    153       out->kind = R_AARCH64_CALL26;
    154       return 1;
    155     case ARM64_RELOC_PAGE21:
    156       out->kind = R_AARCH64_ADR_PREL_PG_HI21;
    157       return 1;
    158     case ARM64_RELOC_PAGEOFF12:
    159       out->kind = macho_aarch64_pageoff12_kind(in->insn, in->insn_len);
    160       return 1;
    161     case ARM64_RELOC_GOT_LOAD_PAGE21:
    162       out->kind = R_AARCH64_ADR_GOT_PAGE;
    163       return 1;
    164     case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
    165       out->kind = R_AARCH64_LD64_GOT_LO12_NC;
    166       return 1;
    167     case ARM64_RELOC_POINTER_TO_GOT:
    168       out->kind = R_AARCH64_POINTER_TO_GOT;
    169       out->inplace_addend = 1;
    170       out->inplace_signed = 1;
    171       return 1;
    172     case ARM64_RELOC_TLVP_LOAD_PAGE21:
    173       out->kind = R_AARCH64_TLVP_LOAD_PAGE21;
    174       return 1;
    175     case ARM64_RELOC_TLVP_LOAD_PAGEOFF12:
    176       out->kind = R_AARCH64_TLVP_LOAD_PAGEOFF12;
    177       return 1;
    178     default:
    179       return 0;
    180   }
    181 }