rv32_class32.c (9691B)
1 /* ELFCLASS32 write->read round-trip — the first ELFCLASS32 consumer test 2 * for the kit rv32 port. 3 * 4 * Builds a tiny riscv32-none-elf relocatable in memory: one .text section 5 * with a few bytes, a GLOBAL FUNC symbol, and one R_ABS32 relocation (the 6 * RV32 primary absolute reloc, which the ELF emitter lowers to 7 * ELF_R_RISCV_32). Then: 8 * 9 * kit_obj_builder_emit(ob, mem_writer) 10 * kit_obj_open("rv32_class32", bytes, len) 11 * 12 * and asserts the readback is a 32-bit (ELFCLASS32) RISC-V object whose 13 * symbol and relocation survived intact. 14 * 15 * Unlike the AArch64/x64/rv64 unit tests this does NOT use 16 * kit_test_target_init: that helper hardcodes ptr_size=8 and has no rv32 17 * branch. Instead we build a fixed rv32 KitTargetSpec directly. 18 * 19 * Exit 0 = pass; non-zero = fail (with one-line stderr explanations). */ 20 21 #include <kit/core.h> 22 #include <kit/object.h> 23 #include <stdarg.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 28 /* ---- env ---- */ 29 30 static void* heap_alloc(KitHeap* h, size_t n, size_t a) { 31 (void)h; 32 (void)a; 33 return n ? malloc(n) : NULL; 34 } 35 static void* heap_realloc(KitHeap* h, void* p, size_t o, size_t n, size_t a) { 36 (void)h; 37 (void)o; 38 (void)a; 39 return realloc(p, n); 40 } 41 static void heap_free(KitHeap* h, void* p, size_t n) { 42 (void)h; 43 (void)n; 44 free(p); 45 } 46 static KitHeap g_heap = {heap_alloc, heap_realloc, heap_free, NULL}; 47 48 static void diag_emit(KitDiagSink* s, KitDiagKind k, KitSrcLoc loc, 49 const char* fmt, va_list ap) { 50 static const char* names[] = {"note", "warning", "error", "fatal"}; 51 (void)s; 52 (void)loc; 53 fprintf(stderr, "%s: ", names[k]); 54 vfprintf(stderr, fmt, ap); 55 fputc('\n', stderr); 56 } 57 static KitDiagSink g_diag = {diag_emit, NULL, 0, 0}; 58 59 /* ---- assertion helpers ---- */ 60 61 static int g_failures; 62 #define CHECK(cond, ...) \ 63 do { \ 64 if (!(cond)) { \ 65 fprintf(stderr, "FAIL %s:%d: ", __FILE__, __LINE__); \ 66 fprintf(stderr, __VA_ARGS__); \ 67 fputc('\n', stderr); \ 68 g_failures++; \ 69 } \ 70 } while (0) 71 72 /* ---- ELF on-the-wire constants (raw-byte checks) ---- */ 73 74 #define EI_CLASS 4 75 #define ELFCLASS32 1 76 #define EM_RISCV 243 77 78 /* ---- input fixture ---- */ 79 80 /* RV32I: addi a0, zero, 0 ; jalr zero, 0(ra) — little-endian. 81 * Exact instruction encoding is irrelevant to this object-shape test; the 82 * bytes only need to round-trip verbatim. */ 83 static const uint8_t TEXT_BYTES[8] = { 84 0x13, 0x05, 0x00, 0x00, 0x67, 0x80, 0x00, 0x00, 85 }; 86 87 #define RELOC_OFFSET 4u 88 89 int main(void) { 90 /* Fixed rv32 target: ELFCLASS32, RISC-V, little-endian, 4-byte pointers. */ 91 KitTargetSpec target; 92 memset(&target, 0, sizeof target); 93 target.arch = KIT_ARCH_RV32; 94 target.os = KIT_OS_LINUX; 95 target.obj = KIT_OBJ_ELF; 96 target.ptr_size = 4; 97 target.ptr_align = 4; 98 target.big_endian = 0; 99 100 KitContext ctx; 101 memset(&ctx, 0, sizeof ctx); 102 ctx.heap = &g_heap; 103 ctx.diag = &g_diag; 104 ctx.now = -1; 105 106 KitTargetOptions target_opts; 107 memset(&target_opts, 0, sizeof target_opts); 108 target_opts.spec = target; 109 KitTarget* kt = NULL; 110 if (kit_target_new(&ctx, &target_opts, &kt) != KIT_OK || !kt) { 111 fprintf(stderr, "FAIL: kit_target_new (rv32)\n"); 112 return 1; 113 } 114 KitCompiler* cc = NULL; 115 if (kit_compiler_new(kt, &ctx, &cc) != KIT_OK || !cc) { 116 fprintf(stderr, "FAIL: kit_compiler_new\n"); 117 kit_target_free(kt); 118 return 1; 119 } 120 121 /* ---- build ---- */ 122 KitObjBuilder* in = NULL; 123 CHECK(kit_obj_builder_new(cc, &in) == KIT_OK && in, "kit_obj_builder_new"); 124 125 KitObjSection sec_text = KIT_SECTION_NONE; 126 KitObjSectionDesc text_desc = { 127 .name = kit_sym_intern(cc, KIT_SLICE_LIT(".text")), 128 .kind = KIT_SEC_TEXT, 129 .flags = KIT_SF_ALLOC | KIT_SF_EXEC, 130 .align = 4, 131 .entsize = 0, 132 }; 133 CHECK(kit_obj_builder_section(in, &text_desc, &sec_text) == KIT_OK, 134 "section .text"); 135 CHECK(kit_obj_builder_write(in, sec_text, TEXT_BYTES, sizeof TEXT_BYTES) == 136 KIT_OK, 137 "write .text"); 138 139 KitObjSymbol sym_func = KIT_OBJ_SYMBOL_NONE; 140 KitObjSymbolDesc func_desc = { 141 .name = kit_sym_intern(cc, KIT_SLICE_LIT("rv32_sym")), 142 .bind = KIT_SB_GLOBAL, 143 .kind = KIT_SK_FUNC, 144 .section = sec_text, 145 .value = 0, 146 .size = sizeof TEXT_BYTES, 147 }; 148 CHECK(kit_obj_builder_symbol(in, &func_desc, &sym_func) == KIT_OK, 149 "symbol rv32_sym"); 150 151 /* One 32-bit absolute reloc. R_ABS32 -> ELF_R_RISCV_32 on RV32, so an 152 * Elf32_Rela survives the round-trip; aimed at rv32_sym itself. */ 153 KitObjRelocDesc reloc_desc = { 154 .section = sec_text, 155 .offset = RELOC_OFFSET, 156 .kind = {.arch = target.arch, 157 .obj_fmt = target.obj, 158 .code = KIT_RELOC_ABS32}, 159 .symbol = sym_func, 160 .addend = 0, 161 }; 162 CHECK(kit_obj_builder_reloc(in, &reloc_desc) == KIT_OK, "reloc .text"); 163 164 CHECK(kit_obj_builder_finalize(in) == KIT_OK, "finalize"); 165 166 /* ---- emit to memory ---- */ 167 KitWriter* w = NULL; 168 (void)kit_writer_mem(&g_heap, &w); 169 CHECK(kit_obj_builder_emit(in, w) == KIT_OK, "emit"); 170 size_t out_len = 0; 171 const uint8_t* out_data = kit_writer_mem_bytes(w, &out_len); 172 173 /* Sanity: ELF magic. */ 174 CHECK(out_len >= 52, "ELF emit produced too few bytes (%zu)", out_len); 175 CHECK(out_len >= 4 && out_data[0] == 0x7f && out_data[1] == 'E' && 176 out_data[2] == 'L' && out_data[3] == 'F', 177 "ELF emit output missing ELF magic"); 178 179 /* (a) Raw-byte class/machine checks on the emitted image. For ELFCLASS32 180 * e_machine is a little-endian uint16 at offset 18; for ELFCLASS64 it is 181 * also at 18, so this offset is valid regardless. e_ident[EI_CLASS] must 182 * be ELFCLASS32 (==1) for the rv32 target. */ 183 if (out_len > EI_CLASS) { 184 CHECK(out_data[EI_CLASS] == ELFCLASS32, 185 "e_ident[EI_CLASS]=%u after emit, want ELFCLASS32=%u", 186 out_data[EI_CLASS], ELFCLASS32); 187 } 188 if (out_len >= 20) { 189 unsigned machine = 190 (unsigned)out_data[18] | ((unsigned)out_data[19] << 8); 191 CHECK(machine == EM_RISCV, "e_machine=%u after emit, want EM_RISCV=%u", 192 machine, EM_RISCV); 193 } 194 195 /* Round-trip: copy to a private buffer first — the mem writer's storage is 196 * freed on close. */ 197 uint8_t* roundtrip = (uint8_t*)malloc(out_len ? out_len : 1); 198 memcpy(roundtrip, out_data, out_len); 199 kit_writer_close(w); 200 201 /* ---- reopen ---- */ 202 KitSlice input = {.data = roundtrip, .len = out_len}; 203 KitObjFile* back = NULL; 204 CHECK(kit_obj_open(&ctx, KIT_SLICE_LIT("rv32_class32"), &input, &back) == 205 KIT_OK && 206 back, 207 "kit_obj_open failed"); 208 209 /* (b) The reader resolves the image to an rv32 target spec. */ 210 if (back) { 211 KitTargetSpec got = kit_obj_target(back); 212 CHECK(got.arch == KIT_ARCH_RV32, "reopened arch=%d, want KIT_ARCH_RV32=%d", 213 (int)got.arch, (int)KIT_ARCH_RV32); 214 CHECK(got.ptr_size == 4, "reopened ptr_size=%u, want 4", 215 (unsigned)got.ptr_size); 216 CHECK(got.obj == KIT_OBJ_ELF, "reopened obj fmt is not ELF"); 217 } 218 219 /* (c) The symbol survives: GLOBAL FUNC in .text at value 0. */ 220 if (back) { 221 KitObjSection text = KIT_SECTION_NONE; 222 CHECK(kit_obj_section_by_name(back, KIT_SLICE_LIT(".text"), &text) == 223 KIT_OK, 224 ".text not present after roundtrip"); 225 226 KitObjSymInfo si; 227 if (kit_obj_symbol_by_name(back, KIT_SLICE_LIT("rv32_sym"), &si) == 228 KIT_OK) { 229 CHECK(si.bind == KIT_SB_GLOBAL, "rv32_sym bind=%u", si.bind); 230 CHECK(si.kind == KIT_SK_FUNC, "rv32_sym kind=%u (want SK_FUNC)", 231 si.kind); 232 CHECK(si.value == 0, "rv32_sym value=%llu, want 0", 233 (unsigned long long)si.value); 234 if (text != KIT_SECTION_NONE) { 235 CHECK(si.section == text, "rv32_sym not bound to .text"); 236 } 237 } else { 238 CHECK(0, "missing 'rv32_sym' symbol after roundtrip"); 239 } 240 } 241 242 /* (d) The relocation survives: one R_ABS32 in .text at RELOC_OFFSET against 243 * rv32_sym, addend 0. */ 244 if (back) { 245 KitObjRelocIter* it = NULL; 246 KitObjReloc found; 247 int ntext = 0; 248 int have = 0; 249 KitObjSection text = KIT_SECTION_NONE; 250 (void)kit_obj_section_by_name(back, KIT_SLICE_LIT(".text"), &text); 251 252 CHECK(kit_obj_reliter_new(back, &it) == KIT_OK && it, "reliter_new"); 253 if (it) { 254 KitObjReloc r; 255 while (kit_obj_reliter_next(it, &r) == KIT_ITER_ITEM) { 256 if (r.section == text) { 257 ++ntext; 258 found = r; 259 have = 1; 260 } 261 } 262 kit_obj_reliter_free(it); 263 } 264 CHECK(ntext == 1, ".text reloc count = %d, want 1", ntext); 265 CHECK(have, "no reloc on .text after roundtrip"); 266 if (have) { 267 CHECK(found.kind.code == KIT_RELOC_ABS32, 268 ".text reloc code=%u (want ABS32=%u)", found.kind.code, 269 KIT_RELOC_ABS32); 270 CHECK(found.offset == RELOC_OFFSET, ".text reloc offset=%llu, want %u", 271 (unsigned long long)found.offset, RELOC_OFFSET); 272 CHECK(found.addend == 0, ".text reloc addend=%lld", 273 (long long)found.addend); 274 CHECK(kit_slice_eq_cstr(found.sym_name, "rv32_sym"), 275 "reloc target name = %.*s", KIT_SLICE_ARG(found.sym_name)); 276 } 277 } 278 279 if (back) kit_obj_free(back); 280 free(roundtrip); 281 kit_obj_builder_free(in); 282 kit_compiler_free(cc); 283 kit_target_free(kt); 284 285 if (g_failures) { 286 fprintf(stderr, "%d failure(s)\n", g_failures); 287 return g_failures; 288 } 289 fputs("rv32_class32: OK\n", stderr); 290 return 0; 291 }