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 }