kit

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

reloc_arm.c (7591B)


      1 /* RelocKind <-> ARM (Thumb-2) ELF reloc-type mapping (ELFCLASS32).
      2  *
      3  * Self-contained (ARM has no 64-bit ELF sibling). Data words reuse the neutral
      4  * R_ABS32 / R_REL32 / R_ABS16 / R_ABS8 kinds; only the instruction-embedded
      5  * Thumb-2 kinds get arch entries. The split-immediate byte patcher lives in
      6  * src/arch/arm32/reloc.c. */
      7 #include "core/bytes.h"
      8 #include "obj/elf/elf.h"
      9 #include "obj/obj.h"
     10 
     11 u32 elf_arm_reloc_to(u32 kind /* RelocKind */) {
     12   switch (kind) {
     13     case R_NONE:
     14       return ELF_R_ARM_NONE;
     15     case R_ABS32:
     16       return ELF_R_ARM_ABS32;
     17     case R_REL32:
     18     case R_PC32:
     19       return ELF_R_ARM_REL32; /* PC-rel data collapses to REL32 */
     20     case R_ABS16:
     21       return ELF_R_ARM_ABS16;
     22     case R_ABS8:
     23       return ELF_R_ARM_ABS8;
     24     case R_ARM_THM_CALL:
     25       return ELF_R_ARM_THM_CALL;
     26     case R_ARM_THM_JUMP24:
     27       return ELF_R_ARM_THM_JUMP24;
     28     case R_ARM_THM_JUMP19:
     29       return ELF_R_ARM_THM_JUMP19;
     30     case R_ARM_THM_MOVW_ABS_NC:
     31       return ELF_R_ARM_THM_MOVW_ABS_NC;
     32     case R_ARM_THM_MOVT_ABS:
     33       return ELF_R_ARM_THM_MOVT_ABS;
     34     case R_ARM_THM_MOVW_PREL_NC:
     35       return ELF_R_ARM_THM_MOVW_PREL_NC;
     36     case R_ARM_THM_MOVT_PREL:
     37       return ELF_R_ARM_THM_MOVT_PREL;
     38     case R_ARM_TLS_LE32:
     39       return ELF_R_ARM_TLS_LE32;
     40     default:
     41       return ELF_R_ARM_NONE;
     42   }
     43 }
     44 
     45 u32 elf_arm_reloc_from(u32 elf_type) {
     46   switch (elf_type) {
     47     case ELF_R_ARM_NONE:
     48       return R_NONE;
     49     case ELF_R_ARM_ABS32:
     50       return R_ABS32;
     51     case ELF_R_ARM_REL32:
     52       return R_REL32;
     53     case ELF_R_ARM_ABS16:
     54       return R_ABS16;
     55     case ELF_R_ARM_ABS8:
     56       return R_ABS8;
     57     case ELF_R_ARM_THM_CALL:
     58       return R_ARM_THM_CALL;
     59     case ELF_R_ARM_THM_JUMP24:
     60       return R_ARM_THM_JUMP24;
     61     case ELF_R_ARM_THM_JUMP19:
     62       return R_ARM_THM_JUMP19;
     63     case ELF_R_ARM_THM_MOVW_ABS_NC:
     64       return R_ARM_THM_MOVW_ABS_NC;
     65     case ELF_R_ARM_THM_MOVT_ABS:
     66       return R_ARM_THM_MOVT_ABS;
     67     case ELF_R_ARM_THM_MOVW_PREL_NC:
     68       return R_ARM_THM_MOVW_PREL_NC;
     69     case ELF_R_ARM_THM_MOVT_PREL:
     70       return R_ARM_THM_MOVT_PREL;
     71     case ELF_R_ARM_TLS_LE32:
     72       return R_ARM_TLS_LE32;
     73     default:
     74       return (u32)-1; /* reader diagnoses unsupported wire types */
     75   }
     76 }
     77 
     78 const char* elf_arm_reloc_name(u32 elf_type) {
     79   switch (elf_type) {
     80     case ELF_R_ARM_NONE:
     81       return "R_ARM_NONE";
     82     case ELF_R_ARM_ABS32:
     83       return "R_ARM_ABS32";
     84     case ELF_R_ARM_REL32:
     85       return "R_ARM_REL32";
     86     case ELF_R_ARM_ABS16:
     87       return "R_ARM_ABS16";
     88     case ELF_R_ARM_ABS8:
     89       return "R_ARM_ABS8";
     90     case ELF_R_ARM_THM_CALL:
     91       return "R_ARM_THM_CALL";
     92     case ELF_R_ARM_THM_JUMP24:
     93       return "R_ARM_THM_JUMP24";
     94     case ELF_R_ARM_THM_JUMP19:
     95       return "R_ARM_THM_JUMP19";
     96     case ELF_R_ARM_THM_MOVW_ABS_NC:
     97       return "R_ARM_THM_MOVW_ABS_NC";
     98     case ELF_R_ARM_THM_MOVT_ABS:
     99       return "R_ARM_THM_MOVT_ABS";
    100     case ELF_R_ARM_THM_MOVW_PREL_NC:
    101       return "R_ARM_THM_MOVW_PREL_NC";
    102     case ELF_R_ARM_THM_MOVT_PREL:
    103       return "R_ARM_THM_MOVT_PREL";
    104     case ELF_R_ARM_TLS_LE32:
    105       return "R_ARM_TLS_LE32";
    106     default:
    107       return NULL;
    108   }
    109 }
    110 
    111 /* Decode the float ABI from ARM ELF e_flags (EF_ARM_ABI_FLOAT_*). The two
    112  * flags are independent bits, not a mask field. */
    113 KitFloatAbi elf_arm_float_abi_from_e_flags(u32 e_flags) {
    114   if (e_flags & EF_ARM_ABI_FLOAT_HARD) return KIT_FLOAT_ABI_SINGLE;
    115   if (e_flags & EF_ARM_ABI_FLOAT_SOFT) return KIT_FLOAT_ABI_SOFT;
    116   return KIT_FLOAT_ABI_DEFAULT;
    117 }
    118 
    119 u32 elf_arm_float_abi_to_e_flags(KitFloatAbi abi) {
    120   /* Match the ARM toolchain convention (clang/gas): only hard-float objects
    121    * set a float-ABI bit; soft/agnostic objects leave both unset (soft is the
    122    * default). Setting EF_ARM_ABI_FLOAT_SOFT would make every kit object
    123    * mismatch the ecosystem's soft objects under the linker's e_flags check. */
    124   switch (abi) {
    125     case KIT_FLOAT_ABI_SINGLE:
    126     case KIT_FLOAT_ABI_DOUBLE:
    127       return EF_ARM_ABI_FLOAT_HARD;
    128     default:
    129       return 0u; /* soft / agnostic: no float-ABI bit */
    130   }
    131 }
    132 
    133 /* A 32-bit Thumb-2 instruction is two independently-little-endian 16-bit
    134  * halfwords, the HIGH halfword FIRST in memory (matches arm_rd_t32 in
    135  * src/arch/arm32/reloc.c). The returned value has hw1 in bits [31:16]. */
    136 static u32 arm_rd_t32_field(const u8* p) {
    137   return ((u32)rd_u16_le(p) << 16) | (u32)rd_u16_le(p + 2);
    138 }
    139 
    140 static i32 sign_extend(u32 v, u32 bits) {
    141   u32 m = 1u << (bits - 1);
    142   return (i32)((v ^ m) - m);
    143 }
    144 
    145 /* Reconstruct the implicit addend a REL relocation carries in its field — the
    146  * inverse of the patchers in src/arch/arm32/reloc.c. For REL there is no
    147  * r_addend slot, so kit's own linker must recover the same addend a
    148  * field-reading linker (ld.lld, GNU ld) would. `field` points at the
    149  * relocated location; `width` is its byte width (4 for data words and Thumb-2
    150  * instructions). Mirrors the AAELF32 REL addend conventions. */
    151 i64 elf_arm_reloc_field_addend(u32 kind /* RelocKind */, const u8* field,
    152                                u32 width) {
    153   switch (kind) {
    154     case R_ABS32:
    155     case R_REL32:
    156     case R_PC32:
    157       /* Plain little-endian data word; the addend sits in the field verbatim
    158        * (api_data_encode_addend pre-wrote it on emit). */
    159       return width >= 4 ? (i64)(i32)rd_u32_le(field) : 0;
    160     case R_ABS16:
    161       return (i64)(i16)rd_u16_le(field);
    162     case R_ABS8:
    163       return (i64)(i8)field[0];
    164     case R_ARM_THM_CALL:
    165     case R_ARM_THM_JUMP24: {
    166       /* BL/B.W 25-bit field {S,I1,I2,imm10,imm11}, J↔I XOR-with-S. The patcher
    167        * wrote disp = (S&~1)+A-P-4; at read time S=P=0, so A = disp + 4. The
    168        * canonical placeholder (disp=-4) thus decodes to addend 0. */
    169       u32 instr = arm_rd_t32_field(field);
    170       u32 sbit = (instr >> 26) & 1u;
    171       u32 j1 = (instr >> 13) & 1u, j2 = (instr >> 11) & 1u;
    172       u32 i1 = (~(j1 ^ sbit)) & 1u, i2 = (~(j2 ^ sbit)) & 1u;
    173       u32 imm10 = (instr >> 16) & 0x3ffu, imm11 = instr & 0x7ffu;
    174       u32 off = (sbit << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) |
    175                 (imm11 << 1);
    176       return (i64)sign_extend(off, 25) + 4;
    177     }
    178     case R_ARM_THM_JUMP19: {
    179       /* B<cond>.W 21-bit field {S,J2,J1,imm6,imm11} (no XOR-with-S). */
    180       u32 instr = arm_rd_t32_field(field);
    181       u32 sbit = (instr >> 26) & 1u;
    182       u32 j2 = (instr >> 11) & 1u, j1 = (instr >> 13) & 1u;
    183       u32 imm6 = (instr >> 16) & 0x3fu, imm11 = instr & 0x7ffu;
    184       u32 off =
    185           (sbit << 20) | (j2 << 19) | (j1 << 18) | (imm6 << 12) | (imm11 << 1);
    186       return (i64)sign_extend(off, 21) + 4;
    187     }
    188     case R_ARM_THM_MOVW_ABS_NC:
    189     case R_ARM_THM_MOVT_ABS:
    190     case R_ARM_THM_MOVW_PREL_NC:
    191     case R_ARM_THM_MOVT_PREL: {
    192       /* MOVW/MOVT 16-bit immediate scattered imm4:i:imm3:imm8. Each reloc
    193        * carries only one 16-bit half of (S+A); at read time S=0 so the field
    194        * is (A & 0xffff) for MOVW or ((A>>16) & 0xffff) for MOVT. Current arm32
    195        * codegen always emits addend 0 here (a constant byte offset folds into
    196        * a separate add), so this decodes to 0. A non-zero addend split across
    197        * the MOVW/MOVT pair would need both halves recombined into one 32-bit
    198        * A for kit-ld's (S+A) patcher — a Phase-2+ refinement; lld already
    199        * handles the per-half group-relocation form. */
    200       u32 instr = arm_rd_t32_field(field);
    201       u32 imm4 = (instr >> 16) & 0xfu, i = (instr >> 26) & 1u;
    202       u32 imm3 = (instr >> 12) & 7u, imm8 = instr & 0xffu;
    203       u32 imm16 = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8;
    204       return (i64)imm16;
    205     }
    206     default:
    207       return 0;
    208   }
    209 }