reloc_desc_test.c (11058B)
1 /* Relocation-descriptor migration guard (doc/plan/RELOC.md, WS-B). 2 * 3 * The per-arch RelocDesc table replaced the generic reloc_width / 4 * reloc_uses_got / reloc_is_tls_got switches and the per-arch 5 * LinkArchDesc.is_branch / is_got_load / is_tlvp / is_direct_page / 6 * needs_jit_call_stub hooks. This test pins behavioural equivalence: for 7 * every RelocKind, under every backend arch, reloc_desc() and the 8 * reloc_kind_* predicates must reproduce the frozen pre-refactor behaviour 9 * captured by the oracle_* functions below. 10 * 11 * The oracle_* bodies are VERBATIM snapshots of the deleted code — do not 12 * "improve" them; they are the spec the descriptor table must match. 13 * 14 * Coverage of per-arch *ownership* of non-classifier kinds (a kind sized 15 * under the wrong arch) is left to the bootstrap / smoke link oracles, which 16 * patch every kind a backend actually emits — the unit guard here pins the 17 * width of every row that exists, the classification of every kind, and that 18 * no previously-sized kind lost its descriptor. */ 19 20 #include <kit/cg.h> 21 #include <kit/core.h> 22 23 #include "core/core.h" 24 #include "lib/kit_unit.h" 25 #include "link/link_reloc_desc.h" 26 #include "obj/obj.h" 27 #include "obj/reloc.h" 28 29 static KitUnit g_u; 30 #define EXPECT(cond, ...) CU_EXPECT(&g_u, cond, __VA_ARGS__) 31 32 /* ============================================================ 33 * Frozen pre-refactor behaviour (the migration guard) 34 * ============================================================ */ 35 36 #define ORACLE_RV_ULEB128_NOMINAL_WIDTH 1u 37 38 /* Verbatim snapshot of the former reloc_width() (arch-independent), with one 39 * deliberate post-freeze addition: R_RV_ALIGN's marker row (see its case). */ 40 static u8 oracle_width(RelocKind k) { 41 switch (k) { 42 case R_ABS32: 43 case R_REL32: 44 case R_PC32: 45 case R_GOT32: 46 case R_PLT32: 47 case R_X64_PLT32: 48 case R_X64_32S: 49 case R_X64_TPOFF32: 50 case R_X64_DTPOFF32: 51 /* R_X64_TLSLD: post-freeze addition for ELF local-dynamic TLS relaxation. 52 * The relocation points at the disp32 in `leaq x@TLSLD(%rip), %rdi`; the 53 * x64 apply hook owns the surrounding 12-byte block rewrite. */ 54 case R_X64_TLSLD: 55 case R_X64_GOTPCREL: 56 case R_X64_GOTPCRELX: 57 case R_X64_REX_GOTPCRELX: 58 case R_X64_GOTPC32: 59 case R_X64_GOTTPOFF: 60 /* R_X64_TLV: post-freeze addition — the Mach-O x86_64 TLV descriptor 61 * access (RIP-relative disp32), peer of arm64's TLVP_LOAD_PAGE21/12. */ 62 case R_X64_TLV: 63 return 4; 64 case R_ABS64: 65 case R_REL64: 66 case R_PC64: 67 case R_TPOFF64: 68 case R_X64_GLOB_DAT: 69 case R_X64_JUMP_SLOT: 70 case R_X64_RELATIVE: 71 return 8; 72 case R_ABS8: 73 case R_X64_PC8: 74 return 1; 75 case R_ABS16: 76 case R_PREL16: 77 return 2; 78 case R_AARCH64_JUMP26: 79 case R_AARCH64_CALL26: 80 case R_AARCH64_CONDBR19: 81 case R_AARCH64_TSTBR14: 82 case R_AARCH64_LD_PREL_LO19: 83 case R_AARCH64_ADR_PREL_LO21: 84 case R_AARCH64_ADR_PREL_PG_HI21: 85 case R_AARCH64_ADR_PREL_PG_HI21_NC: 86 case R_AARCH64_ADD_ABS_LO12_NC: 87 case R_AARCH64_LDST8_ABS_LO12_NC: 88 case R_AARCH64_LDST16_ABS_LO12_NC: 89 case R_AARCH64_LDST32_ABS_LO12_NC: 90 case R_AARCH64_LDST64_ABS_LO12_NC: 91 case R_AARCH64_LDST128_ABS_LO12_NC: 92 case R_AARCH64_ADR_GOT_PAGE: 93 case R_AARCH64_LD64_GOT_LO12_NC: 94 case R_AARCH64_POINTER_TO_GOT: 95 case R_AARCH64_TLSDESC_ADR_PAGE21: 96 case R_AARCH64_TLSDESC_LD64_LO12: 97 case R_AARCH64_TLSDESC_ADD_LO12: 98 case R_AARCH64_TLSDESC_CALL: 99 case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: 100 case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: 101 case R_AARCH64_TLSLE_ADD_TPREL_HI12: 102 case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: 103 case R_AARCH64_TLVP_LOAD_PAGE21: 104 case R_AARCH64_TLVP_LOAD_PAGEOFF12: 105 return 4; 106 case R_RV_HI20: 107 case R_RV_LO12_I: 108 case R_RV_LO12_S: 109 case R_RV_BRANCH: 110 case R_RV_JAL: 111 case R_RV_PCREL_HI20: 112 case R_RV_PCREL_LO12_I: 113 case R_RV_PCREL_LO12_S: 114 case R_RV_GOT_HI20: 115 case R_RV_TLS_GOT_HI20: 116 case R_RV_TLS_GD_HI20: 117 case R_RV_TPREL_HI20: 118 case R_RV_TPREL_LO12_I: 119 case R_RV_TPREL_LO12_S: 120 return 4; 121 case R_RV_CALL: 122 return 8; 123 case R_RV_RVC_BRANCH: 124 case R_RV_RVC_JUMP: 125 return 2; 126 case R_RV_RELAX: 127 case R_RV_TPREL_ADD: 128 /* R_RV_ALIGN gained a marker descriptor row post-freeze (cleanup wave D, 129 * D.2, commit c0b3c960): link_emit_relocations now drops all three RISC-V 130 * relaxation markers via the RELOC_MARKER descriptor flag instead of a 131 * hardcoded name list, so ALIGN needs a row to be recognized. Its width is 132 * the conventional unused gate value (4) shared by its sibling markers; it 133 * is never read, since markers are skipped before the width gate. */ 134 case R_RV_ALIGN: 135 return 4; 136 case R_ADD8: 137 case R_SUB8: 138 case R_SUB6: 139 case R_SET6: 140 return 1; 141 case R_ADD16: 142 case R_SUB16: 143 return 2; 144 case R_ADD32: 145 case R_SUB32: 146 return 4; 147 case R_ADD64: 148 case R_SUB64: 149 return 8; 150 case R_SET_ULEB128: 151 case R_SUB_ULEB128: 152 return ORACLE_RV_ULEB128_NOMINAL_WIDTH; 153 case R_COFF_SECREL: 154 case R_COFF_ADDR32NB: 155 return 4; 156 case R_COFF_SECTION: 157 return 2; 158 case R_COFF_AARCH64_SECREL_LOW12A: 159 case R_COFF_AARCH64_SECREL_HIGH12A: 160 case R_COFF_AARCH64_SECREL_LOW12L: 161 return 4; 162 default: 163 return 0; 164 } 165 } 166 167 /* Per-arch classification snapshots. The deleted reloc_uses_got / 168 * reloc_is_tls_got were arch-independent switches; they partition cleanly 169 * by arch (each case belongs to exactly one backend), so the equivalent 170 * arch-scoped predicate is the per-arch slice below. branch / got_load / 171 * tlvp / direct_page mirror the former per-arch LinkArchDesc hooks (NULL 172 * hook == always 0). */ 173 174 /* RELOC_USES_GOT set (direct GOT load): the Mach-O is_got_load hook for 175 * aa64/x64, and the GOT-allocating subset of reloc_uses_got for rv. */ 176 static int oracle_aa64_got_use(RelocKind k) { 177 return k == R_AARCH64_ADR_GOT_PAGE || k == R_AARCH64_LD64_GOT_LO12_NC || 178 k == R_AARCH64_POINTER_TO_GOT; 179 } 180 static int oracle_x64_got_use(RelocKind k) { 181 return k == R_X64_GOTPCREL || k == R_X64_GOTPCRELX || 182 k == R_X64_REX_GOTPCRELX; 183 } 184 static int oracle_rv_got_use(RelocKind k) { return k == R_RV_GOT_HI20; } 185 186 /* RELOC_IS_TLS_GOT set (TLS Initial-Exec GOT slot). */ 187 static int oracle_aa64_tls_got(RelocKind k) { 188 return k == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 || 189 k == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC; 190 } 191 static int oracle_x64_tls_got(RelocKind k) { return k == R_X64_GOTTPOFF; } 192 static int oracle_rv_tls_got(RelocKind k) { return k == R_RV_TLS_GOT_HI20; } 193 194 /* needs_jit_call_stub / is_branch_reloc. */ 195 static int oracle_aa64_branch(RelocKind k) { 196 return k == R_AARCH64_CALL26 || k == R_AARCH64_JUMP26; 197 } 198 static int oracle_x64_branch(RelocKind k) { 199 return k == R_X64_PLT32 || k == R_PLT32; 200 } 201 static int oracle_rv_branch(RelocKind k) { 202 return k == R_RV_CALL || k == R_PLT32; 203 } 204 205 /* is_tlvp_reloc (Mach-O). */ 206 static int oracle_aa64_tlvp(RelocKind k) { 207 return k == R_AARCH64_TLVP_LOAD_PAGE21 || k == R_AARCH64_TLVP_LOAD_PAGEOFF12; 208 } 209 static int oracle_x64_tlvp(RelocKind k) { return k == R_X64_TLV; } 210 211 /* is_direct_page_reloc (Mach-O aa64 only). */ 212 static int oracle_aa64_direct_page(RelocKind k) { 213 switch (k) { 214 case R_AARCH64_ADR_PREL_PG_HI21: 215 case R_AARCH64_ADR_PREL_PG_HI21_NC: 216 case R_AARCH64_ADD_ABS_LO12_NC: 217 case R_AARCH64_LDST8_ABS_LO12_NC: 218 case R_AARCH64_LDST16_ABS_LO12_NC: 219 case R_AARCH64_LDST32_ABS_LO12_NC: 220 case R_AARCH64_LDST64_ABS_LO12_NC: 221 case R_AARCH64_LDST128_ABS_LO12_NC: 222 return 1; 223 default: 224 return 0; 225 } 226 } 227 228 static int zero_oracle(RelocKind k) { 229 (void)k; 230 return 0; 231 } 232 233 typedef struct ArchOracle { 234 const char* name; 235 KitArchKind arch; 236 int (*got_use)(RelocKind); 237 int (*tls_got)(RelocKind); 238 int (*branch)(RelocKind); 239 int (*tlvp)(RelocKind); 240 int (*direct_page)(RelocKind); 241 } ArchOracle; 242 243 static const ArchOracle kArchOracles[] = { 244 {"aarch64", KIT_ARCH_ARM_64, oracle_aa64_got_use, oracle_aa64_tls_got, 245 oracle_aa64_branch, oracle_aa64_tlvp, oracle_aa64_direct_page}, 246 {"x86_64", KIT_ARCH_X86_64, oracle_x64_got_use, oracle_x64_tls_got, 247 oracle_x64_branch, oracle_x64_tlvp, zero_oracle}, 248 {"rv64", KIT_ARCH_RV64, oracle_rv_got_use, oracle_rv_tls_got, 249 oracle_rv_branch, zero_oracle, zero_oracle}, 250 {"rv32", KIT_ARCH_RV32, oracle_rv_got_use, oracle_rv_tls_got, 251 oracle_rv_branch, zero_oracle, zero_oracle}, 252 }; 253 254 /* Last RelocKind enum value; the enum is contiguous from R_NONE = 0. */ 255 #define RELOC_KIND_LAST R_COFF_AARCH64_SECREL_LOW12L 256 257 static KitCompiler* new_compiler(KitArchKind arch) { 258 KitTargetSpec t = kit_unit_target(arch, KIT_OS_LINUX, KIT_OBJ_ELF); 259 KitCompiler* c = NULL; 260 if (arch == KIT_ARCH_RV32) { 261 t.ptr_size = 4; 262 t.ptr_align = 4; 263 } 264 if (kit_unit_compiler_new(&g_u, t, &c) != KIT_OK || !c) { 265 fprintf(stderr, "compiler_new failed for arch=%d\n", (int)arch); 266 exit(2); 267 } 268 return c; 269 } 270 271 int main(void) { 272 size_t a; 273 int covered[RELOC_KIND_LAST + 1]; 274 int kk; 275 276 kit_unit_init(&g_u); 277 for (kk = 0; kk <= (int)RELOC_KIND_LAST; ++kk) covered[kk] = 0; 278 279 for (a = 0; a < sizeof kArchOracles / sizeof kArchOracles[0]; ++a) { 280 const ArchOracle* ao = &kArchOracles[a]; 281 KitCompiler* c = new_compiler(ao->arch); 282 for (kk = 0; kk <= (int)RELOC_KIND_LAST; ++kk) { 283 RelocKind k = (RelocKind)kk; 284 const RelocDesc* d = reloc_desc(c, k); 285 286 /* Width parity wherever a row exists; coverage tracked separately. */ 287 if (d) { 288 covered[kk] = 1; 289 EXPECT(d->width == oracle_width(k), "%s: width(%d) = %u, want %u", 290 ao->name, kk, (unsigned)d->width, (unsigned)oracle_width(k)); 291 } 292 293 /* Classification parity, strict per arch (arch-scoped both sides). */ 294 EXPECT(reloc_kind_is_got_load(c, k) == ao->got_use(k), 295 "%s: is_got_load(%d) mismatch", ao->name, kk); 296 EXPECT(reloc_kind_is_tls_got(c, k) == ao->tls_got(k), 297 "%s: is_tls_got(%d) mismatch", ao->name, kk); 298 EXPECT(reloc_kind_uses_got(c, k) == (ao->got_use(k) || ao->tls_got(k)), 299 "%s: uses_got(%d) mismatch", ao->name, kk); 300 EXPECT(reloc_kind_is_branch(c, k) == ao->branch(k), 301 "%s: is_branch(%d) mismatch", ao->name, kk); 302 EXPECT(reloc_kind_is_tlvp(c, k) == ao->tlvp(k), 303 "%s: is_tlvp(%d) mismatch", ao->name, kk); 304 EXPECT(reloc_kind_is_direct_page(c, k) == ao->direct_page(k), 305 "%s: is_direct_page(%d) mismatch", ao->name, kk); 306 } 307 } 308 309 /* No previously-sized kind lost its descriptor: every kind the old 310 * reloc_width() sized must resolve under at least one backend arch. */ 311 for (kk = 0; kk <= (int)RELOC_KIND_LAST; ++kk) { 312 if (oracle_width((RelocKind)kk) != 0) 313 EXPECT(covered[kk], "kind %d had width %u but resolves under no arch", kk, 314 (unsigned)oracle_width((RelocKind)kk)); 315 } 316 317 kit_unit_summary(&g_u, "reloc_desc_test"); 318 return kit_unit_status(&g_u); 319 }