link_script_test.c (38459B)
1 /* Public linker-script parser coverage for the kernel-script subset. */ 2 3 #include <kit/core.h> 4 #include <kit/link.h> 5 #include <kit/object.h> 6 #include <string.h> 7 8 #include "lib/kit_unit.h" 9 10 static KitUnit g_u; 11 #define EXPECT(c, ...) CU_EXPECT(&g_u, c, __VA_ARGS__) 12 13 static void check_kernel_script_subset(void) { 14 static const char script_text[] = 15 "OUTPUT_FORMAT(\"elf64-littleaarch64\")\n" 16 "OUTPUT_ARCH(aarch64)\n" 17 "ENTRY(_start)\n" 18 "EXTERN(retained)\n" 19 "MEMORY {\n" 20 " ROM (rx) : ORIGIN = 0x10000, LENGTH = 64K\n" 21 " RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 128K\n" 22 "}\n" 23 "PHDRS {\n" 24 " text PT_LOAD FILEHDR PHDRS FLAGS(5);\n" 25 " data PT_LOAD FLAGS(6);\n" 26 "}\n" 27 "PROVIDE_HIDDEN(__image_base = ORIGIN(ROM));\n" 28 "SECTIONS {\n" 29 " . = ORIGIN(ROM) + SIZEOF_HEADERS;\n" 30 " .text : ALIGN(16) AT(ORIGIN(ROM)) {\n" 31 " KEEP(*(.text.start))\n" 32 " *(.text .text.*)\n" 33 " PROVIDE(__etext = .);\n" 34 " } > ROM :text =0x90\n" 35 " .data ORIGIN(RAM) : {\n" 36 " *(.rodata .rodata.*)\n" 37 " *(EXCLUDE_FILE (*crtend.o) .data .data.*)\n" 38 " } > RAM AT> ROM :data\n" 39 " .bss (NOLOAD) : {\n" 40 " __bss_start = .;\n" 41 " *(.bss .bss.* COMMON)\n" 42 " . = ALIGN(., 16);\n" 43 " } > RAM\n" 44 " ASSERT(LENGTH(ROM) - SIZEOF(.text), \"text too large\");\n" 45 "}\n"; 46 47 KitLinkScript* s = NULL; 48 KitSlice text = {.s = script_text, .len = sizeof(script_text) - 1u}; 49 EXPECT(kit_link_script_parse(&g_u.ctx, text, &s) == KIT_OK, 50 "kernel linker script parses"); 51 EXPECT(s != NULL, "parser returns script"); 52 if (!s) return; 53 54 EXPECT(kit_slice_eq_cstr(s->entry, "_start"), "ENTRY recorded"); 55 EXPECT(kit_slice_eq_cstr(s->output_format, "elf64-littleaarch64"), 56 "OUTPUT_FORMAT recorded"); 57 EXPECT(kit_slice_eq_cstr(s->output_arch, "aarch64"), 58 "OUTPUT_ARCH recorded"); 59 EXPECT(s->nexterns == 1 && 60 kit_slice_eq_cstr(s->externs[0], "retained"), 61 "EXTERN recorded"); 62 63 EXPECT(s->nregions == 2, "MEMORY region count"); 64 EXPECT(kit_slice_eq_cstr(s->regions[0].name, "ROM"), "ROM region name"); 65 EXPECT(s->regions[0].origin == 0x10000u, "ROM origin"); 66 EXPECT(s->regions[0].length == 64u * 1024u, "ROM length suffix"); 67 EXPECT((s->regions[0].flags & (KIT_LRF_R | KIT_LRF_X)) == 68 (KIT_LRF_R | KIT_LRF_X), 69 "ROM flags"); 70 EXPECT(kit_slice_eq_cstr(s->regions[1].name, "RAM"), "RAM region name"); 71 72 EXPECT(s->nphdrs == 2, "PHDRS count"); 73 EXPECT(kit_slice_eq_cstr(s->phdrs[0].name, "text"), "text PHDR name"); 74 EXPECT(s->phdrs[0].filehdr && s->phdrs[0].phdrs, 75 "text PHDR header attrs"); 76 EXPECT(s->phdrs[0].flags && 77 s->phdrs[0].flags->kind == KIT_LE_INT && 78 s->phdrs[0].flags->v.int_val == 5, 79 "text PHDR FLAGS"); 80 81 EXPECT(s->ntop_asns == 2, "top-level dot and PROVIDE_HIDDEN assignments"); 82 const KitLinkAssignment* provide = NULL; 83 for (uint32_t i = 0; i < s->ntop_asns; ++i) 84 if (s->top_asns[i].kind == KIT_LAS_PROVIDE_HIDDEN) 85 provide = &s->top_asns[i]; 86 EXPECT(provide != NULL, "PROVIDE_HIDDEN assignment kind"); 87 EXPECT(provide && provide->expr && 88 provide->expr->kind == KIT_LE_REGION_ORIGIN, 89 "PROVIDE_HIDDEN ORIGIN expression"); 90 91 EXPECT(s->nsections == 3, "output section count"); 92 const KitLinkOutputSection* text_sec = &s->sections[0]; 93 const KitLinkOutputSection* data_sec = &s->sections[1]; 94 const KitLinkOutputSection* bss_sec = &s->sections[2]; 95 EXPECT(kit_slice_eq_cstr(text_sec->name, ".text"), ".text section"); 96 EXPECT(kit_slice_eq_cstr(text_sec->region, "ROM"), ".text region"); 97 EXPECT(text_sec->lma && text_sec->lma->kind == KIT_LE_REGION_ORIGIN, 98 ".text AT(ORIGIN)"); 99 EXPECT(text_sec->nphdrs == 1 && 100 kit_slice_eq_cstr(text_sec->phdrs[0], "text"), 101 ".text PHDR reference"); 102 EXPECT(text_sec->fill && text_sec->fill->kind == KIT_LE_INT && 103 text_sec->fill->v.int_val == 0x90, 104 ".text fill"); 105 EXPECT(text_sec->ninputs >= 1 && text_sec->inputs[0].keep, 106 "KEEP input matcher"); 107 108 EXPECT(data_sec->vma && data_sec->vma->kind == KIT_LE_REGION_ORIGIN, 109 ".data VMA expression"); 110 EXPECT(kit_slice_eq_cstr(data_sec->region, "RAM"), ".data VMA region"); 111 EXPECT(kit_slice_eq_cstr(data_sec->load_region, "ROM"), 112 ".data load region"); 113 EXPECT(data_sec->nphdrs == 1 && 114 kit_slice_eq_cstr(data_sec->phdrs[0], "data"), 115 ".data PHDR reference"); 116 EXPECT(data_sec->ninputs >= 4 && 117 data_sec->inputs[2].nexclude_file_patterns == 1 && 118 kit_slice_eq_cstr(data_sec->inputs[2].exclude_file_patterns[0], 119 "*crtend.o"), 120 "EXCLUDE_FILE matcher"); 121 122 EXPECT(bss_sec->noload, ".bss NOLOAD"); 123 EXPECT(kit_slice_eq_cstr(bss_sec->region, "RAM"), ".bss region"); 124 EXPECT(bss_sec->nasns == 2, ".bss assignments"); 125 EXPECT(s->nasserts == 1 && 126 kit_slice_eq_cstr(s->asserts[0].message, "text too large"), 127 "ASSERT recorded"); 128 129 kit_link_script_free(&g_u.ctx, s); 130 } 131 132 static KitObjBuilder* build_layout_input(KitCompiler* c) { 133 static const uint8_t text_bytes[4] = {0xc0, 0x03, 0x5f, 0xd6}; 134 static const uint8_t data_bytes[4] = {1, 2, 3, 4}; 135 KitObjBuilder* ob = NULL; 136 KitObjSection text = KIT_SECTION_NONE; 137 KitObjSection data = KIT_SECTION_NONE; 138 KitObjSymbol sym = KIT_OBJ_SYMBOL_NONE; 139 KitObjSectionDesc text_desc; 140 KitObjSectionDesc data_desc; 141 KitObjSymbolDesc sym_desc; 142 143 if (kit_obj_builder_new(c, &ob) != KIT_OK || !ob) { 144 EXPECT(0, "obj builder allocation"); 145 return NULL; 146 } 147 148 memset(&text_desc, 0, sizeof(text_desc)); 149 text_desc.name = kit_sym_intern(c, KIT_SLICE_LIT(".text")); 150 text_desc.kind = KIT_SEC_TEXT; 151 text_desc.flags = KIT_SF_ALLOC | KIT_SF_EXEC; 152 text_desc.align = 4; 153 if (kit_obj_builder_section(ob, &text_desc, &text) != KIT_OK) { 154 EXPECT(0, "create .text"); 155 goto fail; 156 } 157 if (kit_obj_builder_write(ob, text, text_bytes, sizeof(text_bytes)) != 158 KIT_OK) { 159 EXPECT(0, "write .text"); 160 goto fail; 161 } 162 163 memset(&data_desc, 0, sizeof(data_desc)); 164 data_desc.name = kit_sym_intern(c, KIT_SLICE_LIT(".data")); 165 data_desc.kind = KIT_SEC_DATA; 166 data_desc.flags = KIT_SF_ALLOC | KIT_SF_WRITE; 167 data_desc.align = 4; 168 if (kit_obj_builder_section(ob, &data_desc, &data) != KIT_OK) { 169 EXPECT(0, "create .data"); 170 goto fail; 171 } 172 if (kit_obj_builder_write(ob, data, data_bytes, sizeof(data_bytes)) != 173 KIT_OK) { 174 EXPECT(0, "write .data"); 175 goto fail; 176 } 177 178 memset(&sym_desc, 0, sizeof(sym_desc)); 179 sym_desc.name = kit_sym_intern(c, KIT_SLICE_LIT("_start")); 180 sym_desc.bind = KIT_SB_GLOBAL; 181 sym_desc.kind = KIT_SK_FUNC; 182 sym_desc.section = text; 183 sym_desc.value = 0; 184 sym_desc.size = sizeof(text_bytes); 185 if (kit_obj_builder_symbol(ob, &sym_desc, &sym) != KIT_OK) { 186 EXPECT(0, "define _start"); 187 goto fail; 188 } 189 190 sym = KIT_OBJ_SYMBOL_NONE; 191 memset(&sym_desc, 0, sizeof(sym_desc)); 192 sym_desc.name = kit_sym_intern(c, KIT_SLICE_LIT("global_data")); 193 sym_desc.bind = KIT_SB_GLOBAL; 194 sym_desc.kind = KIT_SK_OBJ; 195 sym_desc.section = data; 196 sym_desc.value = 0; 197 sym_desc.size = sizeof(data_bytes); 198 if (kit_obj_builder_symbol(ob, &sym_desc, &sym) != KIT_OK) { 199 EXPECT(0, "define global_data"); 200 goto fail; 201 } 202 203 if (kit_obj_builder_finalize(ob) != KIT_OK) { 204 EXPECT(0, "finalize input object"); 205 goto fail; 206 } 207 return ob; 208 209 fail: 210 kit_obj_builder_free(ob); 211 return NULL; 212 } 213 214 /* Parse `script_text`, link `build_layout_input()` against it, and open the 215 * emitted image. On success *out_f / *out_w / *out_c / *out_s / *out_ob own 216 * resources the caller must release (use emit_cleanup). Returns 1 on success, 217 * 0 if any stage failed (an EXPECT already fired). */ 218 typedef struct EmitFixture { 219 KitCompiler* c; 220 KitObjBuilder* ob; 221 KitLinkScript* s; 222 KitLinkSession* sess; 223 KitWriter* w; 224 KitObjFile* f; 225 } EmitFixture; 226 227 static void emit_cleanup(EmitFixture* fx) { 228 if (fx->f) kit_obj_free(fx->f); 229 if (fx->w) kit_writer_close(fx->w); 230 if (fx->sess) kit_link_session_free(fx->sess); 231 if (fx->ob) kit_obj_builder_free(fx->ob); 232 if (fx->s) kit_link_script_free(&g_u.ctx, fx->s); 233 if (fx->c) kit_compiler_free(fx->c); 234 memset(fx, 0, sizeof(*fx)); 235 } 236 237 static int emit_layout(const char* script_text, size_t script_len, 238 EmitFixture* fx) { 239 KitTargetSpec target = 240 kit_unit_target(KIT_ARCH_ARM_64, KIT_OS_LINUX, KIT_OBJ_ELF); 241 KitLinkSessionOptions opts; 242 KitSlice script; 243 KitSlice bytes; 244 size_t len = 0; 245 246 memset(fx, 0, sizeof(*fx)); 247 script.s = script_text; 248 script.len = script_len; 249 EXPECT(kit_link_script_parse(&g_u.ctx, script, &fx->s) == KIT_OK && fx->s, 250 "layout script parses"); 251 if (!fx->s) return 0; 252 EXPECT(kit_unit_compiler_new(&g_u, target, &fx->c) == KIT_OK && fx->c, 253 "compiler allocation"); 254 if (!fx->c) return 0; 255 fx->ob = build_layout_input(fx->c); 256 if (!fx->ob) return 0; 257 memset(&opts, 0, sizeof(opts)); 258 opts.output_kind = KIT_LINK_OUTPUT_EXE; 259 opts.linker_script = fx->s; 260 EXPECT(kit_link_session_new(fx->c, &opts, &fx->sess) == KIT_OK && fx->sess, 261 "link session allocation"); 262 if (!fx->sess) return 0; 263 EXPECT(kit_link_session_add_obj(fx->sess, fx->ob) == KIT_OK, 264 "add input object"); 265 EXPECT(kit_writer_mem(&g_u.heap, &fx->w) == KIT_OK && fx->w, "memory writer"); 266 if (!fx->w) return 0; 267 EXPECT(kit_link_session_emit(fx->sess, fx->w) == KIT_OK, 268 "emit scripted image"); 269 bytes.data = kit_writer_mem_bytes(fx->w, &len); 270 bytes.len = len; 271 EXPECT(bytes.data && bytes.len > 0, "scripted image bytes"); 272 if (!bytes.data || bytes.len == 0) return 0; 273 EXPECT(kit_obj_open(&g_u.ctx, KIT_SLICE_LIT("<scripted-layout>"), &bytes, 274 &fx->f) == KIT_OK && 275 fx->f, 276 "open scripted image"); 277 return fx->f != NULL; 278 } 279 280 static void check_scripted_layout_vma_lma(void) { 281 static const char script_text[] = 282 "OUTPUT_FORMAT(\"elf64-littleaarch64\")\n" 283 "OUTPUT_ARCH(aarch64)\n" 284 "ENTRY(_start)\n" 285 "MEMORY {\n" 286 " ROM (rx) : ORIGIN = 0x10000, LENGTH = 64K\n" 287 " RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 64K\n" 288 "}\n" 289 "PHDRS {\n" 290 " text PT_LOAD FLAGS(5);\n" 291 " data PT_LOAD FLAGS(6);\n" 292 "}\n" 293 "SECTIONS {\n" 294 " .text : { KEEP(*(.text .text.*)) } > ROM :text\n" 295 " .data ORIGIN(RAM) : { *(.data .data.*) } > RAM AT> ROM :data\n" 296 "}\n"; 297 KitTargetSpec target = 298 kit_unit_target(KIT_ARCH_ARM_64, KIT_OS_LINUX, KIT_OBJ_ELF); 299 KitCompiler* c = NULL; 300 KitObjBuilder* ob = NULL; 301 KitLinkScript* s = NULL; 302 KitLinkSession* sess = NULL; 303 KitWriter* w = NULL; 304 KitObjFile* f = NULL; 305 KitObjSegIter* it = NULL; 306 KitSlice script; 307 KitSlice bytes; 308 KitLinkSessionOptions opts; 309 size_t len = 0; 310 uint32_t nload = 0; 311 int saw_text = 0; 312 int saw_data = 0; 313 314 script.s = script_text; 315 script.len = sizeof(script_text) - 1u; 316 EXPECT(kit_link_script_parse(&g_u.ctx, script, &s) == KIT_OK && s, 317 "layout script parses"); 318 if (!s) goto done; 319 320 EXPECT(kit_unit_compiler_new(&g_u, target, &c) == KIT_OK && c, 321 "compiler allocation"); 322 if (!c) goto done; 323 324 ob = build_layout_input(c); 325 if (!ob) goto done; 326 327 memset(&opts, 0, sizeof(opts)); 328 opts.output_kind = KIT_LINK_OUTPUT_EXE; 329 opts.linker_script = s; 330 EXPECT(kit_link_session_new(c, &opts, &sess) == KIT_OK && sess, 331 "link session allocation"); 332 if (!sess) goto done; 333 EXPECT(kit_link_session_add_obj(sess, ob) == KIT_OK, "add input object"); 334 EXPECT(kit_writer_mem(&g_u.heap, &w) == KIT_OK && w, "memory writer"); 335 if (!w) goto done; 336 EXPECT(kit_link_session_emit(sess, w) == KIT_OK, "emit scripted image"); 337 bytes.data = kit_writer_mem_bytes(w, &len); 338 bytes.len = len; 339 EXPECT(bytes.data && bytes.len > 0, "scripted image bytes"); 340 if (!bytes.data || bytes.len == 0) goto done; 341 EXPECT(kit_obj_open(&g_u.ctx, KIT_SLICE_LIT("<scripted-layout>"), &bytes, 342 &f) == KIT_OK && 343 f, 344 "open scripted image"); 345 if (!f) goto done; 346 EXPECT(kit_obj_kind(f) == KIT_OBJ_KIND_EXEC, "scripted image is executable"); 347 EXPECT(kit_obj_segiter_new(f, &it) == KIT_OK && it, "segment iterator"); 348 if (!it) goto done; 349 for (;;) { 350 KitObjSegInfo seg; 351 KitIterResult r = kit_obj_segiter_next(it, &seg); 352 if (r == KIT_ITER_END) break; 353 EXPECT(r == KIT_ITER_ITEM, "segment iteration"); 354 if (r != KIT_ITER_ITEM) break; 355 if (!kit_slice_eq_cstr(seg.name, "LOAD")) continue; 356 ++nload; 357 if (seg.vaddr == 0x10000u) { 358 saw_text = 1; 359 EXPECT(seg.paddr == 0x10000u, ".text LMA follows ROM origin"); 360 EXPECT(seg.file_size == 4u && seg.vsize == 4u, ".text segment size"); 361 EXPECT(seg.perms == (KIT_SEG_R | KIT_SEG_X), ".text PHDR flags"); 362 } else if (seg.vaddr == 0x80000000u) { 363 saw_data = 1; 364 EXPECT(seg.paddr == 0x10004u, ".data AT> ROM follows .text bytes"); 365 EXPECT(seg.file_size == 4u && seg.vsize == 4u, ".data segment size"); 366 EXPECT(seg.perms == (KIT_SEG_R | KIT_SEG_W), ".data PHDR flags"); 367 } 368 } 369 EXPECT(nload == 2, "scripted image LOAD segment count"); 370 EXPECT(saw_text, "scripted image text LOAD"); 371 EXPECT(saw_data, "scripted image data LOAD"); 372 373 done: 374 if (it) kit_obj_segiter_free(it); 375 if (f) kit_obj_free(f); 376 if (w) kit_writer_close(w); 377 if (sess) kit_link_session_free(sess); 378 if (ob) kit_obj_builder_free(ob); 379 if (s) kit_link_script_free(&g_u.ctx, s); 380 if (c) kit_compiler_free(c); 381 } 382 383 /* Item 1: a PROGBITS-content output section marked (NOLOAD) must contribute 384 * zero file bytes and present a NOBITS-style segment (file_size 0, vsize > 0). */ 385 static void check_noload_progbits(void) { 386 static const char script_text[] = 387 "OUTPUT_FORMAT(\"elf64-littleaarch64\")\n" 388 "OUTPUT_ARCH(aarch64)\n" 389 "ENTRY(_start)\n" 390 "SECTIONS {\n" 391 " . = 0x10000;\n" 392 " .text : { *(.text .text.*) }\n" 393 " .shadow (NOLOAD) : { *(.data .data.*) }\n" 394 "}\n"; 395 EmitFixture fx; 396 KitObjSegIter* it = NULL; 397 int saw_shadow = 0; 398 uint64_t text_end = 0; 399 400 if (!emit_layout(script_text, sizeof(script_text) - 1u, &fx)) goto done; 401 EXPECT(kit_obj_segiter_new(fx.f, &it) == KIT_OK && it, "segment iterator"); 402 if (!it) goto done; 403 for (;;) { 404 KitObjSegInfo seg; 405 KitIterResult r = kit_obj_segiter_next(it, &seg); 406 if (r == KIT_ITER_END) break; 407 if (r != KIT_ITER_ITEM) break; 408 if (!kit_slice_eq_cstr(seg.name, "LOAD")) continue; 409 if (seg.file_size == 4u && seg.perms == (KIT_SEG_R | KIT_SEG_X)) 410 text_end = seg.vaddr + seg.vsize; 411 /* the shadow segment carries the .data bytes (4) as pure bss */ 412 if (seg.vsize == 4u && seg.file_size == 0u && 413 (seg.perms & KIT_SEG_X) == 0) { 414 saw_shadow = 1; 415 EXPECT(seg.file_size == 0u, "NOLOAD section contributes no file bytes"); 416 EXPECT(seg.vsize == 4u, "NOLOAD section keeps memory size"); 417 } 418 } 419 EXPECT(text_end != 0u, "saw .text LOAD before shadow"); 420 EXPECT(saw_shadow, "saw NOLOAD shadow segment with zero file size"); 421 422 done: 423 if (it) kit_obj_segiter_free(it); 424 emit_cleanup(&fx); 425 } 426 427 /* Item 2: an inter-section `. = ALIGN(0x1000)` written between two output 428 * sections must advance the location counter at its textual position so the 429 * following section lands at the aligned address. */ 430 static void check_intersection_dot_align(void) { 431 static const char script_text[] = 432 "OUTPUT_FORMAT(\"elf64-littleaarch64\")\n" 433 "OUTPUT_ARCH(aarch64)\n" 434 "ENTRY(_start)\n" 435 "SECTIONS {\n" 436 " . = 0x10000;\n" 437 " .text : { *(.text .text.*) }\n" 438 " . = ALIGN(0x1000);\n" 439 " .data : { *(.data .data.*) }\n" 440 "}\n"; 441 EmitFixture fx; 442 KitObjSegIter* it = NULL; 443 int saw_text = 0; 444 int saw_data = 0; 445 446 if (!emit_layout(script_text, sizeof(script_text) - 1u, &fx)) goto done; 447 EXPECT(kit_obj_segiter_new(fx.f, &it) == KIT_OK && it, "segment iterator"); 448 if (!it) goto done; 449 for (;;) { 450 KitObjSegInfo seg; 451 KitIterResult r = kit_obj_segiter_next(it, &seg); 452 if (r == KIT_ITER_END) break; 453 if (r != KIT_ITER_ITEM) break; 454 if (!kit_slice_eq_cstr(seg.name, "LOAD")) continue; 455 if (seg.vaddr == 0x10000u) saw_text = 1; 456 if (seg.perms & KIT_SEG_W) { 457 saw_data = 1; 458 EXPECT(seg.vaddr == 0x11000u, 459 ".data lands at . = ALIGN(0x1000) after .text"); 460 } 461 } 462 EXPECT(saw_text, "saw .text at 0x10000"); 463 EXPECT(saw_data, "saw .data aligned to 0x1000"); 464 465 done: 466 if (it) kit_obj_segiter_free(it); 467 emit_cleanup(&fx); 468 } 469 470 /* Item 3: a multi-byte fill value must be laid down as a repeating big-endian 471 * pattern across an in-section gap rather than truncated to its low byte. */ 472 static void check_multibyte_fill(void) { 473 /* SUBALIGN(16) forces each input to a 16-byte boundary: .text (4 bytes) at 474 * offset 0, .data (4 bytes) at offset 16, leaving a 12-byte gap [4..16). 475 * That gap must carry the FILL pattern (0x12345678, big-endian, repeated) 476 * rather than just its low byte (0x78). */ 477 static const char script_text[] = 478 "OUTPUT_FORMAT(\"elf64-littleaarch64\")\n" 479 "OUTPUT_ARCH(aarch64)\n" 480 "ENTRY(_start)\n" 481 "SECTIONS {\n" 482 " . = 0x10000;\n" 483 " .text : SUBALIGN(16) {\n" 484 " *(.text .text.*)\n" 485 " *(.data .data.*)\n" 486 " FILL(0x12345678)\n" 487 " }\n" 488 "}\n"; 489 EmitFixture fx; 490 KitObjSegIter* it = NULL; 491 KitSlice img; 492 size_t len = 0; 493 uint64_t seg_off = 0; 494 int found = 0; 495 496 if (!emit_layout(script_text, sizeof(script_text) - 1u, &fx)) goto done; 497 EXPECT(kit_obj_segiter_new(fx.f, &it) == KIT_OK && it, "segment iterator"); 498 if (!it) goto done; 499 for (;;) { 500 KitObjSegInfo seg; 501 KitIterResult r = kit_obj_segiter_next(it, &seg); 502 if (r == KIT_ITER_END) break; 503 if (r != KIT_ITER_ITEM) break; 504 if (!kit_slice_eq_cstr(seg.name, "LOAD")) continue; 505 if (seg.vaddr == 0x10000u) { 506 seg_off = seg.file_off; 507 found = 1; 508 EXPECT(seg.file_size == 20u, ".text segment spans text+gap+data"); 509 } 510 } 511 EXPECT(found, "found .text segment"); 512 if (!found) goto done; 513 img.data = kit_writer_mem_bytes(fx.w, &len); 514 img.len = len; 515 EXPECT(img.data && (size_t)(seg_off + 20) <= len, "segment bytes in range"); 516 if (img.data && (size_t)(seg_off + 20) <= len) { 517 const uint8_t* p = (const uint8_t*)img.data + seg_off; 518 /* bytes [4..16) are the 12-byte gap; the pattern is phase-aligned to the 519 * start of the segment buffer, so offset 4 sees pat[4 % 4] == pat[0]: 520 * 12 34 56 78 repeated. */ 521 static const uint8_t want[12] = {0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 522 0x56, 0x78, 0x12, 0x34, 0x56, 0x78}; 523 EXPECT(memcmp(p + 4, want, 12) == 0, 524 "gap filled with repeated big-endian FILL pattern"); 525 } 526 527 done: 528 if (it) kit_obj_segiter_free(it); 529 emit_cleanup(&fx); 530 } 531 532 /* Item 4: (a) two output sections naming the same :phdr coalesce into a single 533 * PT_LOAD; (b) a section listing several phdrs appears under each named 534 * segment. */ 535 static void check_phdr_coalesce_and_multi(void) { 536 static const char script_text[] = 537 "OUTPUT_FORMAT(\"elf64-littleaarch64\")\n" 538 "OUTPUT_ARCH(aarch64)\n" 539 "ENTRY(_start)\n" 540 "PHDRS {\n" 541 " text PT_LOAD FLAGS(5);\n" 542 " data PT_LOAD FLAGS(6);\n" 543 "}\n" 544 "SECTIONS {\n" 545 " . = 0x10000;\n" 546 " .text : { *(.text .text.*) } :text\n" 547 " .also : { *(.data .data.*) } :text :data\n" 548 "}\n"; 549 EmitFixture fx; 550 KitObjSegIter* it = NULL; 551 uint32_t nload = 0; 552 uint32_t n_rx = 0; /* PT_LOAD with R|X (the coalesced text phdr) */ 553 uint32_t n_rw = 0; /* PT_LOAD with R|W (the data phdr) */ 554 555 if (!emit_layout(script_text, sizeof(script_text) - 1u, &fx)) goto done; 556 EXPECT(kit_obj_segiter_new(fx.f, &it) == KIT_OK && it, "segment iterator"); 557 if (!it) goto done; 558 for (;;) { 559 KitObjSegInfo seg; 560 KitIterResult r = kit_obj_segiter_next(it, &seg); 561 if (r == KIT_ITER_END) break; 562 if (r != KIT_ITER_ITEM) break; 563 if (!kit_slice_eq_cstr(seg.name, "LOAD")) continue; 564 ++nload; 565 if (seg.perms == (KIT_SEG_R | KIT_SEG_X)) ++n_rx; 566 if (seg.perms == (KIT_SEG_R | KIT_SEG_W)) ++n_rw; 567 } 568 /* .text and .also both name :text -> one R|X PT_LOAD; .also also names 569 * :data -> a second PT_LOAD with the data flags. So exactly 2 PT_LOAD. */ 570 EXPECT(nload == 2, "two sections sharing :text coalesce; :data adds one"); 571 EXPECT(n_rx == 1, "single coalesced R|X text segment"); 572 EXPECT(n_rw == 1, ".also also appears under the :data segment"); 573 574 done: 575 if (it) kit_obj_segiter_free(it); 576 emit_cleanup(&fx); 577 } 578 579 /* Build an input object whose .data output-bound content is followed by a 580 * NOBITS .bss tail, so that an output section collecting both gets a PROGBITS 581 * head + NOBITS tail (mem_size > file_size). Defines _start (.text), 582 * data_sym (.data), and bss_sym (.bss) so a linked image exposes their vaddrs. */ 583 static KitObjBuilder* build_bss_tail_input(KitCompiler* c) { 584 static const uint8_t text_bytes[4] = {0xc0, 0x03, 0x5f, 0xd6}; 585 static const uint8_t data_bytes[4] = {1, 2, 3, 4}; 586 KitObjBuilder* ob = NULL; 587 KitObjSection text = KIT_SECTION_NONE; 588 KitObjSection data = KIT_SECTION_NONE; 589 KitObjSection bss = KIT_SECTION_NONE; 590 KitObjSymbol sym = KIT_OBJ_SYMBOL_NONE; 591 KitObjSectionDesc desc; 592 KitObjSymbolDesc sym_desc; 593 594 if (kit_obj_builder_new(c, &ob) != KIT_OK || !ob) { 595 EXPECT(0, "obj builder allocation"); 596 return NULL; 597 } 598 599 memset(&desc, 0, sizeof(desc)); 600 desc.name = kit_sym_intern(c, KIT_SLICE_LIT(".text")); 601 desc.kind = KIT_SEC_TEXT; 602 desc.flags = KIT_SF_ALLOC | KIT_SF_EXEC; 603 desc.align = 4; 604 if (kit_obj_builder_section(ob, &desc, &text) != KIT_OK || 605 kit_obj_builder_write(ob, text, text_bytes, sizeof(text_bytes)) != 606 KIT_OK) { 607 EXPECT(0, "create+write .text"); 608 goto fail; 609 } 610 611 memset(&desc, 0, sizeof(desc)); 612 desc.name = kit_sym_intern(c, KIT_SLICE_LIT(".data")); 613 desc.kind = KIT_SEC_DATA; 614 desc.flags = KIT_SF_ALLOC | KIT_SF_WRITE; 615 desc.align = 4; 616 if (kit_obj_builder_section(ob, &desc, &data) != KIT_OK || 617 kit_obj_builder_write(ob, data, data_bytes, sizeof(data_bytes)) != 618 KIT_OK) { 619 EXPECT(0, "create+write .data"); 620 goto fail; 621 } 622 623 memset(&desc, 0, sizeof(desc)); 624 desc.name = kit_sym_intern(c, KIT_SLICE_LIT(".bss")); 625 desc.kind = KIT_SEC_BSS; 626 desc.flags = KIT_SF_ALLOC | KIT_SF_WRITE; 627 desc.align = 8; 628 if (kit_obj_builder_section(ob, &desc, &bss) != KIT_OK || 629 kit_obj_builder_reserve_bss(ob, bss, 0x40u, 8u) != KIT_OK) { 630 EXPECT(0, "create+reserve .bss"); 631 goto fail; 632 } 633 634 memset(&sym_desc, 0, sizeof(sym_desc)); 635 sym_desc.name = kit_sym_intern(c, KIT_SLICE_LIT("_start")); 636 sym_desc.bind = KIT_SB_GLOBAL; 637 sym_desc.kind = KIT_SK_FUNC; 638 sym_desc.section = text; 639 sym_desc.size = sizeof(text_bytes); 640 if (kit_obj_builder_symbol(ob, &sym_desc, &sym) != KIT_OK) { 641 EXPECT(0, "define _start"); 642 goto fail; 643 } 644 645 sym = KIT_OBJ_SYMBOL_NONE; 646 memset(&sym_desc, 0, sizeof(sym_desc)); 647 sym_desc.name = kit_sym_intern(c, KIT_SLICE_LIT("data_sym")); 648 sym_desc.bind = KIT_SB_GLOBAL; 649 sym_desc.kind = KIT_SK_OBJ; 650 sym_desc.section = data; 651 sym_desc.size = sizeof(data_bytes); 652 if (kit_obj_builder_symbol(ob, &sym_desc, &sym) != KIT_OK) { 653 EXPECT(0, "define data_sym"); 654 goto fail; 655 } 656 657 sym = KIT_OBJ_SYMBOL_NONE; 658 memset(&sym_desc, 0, sizeof(sym_desc)); 659 sym_desc.name = kit_sym_intern(c, KIT_SLICE_LIT("bss_sym")); 660 sym_desc.bind = KIT_SB_GLOBAL; 661 sym_desc.kind = KIT_SK_OBJ; 662 sym_desc.section = bss; 663 sym_desc.size = 0x40u; 664 if (kit_obj_builder_symbol(ob, &sym_desc, &sym) != KIT_OK) { 665 EXPECT(0, "define bss_sym"); 666 goto fail; 667 } 668 669 if (kit_obj_builder_finalize(ob) != KIT_OK) { 670 EXPECT(0, "finalize bss-tail input"); 671 goto fail; 672 } 673 return ob; 674 675 fail: 676 kit_obj_builder_free(ob); 677 return NULL; 678 } 679 680 /* Like emit_layout, but uses build_bss_tail_input as the link input. */ 681 static int emit_layout_with(const char* script_text, size_t script_len, 682 KitObjBuilder* (*build)(KitCompiler*), 683 EmitFixture* fx) { 684 KitTargetSpec target = 685 kit_unit_target(KIT_ARCH_ARM_64, KIT_OS_LINUX, KIT_OBJ_ELF); 686 KitLinkSessionOptions opts; 687 KitSlice script; 688 KitSlice bytes; 689 size_t len = 0; 690 691 memset(fx, 0, sizeof(*fx)); 692 script.s = script_text; 693 script.len = script_len; 694 EXPECT(kit_link_script_parse(&g_u.ctx, script, &fx->s) == KIT_OK && fx->s, 695 "layout script parses"); 696 if (!fx->s) return 0; 697 EXPECT(kit_unit_compiler_new(&g_u, target, &fx->c) == KIT_OK && fx->c, 698 "compiler allocation"); 699 if (!fx->c) return 0; 700 fx->ob = build(fx->c); 701 if (!fx->ob) return 0; 702 memset(&opts, 0, sizeof(opts)); 703 opts.output_kind = KIT_LINK_OUTPUT_EXE; 704 opts.linker_script = fx->s; 705 EXPECT(kit_link_session_new(fx->c, &opts, &fx->sess) == KIT_OK && fx->sess, 706 "link session allocation"); 707 if (!fx->sess) return 0; 708 EXPECT(kit_link_session_add_obj(fx->sess, fx->ob) == KIT_OK, 709 "add input object"); 710 EXPECT(kit_writer_mem(&g_u.heap, &fx->w) == KIT_OK && fx->w, "memory writer"); 711 if (!fx->w) return 0; 712 EXPECT(kit_link_session_emit(fx->sess, fx->w) == KIT_OK, 713 "emit scripted image"); 714 bytes.data = kit_writer_mem_bytes(fx->w, &len); 715 bytes.len = len; 716 EXPECT(bytes.data && bytes.len > 0, "scripted image bytes"); 717 if (!bytes.data || bytes.len == 0) return 0; 718 EXPECT(kit_obj_open(&g_u.ctx, KIT_SLICE_LIT("<scripted-layout>"), &bytes, 719 &fx->f) == KIT_OK && 720 fx->f, 721 "open scripted image"); 722 return fx->f != NULL; 723 } 724 725 /* Bug 1: an output section with a PROGBITS head + NOBITS (bss) tail must NOT 726 * coalesce a following same-:phdr, vaddr-contiguous output section into the 727 * same PT_LOAD by packing its file bytes file-contiguously — that would skew 728 * the follower's loaded bytes down by the bss-tail size relative to its vaddr. 729 * 730 * .first collects .data (4 PROGBITS bytes) + .bss (0x40 NOBITS bytes) so its 731 * mem_size (0x44) > file_size (4). .second collects .text and shares :ld and is 732 * vaddr-contiguous. We assert no vaddr<->file-offset skew: the segment carrying 733 * .second's bytes must place them so (file_off - seg_file_off) matches 734 * (vaddr - seg_vaddr). The safe fix splits into two PT_LOADs. */ 735 static void check_bss_tail_no_coalesce_skew(void) { 736 static const char script_text[] = 737 "OUTPUT_FORMAT(\"elf64-littleaarch64\")\n" 738 "OUTPUT_ARCH(aarch64)\n" 739 "ENTRY(_start)\n" 740 "PHDRS {\n" 741 " ld PT_LOAD FLAGS(6);\n" 742 "}\n" 743 "SECTIONS {\n" 744 " . = 0x10000;\n" 745 " .first : { *(.data .data.*) *(.bss .bss.*) } :ld\n" 746 " .second : { *(.text .text.*) } :ld\n" 747 "}\n"; 748 EmitFixture fx; 749 KitObjSegIter* it = NULL; 750 KitObjSymInfo text_sym; 751 uint64_t text_vaddr = 0; 752 int saw_text_load = 0; 753 int got_text_sym = 0; 754 755 if (!emit_layout_with(script_text, sizeof(script_text) - 1u, 756 build_bss_tail_input, &fx)) 757 goto done; 758 759 got_text_sym = kit_obj_symbol_by_name(fx.f, KIT_SLICE_LIT("_start"), 760 &text_sym) == KIT_OK; 761 EXPECT(got_text_sym, "_start resolves in linked image"); 762 if (!got_text_sym) goto done; 763 text_vaddr = text_sym.value; 764 765 EXPECT(kit_obj_segiter_new(fx.f, &it) == KIT_OK && it, "segment iterator"); 766 if (!it) goto done; 767 for (;;) { 768 KitObjSegInfo seg; 769 KitIterResult r = kit_obj_segiter_next(it, &seg); 770 if (r == KIT_ITER_END) break; 771 if (r != KIT_ITER_ITEM) break; 772 if (!kit_slice_eq_cstr(seg.name, "LOAD")) continue; 773 /* Find the LOAD segment that contains _start's vaddr. */ 774 if (text_vaddr >= seg.vaddr && text_vaddr < seg.vaddr + seg.vsize) { 775 uint64_t voff = text_vaddr - seg.vaddr; 776 saw_text_load = 1; 777 /* The byte for _start must be file-resident at the matching offset: 778 * its file delta must equal its vaddr delta (no NOBITS-tail skew). */ 779 EXPECT(voff < seg.file_size, 780 "_start's vaddr maps to a file-resident byte (no bss-tail skew)"); 781 /* And the segment's own geometry must be self-consistent: a PROGBITS 782 * follower coalesced after a bss tail would have file_size < the offset 783 * at which its bytes sit. The split-PT_LOAD fix keeps file_size==vsize 784 * for the .text-only segment. */ 785 EXPECT(seg.file_size >= voff + 4u, 786 "_start's 4 bytes lie within the segment file image"); 787 } 788 } 789 EXPECT(saw_text_load, "found the LOAD segment carrying _start"); 790 791 done: 792 if (it) kit_obj_segiter_free(it); 793 emit_cleanup(&fx); 794 } 795 796 /* Bug 2: a FLAGS-less PHDRS PT_LOAD that coalesces an RX section and an RW 797 * section must emit p_flags = R|W|X (the union of mapped sections' perms), 798 * matching GNU ld. Without the perms-union, the single PT_LOAD would carry only 799 * the FIRST section's perms. Two order variants pin this down. */ 800 static void check_coalesce_perms_union(void) { 801 static const char script_rx_first[] = 802 "OUTPUT_FORMAT(\"elf64-littleaarch64\")\n" 803 "OUTPUT_ARCH(aarch64)\n" 804 "ENTRY(_start)\n" 805 "PHDRS {\n" 806 " ld PT_LOAD;\n" 807 "}\n" 808 "SECTIONS {\n" 809 " . = 0x10000;\n" 810 " .text : { *(.text .text.*) } :ld\n" 811 " .data : { *(.data .data.*) } :ld\n" 812 "}\n"; 813 static const char script_rw_first[] = 814 "OUTPUT_FORMAT(\"elf64-littleaarch64\")\n" 815 "OUTPUT_ARCH(aarch64)\n" 816 "ENTRY(_start)\n" 817 "PHDRS {\n" 818 " ld PT_LOAD;\n" 819 "}\n" 820 "SECTIONS {\n" 821 " . = 0x10000;\n" 822 " .data : { *(.data .data.*) } :ld\n" 823 " .text : { *(.text .text.*) } :ld\n" 824 "}\n"; 825 const char* variants[2]; 826 size_t lens[2]; 827 unsigned v; 828 variants[0] = script_rx_first; 829 lens[0] = sizeof(script_rx_first) - 1u; 830 variants[1] = script_rw_first; 831 lens[1] = sizeof(script_rw_first) - 1u; 832 833 for (v = 0; v < 2; ++v) { 834 EmitFixture fx; 835 KitObjSegIter* it = NULL; 836 uint32_t nload = 0; 837 int saw_rwx = 0; 838 839 if (!emit_layout(variants[v], lens[v], &fx)) { 840 emit_cleanup(&fx); 841 continue; 842 } 843 EXPECT(kit_obj_segiter_new(fx.f, &it) == KIT_OK && it, "segment iterator"); 844 if (!it) { 845 emit_cleanup(&fx); 846 continue; 847 } 848 for (;;) { 849 KitObjSegInfo seg; 850 KitIterResult r = kit_obj_segiter_next(it, &seg); 851 if (r == KIT_ITER_END) break; 852 if (r != KIT_ITER_ITEM) break; 853 if (!kit_slice_eq_cstr(seg.name, "LOAD")) continue; 854 ++nload; 855 if (seg.perms == (KIT_SEG_R | KIT_SEG_W | KIT_SEG_X)) ++saw_rwx; 856 } 857 EXPECT(nload == 1, 858 v == 0 ? "RX-then-RW coalesces into one PT_LOAD" 859 : "RW-then-RX coalesces into one PT_LOAD"); 860 EXPECT(saw_rwx == 1, 861 v == 0 ? "RX-first coalesced PT_LOAD has R|W|X perms (union)" 862 : "RW-first coalesced PT_LOAD has R|W|X perms (union)"); 863 if (it) kit_obj_segiter_free(it); 864 emit_cleanup(&fx); 865 } 866 } 867 868 /* Item 5: deeply nested parentheses must fail cleanly (diagnostic) rather than 869 * overflow the parser/eval stack. */ 870 static void check_recursion_depth_guard(void) { 871 /* 512 nested parens around an int — well past the 256 limit. */ 872 char buf[2048]; 873 size_t pos = 0; 874 unsigned i; 875 KitLinkScript* s = NULL; 876 KitSlice text; 877 static const char prefix[] = "SECTIONS {\n . = "; 878 static const char suffix[] = ";\n}\n"; 879 memcpy(buf + pos, prefix, sizeof(prefix) - 1); 880 pos += sizeof(prefix) - 1; 881 for (i = 0; i < 512; ++i) buf[pos++] = '('; 882 buf[pos++] = '0'; 883 for (i = 0; i < 512; ++i) buf[pos++] = ')'; 884 memcpy(buf + pos, suffix, sizeof(suffix) - 1); 885 pos += sizeof(suffix) - 1; 886 text.s = buf; 887 text.len = pos; 888 /* must return an error, not crash, and not produce a script */ 889 EXPECT(kit_link_script_parse(&g_u.ctx, text, &s) != KIT_OK, 890 "deeply nested parens rejected, no crash"); 891 if (s) kit_link_script_free(&g_u.ctx, s); 892 } 893 894 /* Build an input with two writable data sections (.data = 4 bytes, .data.more 895 * = 8 bytes) plus a _start in .text, so a section body can glob each 896 * separately and place interior `sym = .` markers between them. */ 897 static KitObjBuilder* build_two_data_input(KitCompiler* c) { 898 static const uint8_t text_bytes[4] = {0xc0, 0x03, 0x5f, 0xd6}; 899 static const uint8_t data_bytes[4] = {1, 2, 3, 4}; 900 static const uint8_t data2_bytes[8] = {5, 6, 7, 8, 9, 10, 11, 12}; 901 KitObjBuilder* ob = NULL; 902 KitObjSection text = KIT_SECTION_NONE; 903 KitObjSection data = KIT_SECTION_NONE; 904 KitObjSection data2 = KIT_SECTION_NONE; 905 KitObjSymbol sym = KIT_OBJ_SYMBOL_NONE; 906 KitObjSectionDesc d; 907 KitObjSymbolDesc sd; 908 909 if (kit_obj_builder_new(c, &ob) != KIT_OK || !ob) { 910 EXPECT(0, "obj builder allocation"); 911 return NULL; 912 } 913 memset(&d, 0, sizeof(d)); 914 d.name = kit_sym_intern(c, KIT_SLICE_LIT(".text")); 915 d.kind = KIT_SEC_TEXT; 916 d.flags = KIT_SF_ALLOC | KIT_SF_EXEC; 917 d.align = 4; 918 if (kit_obj_builder_section(ob, &d, &text) != KIT_OK || 919 kit_obj_builder_write(ob, text, text_bytes, sizeof(text_bytes)) != KIT_OK) 920 goto fail; 921 922 memset(&d, 0, sizeof(d)); 923 d.name = kit_sym_intern(c, KIT_SLICE_LIT(".data")); 924 d.kind = KIT_SEC_DATA; 925 d.flags = KIT_SF_ALLOC | KIT_SF_WRITE; 926 d.align = 4; 927 if (kit_obj_builder_section(ob, &d, &data) != KIT_OK || 928 kit_obj_builder_write(ob, data, data_bytes, sizeof(data_bytes)) != KIT_OK) 929 goto fail; 930 931 memset(&d, 0, sizeof(d)); 932 d.name = kit_sym_intern(c, KIT_SLICE_LIT(".data.more")); 933 d.kind = KIT_SEC_DATA; 934 d.flags = KIT_SF_ALLOC | KIT_SF_WRITE; 935 d.align = 4; 936 if (kit_obj_builder_section(ob, &d, &data2) != KIT_OK || 937 kit_obj_builder_write(ob, data2, data2_bytes, sizeof(data2_bytes)) != 938 KIT_OK) 939 goto fail; 940 941 memset(&sd, 0, sizeof(sd)); 942 sd.name = kit_sym_intern(c, KIT_SLICE_LIT("_start")); 943 sd.bind = KIT_SB_GLOBAL; 944 sd.kind = KIT_SK_FUNC; 945 sd.section = text; 946 sd.value = 0; 947 sd.size = sizeof(text_bytes); 948 if (kit_obj_builder_symbol(ob, &sd, &sym) != KIT_OK) goto fail; 949 950 if (kit_obj_builder_finalize(ob) != KIT_OK) goto fail; 951 return ob; 952 fail: 953 EXPECT(0, "build two-data input"); 954 kit_obj_builder_free(ob); 955 return NULL; 956 } 957 958 /* Bug fix: section-body commands execute in source order, so a `sym = .` after 959 * an input-section glob captures the location counter ADVANCED past the glob's 960 * content, and `. = ALIGN(N)` after the last glob extends the section. Before 961 * the fix every body assignment was evaluated against the section-start dot, so 962 * _sdata == _mid == _edata. This drives the exact bare-metal `.data` copy-loop 963 * idiom (_sdata/_edata bracketing initialized data) plus an interior midpoint 964 * marker between two globs. */ 965 static void check_body_symbols_after_glob(void) { 966 static const char script_text[] = 967 "OUTPUT_FORMAT(\"elf64-littleaarch64\")\n" 968 "OUTPUT_ARCH(aarch64)\n" 969 "ENTRY(_start)\n" 970 "MEMORY {\n" 971 " FLASH (rx) : ORIGIN = 0x10000, LENGTH = 64K\n" 972 " SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K\n" 973 "}\n" 974 "SECTIONS {\n" 975 " .text : { *(.text .text.*) } > FLASH\n" 976 " .data : ALIGN(4) {\n" 977 " _sdata = .;\n" 978 " *(.data)\n" 979 " _mid = .;\n" 980 " *(.data.more)\n" 981 " . = ALIGN(16);\n" 982 " _edata = .;\n" 983 " } > SRAM AT> FLASH\n" 984 "}\n"; 985 KitTargetSpec target = 986 kit_unit_target(KIT_ARCH_ARM_64, KIT_OS_LINUX, KIT_OBJ_ELF); 987 KitCompiler* c = NULL; 988 KitObjBuilder* ob = NULL; 989 KitLinkScript* s = NULL; 990 KitLinkSession* sess = NULL; 991 KitWriter* w = NULL; 992 KitObjFile* f = NULL; 993 KitSlice script; 994 KitSlice bytes; 995 KitLinkSessionOptions opts; 996 KitObjSymInfo sdata, mid, edata; 997 size_t len = 0; 998 999 script.s = script_text; 1000 script.len = sizeof(script_text) - 1u; 1001 EXPECT(kit_link_script_parse(&g_u.ctx, script, &s) == KIT_OK && s, 1002 "body-symbol script parses"); 1003 if (!s) goto done; 1004 EXPECT(kit_unit_compiler_new(&g_u, target, &c) == KIT_OK && c, 1005 "compiler allocation"); 1006 if (!c) goto done; 1007 ob = build_two_data_input(c); 1008 if (!ob) goto done; 1009 memset(&opts, 0, sizeof(opts)); 1010 opts.output_kind = KIT_LINK_OUTPUT_EXE; 1011 opts.linker_script = s; 1012 EXPECT(kit_link_session_new(c, &opts, &sess) == KIT_OK && sess, 1013 "link session allocation"); 1014 if (!sess) goto done; 1015 EXPECT(kit_link_session_add_obj(sess, ob) == KIT_OK, "add input object"); 1016 EXPECT(kit_writer_mem(&g_u.heap, &w) == KIT_OK && w, "memory writer"); 1017 if (!w) goto done; 1018 EXPECT(kit_link_session_emit(sess, w) == KIT_OK, "emit scripted image"); 1019 bytes.data = kit_writer_mem_bytes(w, &len); 1020 bytes.len = len; 1021 if (!bytes.data || bytes.len == 0) goto done; 1022 EXPECT(kit_obj_open(&g_u.ctx, KIT_SLICE_LIT("<body-symbols>"), &bytes, &f) == 1023 KIT_OK && 1024 f, 1025 "open scripted image"); 1026 if (!f) goto done; 1027 1028 EXPECT(kit_obj_symbol_by_name(f, KIT_SLICE_LIT("_sdata"), &sdata) == KIT_OK, 1029 "_sdata defined"); 1030 EXPECT(kit_obj_symbol_by_name(f, KIT_SLICE_LIT("_mid"), &mid) == KIT_OK, 1031 "_mid defined"); 1032 EXPECT(kit_obj_symbol_by_name(f, KIT_SLICE_LIT("_edata"), &edata) == KIT_OK, 1033 "_edata defined"); 1034 1035 /* .data is placed at SRAM origin (4-aligned). The leading marker captures the 1036 * base; `.data` (4 bytes) advances to base+4; `.data.more` (8 bytes, 1037 * already 4-aligned) ends at base+12; `. = ALIGN(16)` rounds to base+16. */ 1038 EXPECT(sdata.value == 0x20000000u, "_sdata == .data start (section base)"); 1039 EXPECT(mid.value == 0x20000004u, "_mid == base + sizeof(.data) after glob"); 1040 EXPECT(edata.value == 0x20000010u, 1041 "_edata == ALIGN(16) of base + .data + .data.more"); 1042 EXPECT(sdata.value != edata.value, 1043 "interior symbols around globs are not collapsed to the section start"); 1044 1045 done: 1046 if (f) kit_obj_free(f); 1047 if (w) kit_writer_close(w); 1048 if (sess) kit_link_session_free(sess); 1049 if (ob) kit_obj_builder_free(ob); 1050 if (s) kit_link_script_free(&g_u.ctx, s); 1051 if (c) kit_compiler_free(c); 1052 } 1053 1054 int main(void) { 1055 kit_unit_init(&g_u); 1056 check_kernel_script_subset(); 1057 check_scripted_layout_vma_lma(); 1058 check_noload_progbits(); 1059 check_intersection_dot_align(); 1060 check_body_symbols_after_glob(); 1061 check_multibyte_fill(); 1062 check_phdr_coalesce_and_multi(); 1063 check_bss_tail_no_coalesce_skew(); 1064 check_coalesce_perms_union(); 1065 check_recursion_depth_guard(); 1066 kit_unit_summary(&g_u, "link_script_test"); 1067 return kit_unit_status(&g_u); 1068 }