roundtrip_unit.c (13570B)
1 /* test/debug/roundtrip_unit.c — drive the Debug producer directly and 2 * assert the resulting section bytes match a known-good encoding for one 3 * tiny case. 4 * 5 * The primary case: one CU with one subprogram named "f" at .text+0, size 4 6 * (one aarch64 instruction), one line row mapping (.text+0, line 10). 7 * A second x64 case checks that .debug_line uses byte-granular PC advances 8 * instead of the aarch64 minimum instruction length. 9 * 10 * This is a producer-side encoder check: we deliberately don't go through 11 * kit_dwarf_open so an encoding bug doesn't get masked by a matching 12 * decoder bug on the other side. Instead we hexdump the produced 13 * .debug_line and .debug_info and spot-check structural invariants 14 * (DWARF 5, address-size 8, version fields, presence of opcodes, length 15 * fields). The end-to-end producer↔consumer round trip is exercised by 16 * test/cg path W. */ 17 18 #include <kit/arch.h> 19 #include <kit/core.h> 20 #include <stdarg.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 25 #include "arch/x64/regs.h" 26 #include "core/core.h" 27 #include "core/pool.h" 28 #include "debug/debug.h" 29 #include "lib/kit_unit.h" 30 #include "obj/obj.h" 31 32 /* Shared test context replaces the per-file heap/diag/counter globals; 33 * EXPECT aliases CU_EXPECT so the call sites are unchanged. The original 34 * context used now=-1, set in main after kit_unit_init. */ 35 static KitUnit g_u; 36 #define EXPECT(cond, ...) CU_EXPECT(&g_u, cond, __VA_ARGS__) 37 38 static const Section* sec_by_name(const ObjBuilder* ob, Pool* pool, 39 const char* name) { 40 u32 i, n = obj_section_count(ob); 41 for (i = 1; i < n; ++i) { 42 const Section* s = obj_section_get(ob, i); 43 Slice sn = pool_slice(pool, s->name); 44 if (sn.s && strlen(name) == sn.len && memcmp(sn.s, name, sn.len) == 0) 45 return s; 46 } 47 return NULL; 48 } 49 50 static u32 sec_size(const Section* s) { return s ? buf_pos(&s->bytes) : 0; } 51 52 static void sec_read(const Section* s, u32 ofs, void* dst, size_t n) { 53 buf_read(&s->bytes, ofs, dst, n); 54 } 55 56 static u16 le16(const Section* s, u32 ofs) { 57 u8 b[2]; 58 sec_read(s, ofs, b, 2); 59 return (u16)(b[0] | ((u16)b[1] << 8)); 60 } 61 62 static u32 le32(const Section* s, u32 ofs) { 63 u8 b[4]; 64 sec_read(s, ofs, b, 4); 65 return (u32)(b[0] | ((u32)b[1] << 8) | ((u32)b[2] << 16) | ((u32)b[3] << 24)); 66 } 67 68 static u8 byte_at(const Section* s, u32 ofs) { 69 u8 b; 70 sec_read(s, ofs, &b, 1); 71 return b; 72 } 73 74 /* Per-arch nop encoding used by the round-trip test. Both 4 bytes; the 75 * encoders are inlined here so the test stays self-contained. */ 76 #define ARCH_NOP_AA64 0xd503201fu /* HINT #0 */ 77 #define ARCH_NOP_RV64 0x00000013u /* ADDI x0, x0, 0 */ 78 79 static int run_one(KitArchKind arch, uint32_t nop_word, const char* tag) { 80 KitTargetSpec t; 81 Compiler* c; 82 ObjBuilder* ob; 83 Debug* d; 84 ObjSecId text_sec; 85 ObjSymId fsym; 86 Pool* pool; 87 int local_fail = 0; 88 89 t = kit_unit_target(arch, KIT_OS_LINUX, KIT_OBJ_ELF); 90 91 if (kit_unit_compiler_new(&g_u, t, &c) != KIT_OK || !c) { 92 fprintf(stderr, "[%s] compiler_new failed\n", tag); 93 return 2; 94 } 95 ob = obj_new(c); 96 pool = c->global; 97 98 /* .text section + symbol "f". */ 99 text_sec = obj_section(ob, pool_intern_slice(pool, SLICE_LIT(".text")), 100 SEC_TEXT, SF_EXEC | SF_ALLOC, 4); 101 /* one 4-byte arch nop */ 102 { 103 u32 nop = nop_word; 104 obj_write(ob, text_sec, &nop, 4); 105 } 106 fsym = obj_symbol(ob, pool_intern_slice(pool, SLICE_LIT("f")), SB_GLOBAL, 107 SK_FUNC, text_sec, 0, 4); 108 109 /* Drive Debug. */ 110 d = debug_new(c, ob); 111 EXPECT(d != NULL, "[%s] debug_new returned NULL", tag); 112 if (!d) { 113 kit_compiler_free(c); 114 return 2; 115 } 116 { 117 /* Set a primary file. */ 118 u32 fid = 0; 119 (void)source_add_memory(c->sources, SLICE_LIT("p01.c"), &fid); 120 SrcLoc decl = {fid, 1, 0}; 121 SrcLoc l10 = {fid, 10, 0}; 122 DebugTypeId int_tid = debug_type_base( 123 d, pool_intern_slice(pool, SLICE_LIT("int")), DEBUG_BE_SIGNED, 4); 124 DebugTypeId fn_tid = debug_type_func(d, int_tid, NULL, 0, 0); 125 /* Pre-register the file as DWARF index 0 = primary. */ 126 (void)debug_file(d, fid); 127 128 debug_func_begin(d, fsym, fn_tid, decl); 129 debug_line(d, text_sec, 0, l10, 1); 130 debug_func_pc_range(d, text_sec, 0, 4); 131 debug_func_end(d); 132 } 133 134 debug_emit(d); 135 136 /* ---- structural assertions ---- */ 137 { 138 const Section* line = sec_by_name(ob, pool, ".debug_line"); 139 const Section* info = sec_by_name(ob, pool, ".debug_info"); 140 const Section* abbr = sec_by_name(ob, pool, ".debug_abbrev"); 141 const Section* str = sec_by_name(ob, pool, ".debug_str"); 142 const Section* lstr = sec_by_name(ob, pool, ".debug_line_str"); 143 const Section* sof = sec_by_name(ob, pool, ".debug_str_offsets"); 144 const Section* aranges = sec_by_name(ob, pool, ".debug_aranges"); 145 const Section* rng = sec_by_name(ob, pool, ".debug_rnglists"); 146 147 EXPECT(line != NULL, "[%s] .debug_line missing", tag); 148 EXPECT(info != NULL, "[%s] .debug_info missing", tag); 149 EXPECT(abbr != NULL, "[%s] .debug_abbrev missing", tag); 150 EXPECT(str != NULL, "[%s] .debug_str missing", tag); 151 EXPECT(lstr != NULL, "[%s] .debug_line_str missing", tag); 152 EXPECT(sof != NULL, "[%s] .debug_str_offsets missing", tag); 153 EXPECT(aranges != NULL, "[%s] .debug_aranges missing", tag); 154 EXPECT(rng != NULL, "[%s] .debug_rnglists missing", tag); 155 156 if (line) { 157 /* unit_length at offset 0 must equal section size - 4. */ 158 u32 ul = le32(line, 0); 159 EXPECT(ul + 4 == sec_size(line), 160 "[%s] .debug_line unit_length=%u, section size=%u", tag, ul, 161 sec_size(line)); 162 /* version */ 163 EXPECT(le16(line, 4) == 5, "[%s] .debug_line version != 5", tag); 164 /* address_size */ 165 EXPECT(byte_at(line, 6) == 8, "[%s] .debug_line address_size != 8", tag); 166 /* segment selector size */ 167 EXPECT(byte_at(line, 7) == 0, "[%s] .debug_line seg_size != 0", tag); 168 /* DWARF 5 §6.2.4: header is unit_length(4) + version(2) + 169 * address_size(1) + seg_size(1) + header_length(4) + 170 * min_inst_length(1) + ... — byte offset 12 holds 171 * min_inst_length. Both aa64 and rv64 emit 4-byte fixed-width 172 * instructions; the producer must encode the value 4 there. */ 173 EXPECT(byte_at(line, 12) == 4, 174 "[%s] .debug_line min_inst_length != 4 (got %u)", tag, 175 byte_at(line, 12)); 176 /* max_ops_per_inst at offset 13. */ 177 EXPECT(byte_at(line, 13) == 1, "[%s] .debug_line max_ops_per_inst != 1", 178 tag); 179 } 180 if (info) { 181 u32 ul = le32(info, 0); 182 EXPECT(ul + 4 == sec_size(info), 183 "[%s] .debug_info unit_length=%u, section size=%u", tag, ul, 184 sec_size(info)); 185 EXPECT(le16(info, 4) == 5, "[%s] .debug_info version != 5", tag); 186 EXPECT(byte_at(info, 6) == 1, 187 "[%s] .debug_info unit_type != DW_UT_compile", tag); 188 EXPECT(byte_at(info, 7) == 8, "[%s] .debug_info address_size != 8", tag); 189 } 190 if (str) { 191 /* Should contain "kit 0.1\0" somewhere. */ 192 u32 sz = sec_size(str); 193 u8* bytes = (u8*)malloc(sz); 194 buf_flatten(&str->bytes, bytes); 195 int found = 0; 196 u32 i; 197 for (i = 0; i + 7 <= sz; ++i) { 198 if (memcmp(bytes + i, "kit 0.1", 7) == 0) { 199 found = 1; 200 break; 201 } 202 } 203 EXPECT(found, "[%s] .debug_str missing producer", tag); 204 free(bytes); 205 } 206 if (sof) { 207 /* unit_length, version 5, padding 0, then N*4 offsets. */ 208 EXPECT(le16(sof, 4) == 5, "[%s] .debug_str_offsets version != 5", tag); 209 } 210 if (rng) { 211 EXPECT(le16(rng, 4) == 5, "[%s] .debug_rnglists version != 5", tag); 212 EXPECT(byte_at(rng, 6) == 8, "[%s] .debug_rnglists addr_size != 8", tag); 213 } 214 if (aranges) { 215 EXPECT(le16(aranges, 4) == 2, "[%s] .debug_aranges version != 2", tag); 216 } 217 218 /* Reloc inventory: there should be exactly 4 ABS64 relocs against 219 * fsym (one each in .debug_info low_pc, .debug_line set_address, 220 * .debug_aranges first tuple addr, .debug_rnglists start_length). */ 221 { 222 u32 nrel = obj_reloc_total(ob); 223 u32 abs64_against_f = 0; 224 u32 i; 225 for (i = 0; i < nrel; ++i) { 226 const Reloc* r = obj_reloc_at(ob, i); 227 if (r->kind == R_ABS64 && r->sym == fsym) abs64_against_f++; 228 } 229 EXPECT(abs64_against_f == 4, 230 "[%s] expected 4 ABS64 relocs against fsym, got %u", tag, 231 abs64_against_f); 232 } 233 } 234 235 debug_free(d); 236 obj_free(ob); 237 kit_compiler_free(c); 238 return local_fail; 239 } 240 241 /* Per-arch register-name spot checks: confirm rv64 DWARF numbers match 242 * the psABI (x1=ra=1, x2=sp=2, x8=s0/fp=8, x10=a0=10, f0=ft0=32, 243 * f8=fs0=40) and aa64 still resolves x0..x30/sp by their DWARF indices. */ 244 static void check_reg(KitArchKind arch, const char* tag, uint32_t expect_idx, 245 const char* expect_name) { 246 KitSlice nm = kit_arch_register_name(arch, expect_idx); 247 uint32_t got_idx = 0u; 248 KitStatus st = 249 kit_arch_register_index(arch, kit_slice_cstr(expect_name), &got_idx); 250 EXPECT(nm.s != NULL && kit_slice_eq_cstr(nm, expect_name), 251 "[%s] register_name(%u) expected %s, got %.*s", tag, expect_idx, 252 expect_name, KIT_SLICE_ARG(nm)); 253 EXPECT(st == KIT_OK && got_idx == expect_idx, 254 "[%s] register_index(%s) expected %u, got %u (status %d)", tag, 255 expect_name, expect_idx, got_idx, (int)st); 256 } 257 258 static void run_arch_register_checks(void) { 259 /* aa64 (sanity): x0..x30 + sp = 0..31. */ 260 check_reg(KIT_ARCH_ARM_64, "aa64", 0, "x0"); 261 check_reg(KIT_ARCH_ARM_64, "aa64", 30, "x30"); 262 check_reg(KIT_ARCH_ARM_64, "aa64", 31, "sp"); 263 264 /* rv64 psABI / DWARF: integer regs 0..31, FP regs 32..63. */ 265 check_reg(KIT_ARCH_RV64, "rv64", 0, "zero"); 266 check_reg(KIT_ARCH_RV64, "rv64", 1, "ra"); 267 check_reg(KIT_ARCH_RV64, "rv64", 2, "sp"); 268 check_reg(KIT_ARCH_RV64, "rv64", 8, "s0"); 269 check_reg(KIT_ARCH_RV64, "rv64", 10, "a0"); 270 check_reg(KIT_ARCH_RV64, "rv64", 31, "t6"); 271 check_reg(KIT_ARCH_RV64, "rv64", 32, "ft0"); 272 check_reg(KIT_ARCH_RV64, "rv64", 40, "fs0"); 273 check_reg(KIT_ARCH_RV64, "rv64", 63, "ft11"); 274 275 /* "fp" alias for s0/x8 on rv64. */ 276 { 277 uint32_t idx = 0; 278 KitStatus st = 279 kit_arch_register_index(KIT_ARCH_RV64, KIT_SLICE_LIT("fp"), &idx); 280 EXPECT(st == KIT_OK && idx == 8, 281 "[rv64] register_index(fp) expected 8, got %u (status %d)", idx, 282 (int)st); 283 } 284 285 /* x64 hardware-GPR-index -> SysV DWARF number. This is the map the x64 286 * backend applies before emitting CFI register operands; rcx/rdx/rsi/rdi/ 287 * rsp/rbp diverge between the two namespaces, while rax/rbx/r8..r15 are 288 * identity. (Guards the table behind x64_dwarf_from_hw_gpr; the rbp=HW5 case 289 * is exactly the one that misencoded .eh_frame as RDI before the map was 290 * applied.) */ 291 { 292 static const uint32_t expect[16] = {0, 2, 1, 3, 7, 6, 4, 5, 293 8, 9, 10, 11, 12, 13, 14, 15}; 294 uint32_t hw; 295 for (hw = 0; hw < 16u; ++hw) { 296 uint32_t got = x64_dwarf_from_hw_gpr(hw); 297 EXPECT(got == expect[hw], 298 "[x64] dwarf_from_hw_gpr(%u) expected %u got %u", hw, expect[hw], 299 got); 300 } 301 /* rip (16) and any non-GPR index passes through unchanged. */ 302 EXPECT(x64_dwarf_from_hw_gpr(16u) == 16u, "[x64] rip passthrough"); 303 } 304 } 305 306 static int run_x64_debug_line_check(void) { 307 KitTargetSpec xt; 308 Compiler* xc; 309 ObjBuilder* xob; 310 Debug* xd; 311 ObjSecId xtext_sec; 312 ObjSymId xfsym; 313 Pool* xpool; 314 315 xt = kit_unit_target(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF); 316 317 if (kit_unit_compiler_new(&g_u, xt, &xc) != KIT_OK || !xc) { 318 fprintf(stderr, "x64 compiler_new failed\n"); 319 return 2; 320 } 321 xob = obj_new(xc); 322 xpool = xc->global; 323 324 xtext_sec = obj_section(xob, pool_intern_slice(xpool, SLICE_LIT(".text")), 325 SEC_TEXT, SF_EXEC | SF_ALLOC, 1); 326 { 327 u8 code[2] = {0x90, 0xc3}; /* nop; ret */ 328 obj_write(xob, xtext_sec, code, sizeof(code)); 329 } 330 xfsym = obj_symbol(xob, pool_intern_slice(xpool, SLICE_LIT("xf")), SB_GLOBAL, 331 SK_FUNC, xtext_sec, 0, 2); 332 333 xd = debug_new(xc, xob); 334 EXPECT(xd != NULL, "x64 debug_new returned NULL"); 335 if (xd) { 336 u32 fid = 0; 337 (void)source_add_memory(xc->sources, SLICE_LIT("x64.c"), &fid); 338 SrcLoc decl = {fid, 1, 0}; 339 SrcLoc l10 = {fid, 10, 0}; 340 SrcLoc l11 = {fid, 11, 0}; 341 DebugTypeId int_tid = debug_type_base( 342 xd, pool_intern_slice(xpool, SLICE_LIT("int")), DEBUG_BE_SIGNED, 4); 343 DebugTypeId fn_tid = debug_type_func(xd, int_tid, NULL, 0, 0); 344 (void)debug_file(xd, fid); 345 346 debug_func_begin(xd, xfsym, fn_tid, decl); 347 debug_line(xd, xtext_sec, 0, l10, 1); 348 debug_line(xd, xtext_sec, 1, l11, 1); 349 debug_func_pc_range(xd, xtext_sec, 0, 2); 350 debug_func_end(xd); 351 debug_emit(xd); 352 353 { 354 const Section* xline = sec_by_name(xob, xpool, ".debug_line"); 355 EXPECT(xline != NULL, "x64 .debug_line missing"); 356 if (xline) { 357 EXPECT(byte_at(xline, 12) == 1, "x64 .debug_line min_inst_length != 1"); 358 } 359 } 360 361 debug_free(xd); 362 } 363 obj_free(xob); 364 kit_compiler_free(xc); 365 return 0; 366 } 367 368 int main(void) { 369 int rc = 0; 370 371 kit_unit_init(&g_u); 372 g_u.ctx.now = -1; 373 374 rc |= run_one(KIT_ARCH_ARM_64, ARCH_NOP_AA64, "aa64"); 375 rc |= run_one(KIT_ARCH_RV64, ARCH_NOP_RV64, "rv64"); 376 rc |= run_x64_debug_line_check(); 377 run_arch_register_checks(); 378 379 if (g_u.fails || rc) { 380 fprintf(stderr, "%d FAILED\n", g_u.fails); 381 return 1; 382 } 383 printf("debug roundtrip_unit: OK\n"); 384 return 0; 385 }