kit

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

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 }