run.sh (49371B)
1 #!/bin/sh 2 # Driver-level checks for the kit-native build verbs `build-exe` / `build-lib` / 3 # `build-obj` (the trio that replaced `compile`). Self-checking (no golden 4 # files): we assert exit status, output existence, symbol/text markers, ld -r 5 # parity, and — for build-exe — native execution of the produced binary. 6 # 7 # Coverage: 8 # build-obj : per-language compile (C / toy / wasm), --emit=obj|asm|c|ir, 9 # -fsyntax-only, default output naming, -o - to stdout, the 10 # -X<lang> frontend-flag router, target features, the multi-source 11 # relocatable combine (with ld -r parity), and the negative paths. 12 # build-lib : static .a from mixed sources, then link against it; single-TU 13 # C-source emission with cross-TU references and local names. 14 # build-exe : polyglot relocatable combine, native link+exec, --group scoping 15 # (verified through the produced exit code), and -L/-l. 16 17 set -u 18 19 script_dir=$(cd "$(dirname "$0")" && pwd) 20 repo_root=$(cd "$script_dir/../.." && pwd) 21 22 KIT="${KIT:-$repo_root/build/kit}" 23 24 if [ ! -x "$KIT" ]; then 25 echo "build: kit binary not found at $KIT" >&2 26 exit 2 27 fi 28 29 work=$(mktemp -d "${TMPDIR:-/tmp}/kit-build-test.XXXXXX") 30 trap 'rm -rf "$work"' EXIT 31 32 KIT_KIT_DIR="$repo_root/test/lib" 33 . "$repo_root/test/lib/kit_sh_kit.sh" 34 kit_report_init 35 36 # ---- fixtures (all local to $work so default-name tests are isolated) ------- 37 mkdir -p "$work/inc" 38 printf '#define ZERO 0\n' > "$work/inc/h.h" 39 printf '#include "h.h"\nint helper(void);\nint main(void){return ZERO+helper();}\n' > "$work/hello.c" 40 printf 'int helper(void){return 0;}\n' > "$work/helper.c" 41 printf '#include <stdint.h>\nint32_t f(void){return 1;}\n' > "$work/std.c" 42 printf 'double fp(void){return 1.0;}\n' > "$work/fp.c" 43 cp "$(ls "$repo_root"/test/toy/cases/*.toy | head -1)" "$work/prog.toy" 44 cp "$repo_root/test/wasm/cases/if_return.wat" "$work/prog.wat" 45 printf 'int wasm_add(int a, int b);\nint wasm_main(void){return wasm_add(1,2)==3?0:1;}\n' > "$work/wasm_main.c" 46 printf 'int wasm_add(int a, int b){return a+b;}\n' > "$work/wasm_helper.c" 47 printf 'int wexe_add(int a, int b);\nint test_main(void){return wexe_add(2,3)==5?0:1;}\n' > "$work/wexe_main.c" 48 printf 'int wexe_add(int a, int b){return a+b;}\nint dead_fn(void){return 99;}\n' > "$work/wexe_add.c" 49 printf '#ifndef AMAL_VALUE\n#error missing AMAL_VALUE\n#endif\nstatic int slot=AMAL_VALUE; static int local(void){return slot;}\nint amal_left(void){return local();}\n' > "$work/amal_left.c" 50 printf '#ifndef AMAL_VALUE\n#error missing AMAL_VALUE\n#endif\nstatic int slot=AMAL_VALUE; static int local(void){return slot;}\nint amal_right(void){return local();}\n' > "$work/amal_right.c" 51 printf 'int amal_left(void); int amal_right(void);\nint main(void){return amal_left()+amal_right()==42?0:1;}\n' > "$work/amal_main.c" 52 printf '.text\n' > "$work/opaque.s" 53 54 cd "$work" 55 56 # =========================================================================== 57 # build-obj — the compile replacement 58 # =========================================================================== 59 60 # ---- C: preprocessor flags + object output --------------------------------- 61 run_ok bo-c-obj "$KIT" build-obj -c -Iinc -DUNUSED=1 hello.c -o hello.o 62 assert_file_exists bo-c-obj-exists hello.o 63 "$KIT" nm hello.o > nm.out 2>/dev/null 64 contains bo-c-obj-has-main nm.out main 65 66 # C freestanding system header (<stdint.h>) resolves via the rt include set. 67 run_ok bo-c-freestanding-header "$KIT" build-obj -c std.c -o std.o 68 69 # Kernel-oriented compile policy flags are accepted by build-obj and flow into 70 # codegen/frontend policy instead of being silently ignored. 71 run_ok bo-kernel-policy-flags "$KIT" build-obj -target x86_64-none-elf \ 72 -ffreestanding -nostdlib -static -no-pie -fno-pic -fno-pie \ 73 -mcmodel=kernel -mno-red-zone -mgeneral-regs-only \ 74 -fno-builtin -fno-builtin-memcpy -fno-stack-protector \ 75 -ffunction-sections -fdata-sections -c -Iinc hello.c \ 76 -o kernel_policy.o 77 assert_file_exists bo-kernel-policy-flags-file kernel_policy.o 78 79 run_ok bo-autovar-zero "$KIT" build-obj \ 80 -ftrivial-auto-var-init=zero -c -Iinc hello.c -o autovar_zero.o 81 assert_file_exists bo-autovar-zero-file autovar_zero.o 82 83 run_fail bo-autovar-pattern-rejected "$KIT" build-obj \ 84 -ftrivial-auto-var-init=pattern -c -Iinc hello.c -o autovar_pattern.o 85 contains bo-autovar-pattern-rejected-diag \ 86 "$work/bo-autovar-pattern-rejected.err" "not yet supported" 87 88 run_ok bo-stack-protector-strong "$KIT" build-obj \ 89 -fstack-protector-strong -c -Iinc hello.c -o stack_protector.o 90 assert_file_exists bo-stack-protector-strong-file stack_protector.o 91 92 run_fail bo-general-regs-only-fp "$KIT" build-obj \ 93 -mgeneral-regs-only -c fp.c -o fp.o 94 contains bo-general-regs-only-fp-diag \ 95 "$work/bo-general-regs-only-fp.err" "floating-point" 96 97 # Default output name: <basename>.o next to cwd when -o is omitted. 98 run_ok bo-c-default-name "$KIT" build-obj -c -Iinc hello.c 99 assert_file_exists bo-c-default-name-file hello.o 100 101 # ---- toy + wasm: non-C frontends through the generic driver ----------------- 102 run_ok bo-toy "$KIT" build-obj -c prog.toy -o prog_toy.o 103 assert_file_exists bo-toy-exists prog_toy.o 104 run_ok bo-wasm "$KIT" build-obj -c prog.wat -o prog_wat.o 105 assert_file_exists bo-wasm-exists prog_wat.o 106 107 # Target-owned option: the driver lowers feature flags into KitTargetOptions. 108 run_ok bo-wasm-target-feature "$KIT" build-obj -target wasm32-none -c \ 109 -mattr=-tail-calls prog.wat -o prog_wat2.o 110 111 # Per-language frontend flag via -X<lang> (routed to the wasm frontend parser). 112 run_ok bo-Xwasm "$KIT" build-obj -target wasm32-none -c \ 113 -Xwasm -mno-feature=tail-calls prog.wat -o prog_wat3.o 114 115 # ---- emit modes ------------------------------------------------------------ 116 run_ok bo-emit-asm "$KIT" build-obj -S -Iinc hello.c -o hello.s 117 contains bo-emit-asm-text hello.s .text 118 run_ok bo-emit-asm-long "$KIT" build-obj --emit=asm -Iinc hello.c -o hello2.s 119 run_ok bo-emit-c "$KIT" build-obj --emit=c prog.toy -o prog.c 120 assert_file_exists bo-emit-c-file prog.c 121 run_ok bo-emit-ir "$KIT" build-obj --emit=ir -O1 prog.toy -o prog.ir 122 assert_file_exists bo-emit-ir-file prog.ir 123 124 # -o - sends the emit to stdout (reusing the cc stdout writer). 125 "$KIT" build-obj --emit=ir -O1 prog.toy -o - > ir.stdout 2>/dev/null 126 if [ -s ir.stdout ]; then ok bo-emit-ir-stdout; else not_ok bo-emit-ir-stdout; fi 127 128 # ---- check-only writes no object ------------------------------------------- 129 rm -f hello.o 130 run_ok bo-check-only "$KIT" build-obj -fsyntax-only -Iinc hello.c helper.c 131 if [ ! -f hello.o ]; then ok bo-check-no-output; else not_ok bo-check-no-output; fi 132 133 # ---- multi-source relocatable combine (ld -r) ------------------------------ 134 run_ok bo-reloc-combine "$KIT" build-obj -Iinc hello.c helper.c -o combined.o 135 assert_file_exists bo-reloc-combine-file combined.o 136 "$KIT" nm combined.o > combined.nm 2>/dev/null 137 contains bo-reloc-has-helper combined.nm helper 138 contains bo-reloc-has-main combined.nm main 139 140 # ld -r parity: the in-memory combine matches a separate compile + `ld -r`. 141 "$KIT" build-obj -c -Iinc hello.c -o sep_hello.o 2>/dev/null 142 "$KIT" build-obj -c helper.c -o sep_helper.o 2>/dev/null 143 "$KIT" ld -r sep_hello.o sep_helper.o -o ldr.o 2>/dev/null 144 "$KIT" nm combined.o | sort > combine.sorted 2>/dev/null 145 "$KIT" nm ldr.o | sort > ldr.sorted 2>/dev/null 146 same_file bo-reloc-ld-r-parity ldr.sorted combine.sorted 147 148 # Polyglot combine: C + toy in one relocatable object. 149 run_ok bo-polyglot-combine "$KIT" build-obj -Iinc helper.c prog.toy -o poly.o 150 151 # ---- wasm multi-source combine (CG-merge path) ----------------------------- 152 # Two C files merged into one .wasm module (magic \0asm + non-empty content). 153 run_ok bo-wasm-multi "$KIT" build-obj -target wasm32-none \ 154 wasm_main.c wasm_helper.c -o multi.wasm 155 assert_file_exists bo-wasm-multi-file multi.wasm 156 # Wasm magic (\0asm = 00 61 73 6d): check first 4 bytes via od (avoids null-byte 157 # issues with cmp on BSD). 158 _wasm_hdr=$(od -A n -t x1 -N 4 multi.wasm | tr -d ' \t\n') 159 [ "$_wasm_hdr" = "0061736d" ] \ 160 && ok bo-wasm-multi-magic || not_ok bo-wasm-multi-magic 161 # Non-trivial module: size must exceed the 8-byte magic+version stub. 162 _wasm_size=$(wc -c < multi.wasm | tr -d ' ') 163 [ "$_wasm_size" -gt 8 ] \ 164 && ok bo-wasm-multi-nontrivial || not_ok bo-wasm-multi-nontrivial 165 166 # Polyglot wasm: C + toy in one .wasm module. 167 run_ok bo-wasm-polyglot "$KIT" build-obj -target wasm32-none \ 168 wasm_helper.c prog.toy -o poly.wasm 169 assert_file_exists bo-wasm-polyglot-file poly.wasm 170 _poly_hdr=$(od -A n -t x1 -N 4 poly.wasm | tr -d ' \t\n') 171 [ "$_poly_hdr" = "0061736d" ] \ 172 && ok bo-wasm-polyglot-magic || not_ok bo-wasm-polyglot-magic 173 174 # Negative: duplicate strong symbol definition must be rejected (at -O1 the 175 # optimizer runs opt_resolve_duplicate_funcs which fires the ODR check). 176 printf 'int wasm_add(int a, int b){return a-b;}\n' > wasm_dup.c 177 run_fail bo-wasm-dup "$KIT" build-obj -O1 -target wasm32-none \ 178 wasm_helper.c wasm_dup.c -o dup.wasm 179 contains bo-wasm-dup-diag "$work/bo-wasm-dup.err" "duplicate definition" 180 181 # ---- negative paths -------------------------------------------------------- 182 # A frontend with an unknown -X flag is rejected by that frontend's parser. 183 run_fail bo-neg-bad-Xflag "$KIT" build-obj -c -Xtoy --bogus prog.toy -o x.o 184 # The target parser rejects an unknown feature name. 185 run_fail bo-neg-bad-feature "$KIT" build-obj -target wasm32-none \ 186 -mattr=+nope -c prog.wat -o x.o 187 # build-obj never links: object/archive inputs are refused. 188 run_fail bo-neg-link-input "$KIT" build-obj hello.o 189 # --emit=c needs an explicit destination. 190 run_fail bo-neg-emit-c-needs-o "$KIT" build-obj --emit=c prog.toy 191 # IR is only available with the optimizer on. 192 run_fail bo-neg-ir-needs-opt "$KIT" build-obj --emit=ir prog.toy -o x.ir 193 # -o cannot fan out across multiple sources in a per-source emit. 194 run_fail bo-neg-o-multi-asm "$KIT" build-obj -S -Iinc hello.c std.c -o both.s 195 # A per-output flag is not allowed inside a --group. 196 run_fail bo-neg-global-in-group "$KIT" build-obj -Iinc --group -O2 -- hello.c 197 # A missing -x argument before a --group source separator should not treat `--` 198 # as a language name. 199 run_fail bo-neg-group-x-missing-arg "$KIT" build-obj --group -x -- hello.c 200 contains bo-neg-group-x-missing-arg-diag "$work/bo-neg-group-x-missing-arg.err" \ 201 "requires an argument" 202 # -dynamic is a build-lib concept. 203 run_fail bo-neg-dynamic "$KIT" build-obj -dynamic prog.toy 204 # unknown flag. 205 run_fail bo-neg-unknown-flag "$KIT" build-obj --nope -c prog.toy -o x.o 206 207 # =========================================================================== 208 # build-lib — static archive 209 # =========================================================================== 210 211 run_ok bl-static "$KIT" build-lib -Iinc -o libmix.a helper.c std.c prog.toy 212 assert_file_exists bl-static-file libmix.a 213 "$KIT" nm libmix.a > libmix.nm 2>/dev/null 214 contains bl-static-has-helper libmix.nm helper 215 216 # One semantic batch becomes one C translation unit. Reusing `local` and 217 # `slot` in both inputs verifies that source-level internal linkage remains 218 # isolated after the original translation-unit boundary disappears. 219 run_ok bl-emit-c "$KIT" build-lib --emit=c -o libamal.c \ 220 --group -DAMAL_VALUE=20 -- amal_left.c \ 221 --group -DAMAL_VALUE=22 -- amal_right.c 222 assert_file_exists bl-emit-c-file libamal.c 223 run_ok bl-emit-c-host-compile "${CC:-cc}" -std=c11 -Wall -Wextra -Werror \ 224 libamal.c amal_main.c -o amal-app 225 run_ok bl-emit-c-run ./amal-app 226 227 # Standalone assembly is an object-only frontend and cannot be represented in 228 # the generated C translation unit. 229 run_fail bl-emit-c-opaque "$KIT" build-lib --emit=c -o opaque.c opaque.s 230 contains bl-emit-c-opaque-diag "$work/bl-emit-c-opaque.err" \ 231 "cannot emit C" 232 # -o is required (no obvious base name across N sources). 233 run_fail bl-neg-needs-o "$KIT" build-lib helper.c std.c 234 # build-lib takes only sources. 235 run_fail bl-neg-link-input "$KIT" build-lib -o x.a helper.o 236 # Dynamic/shared libraries are not yet supported. 237 run_fail bl-neg-dynamic "$KIT" build-lib -dynamic -o x.a helper.c 238 239 # =========================================================================== 240 # build-exe — native link + exec (the headline path) 241 # =========================================================================== 242 243 # A hosted executable (-lc) that returns 0; build, then run it. 244 run_ok be-link "$KIT" build-exe -lc -Iinc hello.c helper.c -o app 245 is_executable be-link-exec app 246 run_ok be-run ./app 247 248 # build-exe should reject the shared-library spelling instead of ignoring it. 249 run_fail be-neg-shared "$KIT" build-exe -shared -lc -Iinc hello.c helper.c \ 250 -o shared-nope 251 contains be-neg-shared-diag "$work/be-neg-shared.err" "shared" 252 253 # Default output name a.out when -o is omitted. 254 run_ok be-default-name "$KIT" build-exe -lc -Iinc hello.c helper.c 255 assert_file_exists be-default-name-file a.out 256 257 # -no-pie must cancel an earlier -pie for build-exe's link state. 258 printf 'void _start(void) { for (;;) {} }\n' > no_pie_start.c 259 run_ok be-no-pie "$KIT" build-exe -target x86_64-linux -nostdlib \ 260 -pie -no-pie no_pie_start.c -o no-pie-exe 261 e_type=$(od -An -tx1 -j 16 -N 2 no-pie-exe | tr -d ' \n') 262 if [ "$e_type" = "0200" ]; then ok be-no-pie-et-exec; else 263 printf 'e_type bytes=%s want=0200\n' "$e_type" \ 264 > "$work/be-no-pie-et-exec.diag" 265 not_ok be-no-pie-et-exec "$work/be-no-pie-et-exec.diag" 266 fi 267 268 # Direct linker flag parity: build-exe accepts --gc-sections, -Ttext, --map, 269 # and --symbols without requiring -Wl, spellings. 270 run_ok be-link-reports "$KIT" build-exe -target x86_64-linux -nostdlib \ 271 -static -no-pie --gc-sections -Ttext=0x500000 \ 272 --map kernel.map --symbols kernel.sym --symbols-format=nm \ 273 no_pie_start.c -o kernel.elf 274 contains be-link-map-target kernel.map "target x86_64-linux" 275 contains be-link-map-segments kernel.map "segments" 276 contains be-link-map-ttext kernel.map "vaddr=0x500000" 277 contains be-link-symbol-start kernel.sym "_start" 278 279 run_ok be-ld-report-obj "$KIT" build-obj -target x86_64-linux \ 280 no_pie_start.c -o no_pie_start.o 281 run_ok be-ld-reports "$KIT" ld -nostdlib -static -no-pie \ 282 -Ttext 0x510000 --map ld.map --symbols ld.sym \ 283 no_pie_start.o -o ld-kernel.elf 284 contains be-ld-map-target ld.map "target x86_64-linux" 285 contains be-ld-map-ttext ld.map "vaddr=0x510000" 286 contains be-ld-symbol-start ld.sym "_start" 287 288 # Executable links are strict by default, with --allow-undefined as an explicit 289 # bring-up escape hatch. 290 printf 'void missing(void); void _start(void){ missing(); }\n' > undef_start.c 291 run_fail be-undef-strict "$KIT" build-exe -target x86_64-linux -nostdlib \ 292 -static -no-pie undef_start.c -o undef-strict 293 contains be-undef-strict-diag "$work/be-undef-strict.err" "undefined reference" 294 run_ok be-undef-allowed "$KIT" build-exe -target x86_64-linux -nostdlib \ 295 -static -no-pie --allow-undefined undef_start.c -o undef-allowed 296 assert_file_exists be-undef-allowed-file undef-allowed 297 298 # --group scoping is observable through the program's exit code: the bare TU 299 # sees -DRET=0 (global), the grouped TU overrides it to a non-zero value. 300 printf '#ifndef RET\n#define RET 7\n#endif\nint ret_val(void){return RET;}\n' > rv.c 301 printf 'int ret_val(void);\nint main(void){return ret_val();}\n' > rmain.c 302 run_ok be-group-link "$KIT" build-exe -lc -DRET=0 rmain.c \ 303 --group -DRET=3 -- rv.c -o appg 304 if ./appg; then code=0; else code=$?; fi 305 if [ "$code" -eq 3 ]; then ok be-group-scope; else 306 echo "expected exit 3 from grouped -DRET, got $code" > "$work/be-group-scope.diag" 307 not_ok be-group-scope "$work/be-group-scope.diag" 308 fi 309 310 # Link against the static library built above via -L/-l, then run. 311 printf 'int helper(void);\nint main(void){return helper();}\n' > usemix.c 312 run_ok be-link-archive "$KIT" build-exe -lc usemix.c -L. -lmix -o app2 313 run_ok be-run-archive ./app2 314 315 # =========================================================================== 316 # build-exe — wasm target 317 # =========================================================================== 318 319 # Multi-source: produces a .wasm with DCE/internalization, runnable via kit run. 320 run_ok be-wasm-exe "$KIT" build-exe -target wasm32-none \ 321 wexe_main.c wexe_add.c -e test_main -o wexe.wasm 322 assert_file_exists be-wasm-exe-file wexe.wasm 323 _wexe_hdr=$(od -A n -t x1 -N 4 wexe.wasm | tr -d ' \t\n') 324 [ "$_wexe_hdr" = "0061736d" ] && ok be-wasm-exe-magic || not_ok be-wasm-exe-magic 325 run_ok be-wasm-exe-run "$KIT" run -e test_main wexe.wasm 326 327 # Default output name a.wasm when -o is omitted. 328 run_ok be-wasm-exe-default-name "$KIT" build-exe -target wasm32-none \ 329 wexe_main.c wexe_add.c -e test_main 330 assert_file_exists be-wasm-exe-default-name-file a.wasm 331 332 # kit run without -e reads the kit-entry custom section from the module. 333 run_ok be-wasm-exe-autoentry "$KIT" run wexe.wasm 334 335 # Unknown entry symbol is rejected with a clear diagnostic. 336 run_fail be-wasm-exe-bad-entry "$KIT" build-exe -target wasm32-none \ 337 wexe_main.c wexe_add.c -e no_such_fn -o bad_entry.wasm 338 contains be-wasm-exe-bad-entry-diag "$work/be-wasm-exe-bad-entry.err" \ 339 "entry symbol" 340 341 # =========================================================================== 342 # build-exe / ld — kernel linker flags, map completeness, freestanding policy 343 # =========================================================================== 344 345 # A bare freestanding _start used by the flag/policy tests below. 346 printf 'void _start(void){ for(;;){} }\nint mydata = 42;\n' > kstart.c 347 348 # ---- --defsym: absolute literal + symbol alias ---------------------------- 349 # my_abs becomes an absolute symbol at 0x1234; my_alias takes _start's address. 350 run_ok be-defsym "$KIT" build-exe -target x86_64-linux -nostdlib -static -no-pie \ 351 --defsym my_abs=0x1234 --defsym my_alias=_start \ 352 --symbols defsym.sym kstart.c -o defsym.elf 353 contains be-defsym-abs defsym.sym "0000000000001234 A my_abs" 354 # my_alias takes _start's address (the alias inherits the target's placement, 355 # so it rebases to the same final address _start carries). 356 _start_addr=$(grep -E ' T _start$' defsym.sym | cut -d' ' -f1) 357 if grep -qE "^$_start_addr . my_alias\$" defsym.sym; then ok be-defsym-alias; else 358 { echo "my_alias != _start ($_start_addr)"; cat defsym.sym; } \ 359 > "$work/be-defsym-alias.diag" 360 not_ok be-defsym-alias "$work/be-defsym-alias.diag" 361 fi 362 # defsym via ld too. 363 run_ok be-ld-report-kstart "$KIT" build-obj -target x86_64-linux \ 364 kstart.c -o kstart.o 365 run_ok ld-defsym "$KIT" ld -nostdlib -static -no-pie \ 366 --defsym ld_abs=0x4321 --symbols lddefsym.sym kstart.o -o lddefsym.elf 367 contains ld-defsym-abs lddefsym.sym "0000000000004321 A ld_abs" 368 # --defsym with no value is rejected. 369 run_fail be-defsym-bad "$KIT" build-exe -target x86_64-linux -nostdlib -static \ 370 -no-pie --defsym broken kstart.c -o defsymbad.elf 371 contains be-defsym-bad-diag "$work/be-defsym-bad.err" "NAME=EXPR" 372 373 # ---- --section-start / -Tdata / -Tbss: per-section address overrides ------- 374 # The requested absolute address is the section's EXACT final runtime address 375 # (GNU-ld semantics). (-Ttext is the ET_EXEC header-page model — img_base plus 376 # the ehdr+phdrs page — so it is intentionally NOT exact; see the FX2 block at 377 # the end for the dedicated exact-vaddr coverage.) 378 run_ok be-section-start "$KIT" build-exe -target x86_64-linux -nostdlib \ 379 -static -no-pie --section-start=.text=0x500000 -Tdata 0x600000 \ 380 --map secstart.map kstart.c -o secstart.elf 381 contains be-section-start-text secstart.map "vaddr=0x500000" 382 contains be-section-start-data secstart.map "vaddr=0x600000" 383 run_ok ld-tbss "$KIT" ld -nostdlib -static -no-pie -Tdata 0x600000 \ 384 kstart.o -o tbss.elf 385 run_ok ld-section-start "$KIT" ld -nostdlib -static -no-pie \ 386 --section-start=.text=0x700000 kstart.o -o secstart2.elf 387 388 # ---- --orphan-handling / --fatal-warnings: parse + plumb ------------------- 389 run_ok be-orphan "$KIT" build-exe -target x86_64-linux -nostdlib -static \ 390 -no-pie --orphan-handling=warn kstart.c -o orphan.elf 391 run_fail be-orphan-bad "$KIT" build-exe -target x86_64-linux -nostdlib \ 392 -static -no-pie --orphan-handling=nonsense kstart.c -o orphanbad.elf 393 contains be-orphan-bad-diag "$work/be-orphan-bad.err" "orphan-handling" 394 run_ok be-fatal-warnings "$KIT" build-exe -target x86_64-linux -nostdlib \ 395 -static -no-pie --fatal-warnings kstart.c -o fatal.elf 396 run_ok ld-orphan "$KIT" ld -nostdlib -static -no-pie --orphan-handling=error \ 397 kstart.o -o ldorphan.elf 398 399 # ---- map completeness: LMA, discarded, unresolved, normalized paths -------- 400 run_ok be-map-complete "$KIT" build-exe -target x86_64-linux -nostdlib \ 401 -static -no-pie --gc-sections -ffunction-sections \ 402 --map complete.map kstart.c -o complete.elf 403 contains be-map-has-lma complete.map "lma=0x" 404 contains be-map-has-discarded complete.map "discarded" 405 contains be-map-has-unresolved complete.map "unresolved" 406 # No absolute host path leaks into the map (paths are normalized to basenames). 407 if grep -qE 'input=/|input=.:[\\/]' complete.map; then 408 { echo "absolute path leaked into map"; grep input= complete.map; } \ 409 > "$work/be-map-no-abspath.diag" 410 not_ok be-map-no-abspath "$work/be-map-no-abspath.diag" 411 else 412 ok be-map-no-abspath 413 fi 414 # The map is deterministic across two identical links. 415 run_ok be-map-det1 "$KIT" build-exe -target x86_64-linux -nostdlib -static \ 416 -no-pie --map det1.map kstart.c -o det1.elf 417 run_ok be-map-det2 "$KIT" build-exe -target x86_64-linux -nostdlib -static \ 418 -no-pie --map det2.map kstart.c -o det2.elf 419 same_file be-map-deterministic det1.map det2.map 420 421 # ---- --cref: cross-reference table (deterministic, basenames only) --------- 422 printf 'int xref_helper(void);\nvoid _start(void){ xref_helper(); for(;;){} }\n' \ 423 > xmain.c 424 printf 'int xref_helper(void){ return 9; }\n' > xhelp.c 425 run_ok be-cref "$KIT" build-exe -target x86_64-linux -nostdlib -static -no-pie \ 426 --cref cref.out xmain.c xhelp.c -o cref.elf 427 contains be-cref-header cref.out "cross reference table" 428 contains be-cref-symbol cref.out "xref_helper" 429 if grep -qE '^ def /|^ ref /' cref.out; then 430 { echo "absolute path leaked into cref"; cat cref.out; } \ 431 > "$work/be-cref-no-abspath.diag" 432 not_ok be-cref-no-abspath "$work/be-cref-no-abspath.diag" 433 else 434 ok be-cref-no-abspath 435 fi 436 # cref via ld attributes def/ref to the right object basenames. 437 run_ok ld-cref-objs "$KIT" build-obj -target x86_64-linux xmain.c -o xmain.o 438 run_ok ld-cref-objs2 "$KIT" build-obj -target x86_64-linux xhelp.c -o xhelp.o 439 run_ok ld-cref "$KIT" ld -nostdlib -static -no-pie --cref ldcref.out \ 440 xmain.o xhelp.o -o ldcref.elf 441 # xref_helper is defined in xhelp.o, referenced from xmain.o. 442 if awk '/^xref_helper$/{f=1;next} f&&/^ def xhelp.o$/{ok=1} f&&/^[^ ]/&&!/^xref_helper$/{f=0} END{exit !ok}' ldcref.out; then 443 ok ld-cref-def 444 else 445 cp ldcref.out "$work/ld-cref-def.diag"; not_ok ld-cref-def "$work/ld-cref-def.diag" 446 fi 447 448 # ---- --print-memory-usage: per-MEMORY-region summary (script-driven) ------- 449 cat > memuse.ld <<'LDEOF' 450 MEMORY { 451 RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 0x100000 452 FLASH (rx) : ORIGIN = 0x10000000, LENGTH = 0x40000 453 } 454 SECTIONS { 455 . = 0x80000000; 456 .text : { *(.text*) } 457 .data : { *(.data*) } 458 .bss : { *(.bss*) } 459 } 460 LDEOF 461 printf 'void _start(void){for(;;){}}\nint memd=7;\n' > memk.c 462 run_ok be-memuse "$KIT" build-exe -target riscv64-none-elf -ffreestanding \ 463 -nostdlib -static -no-pie -T memuse.ld --print-memory-usage memk.c \ 464 -o memk.elf 465 contains be-memuse-header "$work/be-memuse.out" "Memory region" 466 contains be-memuse-ram "$work/be-memuse.out" "RAM" 467 contains be-memuse-flash "$work/be-memuse.out" "FLASH" 468 run_ok be-memuse-obj "$KIT" build-obj -target riscv64-none-elf -ffreestanding \ 469 -nostdlib memk.c -o memk.o 470 run_ok ld-memuse "$KIT" ld -T memuse.ld --print-memory-usage memk.o -o memkld.elf 471 contains ld-memuse-header "$work/ld-memuse.out" "Memory region" 472 473 # ---- freestanding strict validation (build-exe + ld) ----------------------- 474 # A foreign-arch object in a freestanding link is rejected (the link target's 475 # arch is authoritative; a silent mis-link is a hard error). 476 run_ok fs-x64-obj "$KIT" build-obj -target x86_64-none-elf -ffreestanding \ 477 -nostdlib kstart.c -o fs_x64.o 478 printf 'int other(void){return 1;}\n' > fsother.c 479 run_ok fs-aa64-obj "$KIT" build-obj -target aarch64-none-elf -ffreestanding \ 480 -nostdlib fsother.c -o fs_aa64.o 481 run_fail fs-cross-arch-ld "$KIT" ld -o fscross.elf fs_x64.o fs_aa64.o 482 contains fs-cross-arch-ld-diag "$work/fs-cross-arch-ld.err" \ 483 "does not match the link target" 484 run_fail fs-cross-arch-be "$KIT" build-exe -target x86_64-none-elf \ 485 -ffreestanding -nostdlib -static -no-pie kstart.c fs_aa64.o -o fscross2.elf 486 contains fs-cross-arch-be-diag "$work/fs-cross-arch-be.err" \ 487 "does not match the link target" 488 # A DSO (shared object) input into a freestanding link is rejected. The fixture 489 # libc.so is an ET_DYN x86_64 shared object copied in locally. 490 if [ -f "$repo_root/test/driver/fixtures/libc.so" ]; then 491 cp "$repo_root/test/driver/fixtures/libc.so" . 492 run_fail fs-dso-ld "$KIT" ld -o fsdso.elf fs_x64.o libc.so 493 contains fs-dso-ld-diag "$work/fs-dso-ld.err" "DSO" 494 run_fail fs-dso-be "$KIT" build-exe -target x86_64-none-elf -ffreestanding \ 495 -nostdlib -static -no-pie kstart.c libc.so -o fsdso2.elf 496 contains fs-dso-be-diag "$work/fs-dso-be.err" "DSO" 497 else 498 skip_test fs-dso-ld "no libc.so fixture" 499 skip_test fs-dso-be "no libc.so fixture" 500 fi 501 # A same-arch freestanding link still succeeds (no false positive). 502 run_ok fs-same-arch-ok "$KIT" ld -o fsok.elf fs_x64.o 503 504 # =========================================================================== 505 # Per-arch freestanding kernel-link fixtures (KERNEL.md acceptance criteria) 506 # =========================================================================== 507 # 508 # A HOST-SIDE (no qemu/emulator) end-to-end kernel-link per first-pass 509 # freestanding target: x86_64 / aarch64 / riscv64 / riscv32 none-elf. Each 510 # exercises the documented acceptance flow: 511 # - compile a tiny C + asm kernel set with `build-exe -ffreestanding 512 # -nostdlib -static -no-pie -T kernel.ld -e _start`, 513 # - emit a deterministic `--map` and `--symbols` side file, 514 # - convert the linked ELF to a flat binary with `kit image --format bin`, 515 # - assert the link succeeds, the image is non-empty, the link is byte-for-byte 516 # reproducible across two runs (ELF + map + symbols), and freestanding strict 517 # validation accepts it (no dynamic artifacts: --no-dynamic + --require-entry). 518 # 519 # The kernel source per arch is minimal: an asm `_start` self-loop stub (fine to 520 # never run), plus a C function touching .rodata / .data / .bss. The linker 521 # script pins .text at a fixed VMA so addresses are deterministic. 522 523 # Shared linker script: a fixed text base makes every emitted address stable. 524 cat > kernel.ld <<'LDEOF' 525 ENTRY(_start) 526 SECTIONS { 527 . = 0x80000000; 528 .text : { *(.text*) } 529 .rodata : { *(.rodata*) } 530 .data : { *(.data*) } 531 .bss : { *(.bss*) } 532 } 533 LDEOF 534 535 # Shared C kernel translation unit: one function plus .rodata / .data / .bss. 536 cat > kernel_main.c <<'CEOF' 537 int kernel_var = 42; /* .data */ 538 int kernel_bss; /* .bss */ 539 const int kernel_ro = 7; /* .rodata */ 540 int kernel_main(void) { return kernel_var + kernel_ro + kernel_bss; } 541 CEOF 542 543 # kernel_link ARCH ASMBODY [EXTRA_CFLAGS...] : build + map/symbols + image + 544 # determinism + strict-validation for one none-elf target. The asm body is the 545 # instruction(s) for an `_start` self-loop stub. 546 kernel_link() { 547 _ka_arch=$1; _ka_asm=$2; shift 2 548 _ka_t="${_ka_arch}-none-elf" 549 printf '.globl _start\n.section .text\n_start:\n %s\n' "$_ka_asm" \ 550 > "kstart_${_ka_arch}.s" 551 552 # build-exe: C + asm freestanding kernel link with a pinned script + entry. 553 run_ok "kern-${_ka_arch}-link" "$KIT" build-exe -target "$_ka_t" \ 554 -ffreestanding -nostdlib -static -no-pie "$@" \ 555 -T kernel.ld -e _start \ 556 --map "kern_${_ka_arch}.map" --symbols "kern_${_ka_arch}.sym" \ 557 "kstart_${_ka_arch}.s" kernel_main.c -o "kern_${_ka_arch}.elf" 558 assert_file_exists "kern-${_ka_arch}-elf" "kern_${_ka_arch}.elf" 559 assert_file_exists "kern-${_ka_arch}-map" "kern_${_ka_arch}.map" 560 assert_file_exists "kern-${_ka_arch}-sym" "kern_${_ka_arch}.sym" 561 # The pinned _start address shows up in both the map and the symbols file. 562 contains "kern-${_ka_arch}-map-entry" "kern_${_ka_arch}.map" \ 563 "entry _start 0x80000000" 564 contains "kern-${_ka_arch}-sym-start" "kern_${_ka_arch}.sym" \ 565 "0000000080000000 T _start" 566 567 # Flat binary image: succeeds and is non-empty. 568 run_ok "kern-${_ka_arch}-image" "$KIT" image --format bin \ 569 "kern_${_ka_arch}.elf" -o "kern_${_ka_arch}.bin" 570 if [ -s "kern_${_ka_arch}.bin" ]; then ok "kern-${_ka_arch}-image-nonempty" 571 else not_ok "kern-${_ka_arch}-image-nonempty"; fi 572 573 # Freestanding strict validation accepts it: a static, entried, non-dynamic 574 # ELF passes --no-dynamic + --require-entry. 575 run_ok "kern-${_ka_arch}-no-dynamic" "$KIT" image --no-dynamic \ 576 --require-entry --format bin "kern_${_ka_arch}.elf" \ 577 -o "kern_${_ka_arch}.nd.bin" 578 579 # Reproducibility: a second identical link is byte-identical (ELF + map + 580 # symbols + image). 581 run_ok "kern-${_ka_arch}-link2" "$KIT" build-exe -target "$_ka_t" \ 582 -ffreestanding -nostdlib -static -no-pie "$@" \ 583 -T kernel.ld -e _start \ 584 --map "kern_${_ka_arch}.2.map" --symbols "kern_${_ka_arch}.2.sym" \ 585 "kstart_${_ka_arch}.s" kernel_main.c -o "kern_${_ka_arch}.2.elf" 586 run_ok "kern-${_ka_arch}-image2" "$KIT" image --format bin \ 587 "kern_${_ka_arch}.2.elf" -o "kern_${_ka_arch}.2.bin" 588 same_file "kern-${_ka_arch}-elf-reproducible" \ 589 "kern_${_ka_arch}.elf" "kern_${_ka_arch}.2.elf" 590 same_file "kern-${_ka_arch}-map-reproducible" \ 591 "kern_${_ka_arch}.map" "kern_${_ka_arch}.2.map" 592 same_file "kern-${_ka_arch}-sym-reproducible" \ 593 "kern_${_ka_arch}.sym" "kern_${_ka_arch}.2.sym" 594 same_file "kern-${_ka_arch}-image-reproducible" \ 595 "kern_${_ka_arch}.bin" "kern_${_ka_arch}.2.bin" 596 } 597 598 # x86_64 also disables the SysV red zone (kernel codegen requirement). 599 kernel_link x86_64 "jmp _start" -mno-red-zone 600 kernel_link aarch64 "b _start" 601 kernel_link riscv64 "j _start" 602 kernel_link riscv32 "j _start" 603 604 # =========================================================================== 605 # Byte-golden --map / --symbols for a fully-deterministic kernel link 606 # =========================================================================== 607 # 608 # The grep-assertions above (and in the kernel-flag section) prove fields are 609 # present; this locks the *exact* emitted bytes against a checked-in golden so a 610 # layout/format regression is caught. We pin one arch (aarch64-none-elf, the 611 # reference backend) with the address-pinning kernel.ld above. The map/symbols 612 # carry no host paths (the writer uses basenames; this fixture's links contribute 613 # no input= lines at all), so a raw byte-golden is directory-independent. 614 golden_dir="$repo_root/test/buildcmds/golden" 615 run_ok be-golden-link "$KIT" build-exe -target aarch64-none-elf \ 616 -ffreestanding -nostdlib -static -no-pie \ 617 -T kernel.ld -e _start \ 618 --map golden.map --symbols golden.sym \ 619 kstart_aarch64.s kernel_main.c -o golden.elf 620 if [ "${KIT_REGEN_GOLDEN:-0}" = "1" ]; then 621 # Opt-in regeneration: refresh the committed goldens from this run. 622 mkdir -p "$golden_dir" 623 cp golden.map "$golden_dir/aarch64_kernel.map" 624 cp golden.sym "$golden_dir/aarch64_kernel.sym" 625 echo "regenerated goldens in $golden_dir" >&2 626 fi 627 same_file be-golden-map "$golden_dir/aarch64_kernel.map" golden.map 628 same_file be-golden-sym "$golden_dir/aarch64_kernel.sym" golden.sym 629 630 # =========================================================================== 631 # Negative tests: region overflow + discarded-section map reporting 632 # =========================================================================== 633 634 # ---- region overflow: a MEMORY region too small for its sections is rejected. 635 cat > overflow.ld <<'LDEOF' 636 ENTRY(_start) 637 MEMORY { 638 TINY (rwx) : ORIGIN = 0x80000000, LENGTH = 8 639 } 640 SECTIONS { 641 .text : { *(.text*) } > TINY 642 .data : { *(.data*) } > TINY 643 } 644 LDEOF 645 run_fail be-region-overflow "$KIT" build-exe -target aarch64-none-elf \ 646 -ffreestanding -nostdlib -static -no-pie -T overflow.ld -e _start \ 647 kstart_aarch64.s kernel_main.c -o overflow.elf 648 contains be-region-overflow-diag "$work/be-region-overflow.err" \ 649 "overflows MEMORY region" 650 # Via ld too: the same script over a precompiled object is rejected. 651 run_ok be-region-overflow-obj "$KIT" build-obj -target aarch64-none-elf \ 652 -ffreestanding -nostdlib kernel_main.c -o ovf_main.o 653 run_ok be-region-overflow-start "$KIT" build-obj -target aarch64-none-elf \ 654 -ffreestanding -nostdlib kstart_aarch64.s -o ovf_start.o 655 run_fail ld-region-overflow "$KIT" ld -nostdlib -static -no-pie \ 656 -T overflow.ld ovf_start.o ovf_main.o -o ld-overflow.elf 657 contains ld-region-overflow-diag "$work/ld-region-overflow.err" \ 658 "overflows MEMORY region" 659 660 # ---- discarded sections: a /DISCARD/-removed section appears in the map's 661 # discarded list (positive assertion on the discarded-sections map field). 662 cat > discard.ld <<'LDEOF' 663 ENTRY(_start) 664 SECTIONS { 665 . = 0x80000000; 666 .text : { *(.text*) } 667 .data : { *(.data*) } 668 /DISCARD/ : { *(.rodata*) } 669 } 670 LDEOF 671 run_ok be-discard "$KIT" build-exe -target aarch64-none-elf \ 672 -ffreestanding -nostdlib -static -no-pie -T discard.ld -e _start \ 673 --map discard.map kstart_aarch64.s kernel_main.c -o discard.elf 674 # The map's discarded list (between the `discarded` and `unresolved` headers) 675 # names .rodata, the section routed into /DISCARD/. 676 awk '/^discarded/{f=1;next} /^unresolved/{f=0} f' discard.map > discard.list 677 contains be-discard-rodata discard.list ".rodata" 678 679 # ---- discarded via --gc-sections: a GC-dropped function-section shows up in 680 # the discarded list. kernel_main is unreferenced from _start (the asm stub just 681 # self-loops), so with -ffunction-sections + --gc-sections it is collected. 682 run_ok be-gc-discard "$KIT" build-exe -target aarch64-none-elf \ 683 -ffreestanding -nostdlib -static -no-pie --gc-sections \ 684 -ffunction-sections -fdata-sections -T kernel.ld -e _start \ 685 --map gc.map kstart_aarch64.s kernel_main.c -o gc.elf 686 awk '/^discarded/{f=1;next} /^unresolved/{f=0} f' gc.map > gc.list 687 contains be-gc-discard-fn gc.list ".text.kernel_main" 688 689 # =========================================================================== 690 # kernel-FX2: resolve/validate/section-pinning correctness fixes (Bugs 3-6) 691 # =========================================================================== 692 # Self-contained block (kept near the end to ease merge with the other agent's 693 # additions). Each test corresponds to one of the FX2 linker correctness bugs. 694 695 # ---- Bug 4: --defsym satisfies an otherwise-undefined reference ------------ 696 # A C source references an extern with no providing input; --defsym supplies its 697 # absolute value. Without the fix the link aborts with "undefined reference" 698 # before defsyms are applied (the documented kernel use case for 699 # --defsym __stack_chk_guard / _kernel_base referenced from C). 700 printf 'extern char fx2_marker[];\nchar* fx2_use(void){ return fx2_marker; }\nvoid _start(void){ (void)fx2_use(); for(;;){} }\n' > fx2_defsym.c 701 run_ok fx2-defsym-undef-be "$KIT" build-exe -target x86_64-linux -nostdlib \ 702 -static -no-pie --defsym fx2_marker=0x1000 -e _start \ 703 --symbols fx2_defsym.sym fx2_defsym.c -o fx2_defsym.elf 704 contains fx2-defsym-undef-be-abs fx2_defsym.sym "0000000000001000 A fx2_marker" 705 # Same through ld over a precompiled object (no --allow-undefined escape). 706 run_ok fx2-defsym-undef-obj "$KIT" build-obj -target x86_64-linux \ 707 fx2_defsym.c -o fx2_defsym.o 708 run_ok fx2-defsym-undef-ld "$KIT" ld -nostdlib -static -no-pie \ 709 --defsym fx2_marker=0x2000 -e _start --symbols fx2_defsym_ld.sym \ 710 fx2_defsym.o -o fx2_defsym_ld.elf 711 contains fx2-defsym-undef-ld-abs fx2_defsym_ld.sym \ 712 "0000000000002000 A fx2_marker" 713 # Exactly one fx2_marker symbol appears (no duplicate-symbol artifact: the skip 714 # path leaves the undef record for link_apply_defsyms to update in place). 715 _fx2_ndef=$(grep -c ' A fx2_marker$' fx2_defsym.sym) 716 if [ "$_fx2_ndef" -eq 1 ]; then ok fx2-defsym-undef-no-dup; else 717 { echo "expected 1 fx2_marker, got $_fx2_ndef"; cat fx2_defsym.sym; } \ 718 > "$work/fx2-defsym-undef-no-dup.diag" 719 not_ok fx2-defsym-undef-no-dup "$work/fx2-defsym-undef-no-dup.diag" 720 fi 721 # A genuine undef NOT named by any --defsym still fails (no over-broad escape). 722 printf 'extern char fx2_other[];\nchar* g(void){return fx2_other;}\nvoid _start(void){(void)g();for(;;){}}\n' > fx2_undef2.c 723 run_fail fx2-defsym-still-strict "$KIT" build-exe -target x86_64-linux \ 724 -nostdlib -static -no-pie --defsym fx2_marker=0x1000 -e _start \ 725 fx2_undef2.c -o fx2_undef2.elf 726 contains fx2-defsym-still-strict-diag "$work/fx2-defsym-still-strict.err" \ 727 "undefined reference to 'fx2_other'" 728 729 # ---- Bug 6: --section-start / -Tdata land at the EXACT requested vaddr ------ 730 # The requested absolute address is honored exactly (GNU-ld semantics); without 731 # the fix the section/entry lands headers_load (one page) too high. 732 run_ok fx2-section-start "$KIT" build-exe -target x86_64-linux -nostdlib \ 733 -static -no-pie --section-start=.text=0x500000 -Tdata=0x600000 \ 734 --map fx2_ss.map --symbols fx2_ss.sym kstart.c -o fx2_ss.elf 735 contains fx2-ss-text-exact fx2_ss.map "vaddr=0x500000" 736 contains fx2-ss-data-exact fx2_ss.map "vaddr=0x600000" 737 # _start sits at the pinned .text base, and the entry reflects it. 738 contains fx2-ss-start-sym fx2_ss.sym "0000000000500000 T _start" 739 contains fx2-ss-entry fx2_ss.map "entry _start 0x500000" 740 # Below-base requests are honored exactly too (no re-add of img_base). 741 run_ok fx2-section-start-low "$KIT" build-exe -target x86_64-linux -nostdlib \ 742 -static -no-pie --section-start=.text=0x1000 -e _start \ 743 --map fx2_ss_low.map --symbols fx2_ss_low.sym kstart.c -o fx2_ss_low.elf 744 contains fx2-ss-low-text-exact fx2_ss_low.map "vaddr=0x1000" 745 contains fx2-ss-low-entry fx2_ss_low.map "entry _start 0x1000" 746 # Via ld too: --section-start lands the section exactly. 747 run_ok fx2-ss-ld "$KIT" ld -nostdlib -static -no-pie \ 748 --section-start=.text=0x700000 -e _start --map fx2_ss_ld.map \ 749 kstart.o -o fx2_ss_ld.elf 750 contains fx2-ss-ld-text-exact fx2_ss_ld.map "vaddr=0x700000" 751 752 # ---- Bug 3: relocation into a (NOLOAD) output section does not crash -------- 753 # A PROGBITS input carrying a pointer initializer (R_ABS64) routed into a NOLOAD 754 # output section. NOLOAD makes the segment NOBITS (no on-disk bytes); the reloc 755 # must be skipped, not written through a NULL segment buffer (UBSan: store to 756 # null pointer). Assert the link succeeds and emits a binary. 757 cat > fx2_noload.ld <<'LDEOF' 758 ENTRY(_start) 759 SECTIONS { 760 . = 0x80000000; 761 .text : { *(.text*) } 762 .data : { *(.data*) } 763 .shadow (NOLOAD) : { *(.shadow*) } 764 .bss : { *(.bss*) } 765 } 766 LDEOF 767 cat > fx2_noload.c <<'CEOF' 768 int fx2_target = 7; 769 __attribute__((section(".shadow"))) int* fx2_shadow_ptr = &fx2_target; 770 void _start(void){ for(;;){} } 771 CEOF 772 run_ok fx2-noload-reloc "$KIT" build-exe -target aarch64-none-elf \ 773 -ffreestanding -nostdlib -static -no-pie -T fx2_noload.ld -e _start \ 774 fx2_noload.c -o fx2_noload.elf 775 assert_file_exists fx2-noload-reloc-file fx2_noload.elf 776 777 # ---- Bug 5: hosted -ffreestanding -static -no-pie also rejects cross-arch -- 778 # build.c sets freestanding_strict for BOTH the *-none-* target AND the hosted 779 # -ffreestanding static-non-PIE trigger; the per-input arch/format guard must 780 # run for the hosted trigger too (else a silent mis-link of a foreign-arch .o). 781 # fsother.c (defined earlier) provides only `other()`, so it can be linked 782 # alongside kstart.c's `_start` without a duplicate-symbol clash. 783 run_ok fx2-fs-hosted-x64 "$KIT" build-obj -target x86_64-linux -ffreestanding \ 784 -nostdlib fsother.c -o fx2_fs_x64.o 785 run_ok fx2-fs-hosted-aa64 "$KIT" build-obj -target aarch64-linux \ 786 -ffreestanding -nostdlib fsother.c -o fx2_fs_aa64.o 787 run_fail fx2-fs-hosted-cross "$KIT" build-exe -target x86_64-linux \ 788 -ffreestanding -nostdlib -static -no-pie kstart.c fx2_fs_aa64.o \ 789 -o fx2_fs_cross.elf 790 contains fx2-fs-hosted-cross-diag "$work/fx2-fs-hosted-cross.err" \ 791 "does not match the link target" 792 # Same-arch hosted freestanding link still succeeds (no false positive). 793 run_ok fx2-fs-hosted-same "$KIT" build-exe -target x86_64-linux -ffreestanding \ 794 -nostdlib -static -no-pie -e _start kstart.c fx2_fs_x64.o -o fx2_fs_same.elf 795 # Link report-writer correctness fixes (kernel-FY): --print-memory-usage alias 796 # double-count + high-half region overflow, and --cref imported-symbol coverage. 797 # =========================================================================== 798 799 # Helper: extract a region's "used=0x..." token from a --print-memory-usage 800 # output file by region name (first match wins). 801 fy_memuse_used() { 802 awk -v rg="$1" '$1==rg { for (i=1;i<=NF;i++) if ($i ~ /^used=/) { print substr($i,6); exit } }' "$2" 803 } 804 805 # ---- Bug 7: a section placed into N program headers (alias segments) must be 806 # charged to its MEMORY region ONCE, not N times. We compare the single-phdr 807 # `used` to the dual-phdr `used` for an otherwise-identical layout; they must be 808 # equal (no double-count). (Repro: dual-phdr was used=0x84 vs single 0x44.) 809 printf 'void _start(void){for(;;){}}\nint fydata=7;\n' > fymem.c 810 cat > fy_single.ld <<'LDEOF' 811 PHDRS { a PT_LOAD; } 812 MEMORY { RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 0x100000 } 813 SECTIONS { 814 . = 0x80000000; 815 .text : { *(.text*) } :a 816 .data : { *(.data*) } :a 817 .bss : { *(.bss*) } :a 818 } 819 LDEOF 820 cat > fy_dual.ld <<'LDEOF' 821 PHDRS { a PT_LOAD; b PT_LOAD; } 822 MEMORY { RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 0x100000 } 823 SECTIONS { 824 . = 0x80000000; 825 .text : { *(.text*) } :a :b 826 .data : { *(.data*) } :a 827 .bss : { *(.bss*) } :a 828 } 829 LDEOF 830 run_ok fy-memuse-single "$KIT" build-exe -target riscv64-none-elf -ffreestanding \ 831 -nostdlib -static -no-pie -T fy_single.ld --print-memory-usage fymem.c \ 832 -o fymem_single.elf 833 run_ok fy-memuse-dual "$KIT" build-exe -target riscv64-none-elf -ffreestanding \ 834 -nostdlib -static -no-pie -T fy_dual.ld --print-memory-usage fymem.c \ 835 -o fymem_dual.elf 836 fy_used_single=$(fy_memuse_used RAM "$work/fy-memuse-single.out") 837 fy_used_dual=$(fy_memuse_used RAM "$work/fy-memuse-dual.out") 838 if [ -n "$fy_used_single" ] && [ "$fy_used_single" = "$fy_used_dual" ]; then 839 ok fy-memuse-alias-not-doubled 840 else 841 { printf 'alias segment double-counts region usage\n'; 842 printf ' single-phdr used=%s\n' "$fy_used_single"; 843 printf ' dual-phdr used=%s (expected equal)\n' "$fy_used_dual"; 844 } > "$work/fy-memuse-alias-not-doubled.diag" 845 not_ok fy-memuse-alias-not-doubled "$work/fy-memuse-alias-not-doubled.diag" 846 fi 847 848 # ---- Bug 8: a MEMORY region touching the TOP of the address space 849 # (ORIGIN=0xFFFFFFFF80000000, LENGTH=0x80000000 → origin+length wraps to 0) must 850 # still attribute the image that lives inside it (nonzero used / nonzero %age). 851 # Under an ASan/UBSan build the linker-script hex parser traps on the 16-digit 852 # ORIGIN (a separate, pre-existing defect in src/link/link_script.c, owned by 853 # another agent); in that case the high-half script can't even be parsed, so we 854 # skip rather than fail for an unrelated reason. 855 cat > fy_high.ld <<'LDEOF' 856 MEMORY { RAM (rwx) : ORIGIN = 0xFFFFFFFF80000000, LENGTH = 0x80000000 } 857 SECTIONS { 858 . = 0xFFFFFFFF80000000; 859 .text : { *(.text*) } 860 .data : { *(.data*) } 861 .bss : { *(.bss*) } 862 } 863 LDEOF 864 if "$KIT" build-exe -target riscv64-none-elf -ffreestanding -nostdlib -static \ 865 -no-pie -T fy_high.ld --print-memory-usage fymem.c -o fymem_high.elf \ 866 > "$work/fy-memuse-high.out" 2> "$work/fy-memuse-high.err"; then 867 fy_used_high=$(fy_memuse_used RAM "$work/fy-memuse-high.out") 868 if [ -n "$fy_used_high" ] && [ "$fy_used_high" != "0x0" ]; then 869 ok fy-memuse-high-half-nonzero 870 else 871 { printf 'high-half region reports zero usage (origin+length wrap)\n'; 872 sed 's/^/out: /' "$work/fy-memuse-high.out"; 873 } > "$work/fy-memuse-high-half-nonzero.diag" 874 not_ok fy-memuse-high-half-nonzero "$work/fy-memuse-high-half-nonzero.diag" 875 fi 876 elif grep -q 'link_script.c' "$work/fy-memuse-high.err"; then 877 skip_test fy-memuse-high-half-nonzero \ 878 "linker-script hex parser traps on 16-digit ORIGIN (pre-existing UBSan in link_script.c)" 879 else 880 not_ok fy-memuse-high-half-nonzero "$work/fy-memuse-high.err" 881 fi 882 883 # ---- Bug 9: --cref must list imported (DSO-resolved) symbols and their 884 # referencing files. Two PIC objects reference libc_marker, resolved from the 885 # hand-built libc.so DSO fixture; libc_marker must appear in the cref table. 886 if [ -f "$repo_root/test/driver/fixtures/libc.so" ]; then 887 cp "$repo_root/test/driver/fixtures/libc.so" fy_libc.so 888 printf 'extern int libc_marker(void);\nint fy_use_a(void){return libc_marker();}\nvoid _start(void){fy_use_a();for(;;){}}\n' > fy_crefa.c 889 printf 'extern int libc_marker(void);\nint fy_use_b(void){return libc_marker()+1;}\n' > fy_crefb.c 890 run_ok fy-cref-obja "$KIT" build-obj -target x86_64-linux -fPIC fy_crefa.c -o fy_crefa.o 891 run_ok fy-cref-objb "$KIT" build-obj -target x86_64-linux -fPIC fy_crefb.c -o fy_crefb.o 892 run_ok fy-cref-link "$KIT" ld -pie --cref fy_cref.out \ 893 fy_crefa.o fy_crefb.o fy_libc.so -o fy_crefexe.elf 894 contains fy-cref-imported-symbol fy_cref.out "libc_marker" 895 # The imported symbol's def line should name the providing DSO (basename), 896 # never "-" and never an absolute host path. 897 if awk '/^libc_marker$/{f=1;next} f&&/^ def fy_libc.so$/{ok=1} f&&/^[^ ]/&&!/^libc_marker$/{f=0} END{exit !ok}' fy_cref.out; then 898 ok fy-cref-imported-def-dso 899 else 900 cp fy_cref.out "$work/fy-cref-imported-def-dso.diag" 901 not_ok fy-cref-imported-def-dso "$work/fy-cref-imported-def-dso.diag" 902 fi 903 else 904 skip_test fy-cref-imported-symbol "no libc.so fixture" 905 skip_test fy-cref-imported-def-dso "no libc.so fixture" 906 fi 907 # ==== BEGIN kernel-FZ Bug 11: cc honors -Wl link side-reports =============== 908 # cc routes -Wl,/-Xlinker report flags through driver_link_flags, but its link 909 # ran inside a build helper that created/emitted/freed the session internally, 910 # so --cref / --print-memory-usage / -Map / --symbols were silently dropped 911 # (exit 0, no output). cc must now write them from the live session, matching 912 # ld and build-exe. Reuses the xmain.c/xhelp.c cref fixtures above. 913 run_ok cc-wl-cref "$KIT" cc -target x86_64-linux -nostdlib -static -no-pie \ 914 -e _start -Wl,--cref=cc_cref.out xmain.c xhelp.c -o cc_cref.elf 915 assert_file_exists cc-wl-cref-file cc_cref.out 916 contains cc-wl-cref-header cc_cref.out "cross reference table" 917 contains cc-wl-cref-symbol cc_cref.out "xref_helper" 918 # --print-memory-usage emits the per-region summary to stdout (captured in .out). 919 run_ok cc-wl-memuse "$KIT" cc -target x86_64-linux -nostdlib -static -no-pie \ 920 -e _start -Wl,--print-memory-usage xmain.c xhelp.c -o cc_memuse.elf 921 contains cc-wl-memuse-header "$work/cc-wl-memuse.out" "Memory region" 922 # -Map / --symbols side files (the pre-existing silent drop) also get written. 923 run_ok cc-wl-map "$KIT" cc -target x86_64-linux -nostdlib -static -no-pie \ 924 -e _start -Wl,-Map=cc_map.out -Wl,--symbols=cc_sym.out \ 925 xmain.c xhelp.c -o cc_map.elf 926 assert_file_exists cc-wl-map-file cc_map.out 927 contains cc-wl-map-segments cc_map.out "segments" 928 assert_file_exists cc-wl-symbols-file cc_sym.out 929 contains cc-wl-symbols-start cc_sym.out "_start" 930 # ==== END kernel-FZ Bug 11 ================================================== 931 932 # ==== BEGIN kernel-FZ Bug 12: ld -Wl owned-string pool grows ================ 933 # A single -Wl,--section-start=...,...(many) token adds only 1 to argc but each 934 # --section-start minted up to THREE owned strings (name dup'd twice + addr), 935 # overrunning ld's fixed argc+32 owned-paths bound and failing valid input with 936 # "too many sysroot-expanded paths". The pool now grows independently of argc, 937 # and each --section-start consumes exactly two slots. Reuses no_pie_start.o. 938 ld_b12_tok="--section-start=.s00=0x10000" 939 ld_b12_i=1 940 while [ "$ld_b12_i" -le 19 ]; do 941 ld_b12_addr=$(printf '0x%x' $((0x10000 + ld_b12_i * 0x1000))) 942 ld_b12_tok="$ld_b12_tok,--section-start=.s$(printf '%02d' "$ld_b12_i")=$ld_b12_addr" 943 ld_b12_i=$((ld_b12_i + 1)) 944 done 945 # 20 --section-start entries in one -Wl token links successfully (was exit 2). 946 run_ok ld-wl-section-start-many "$KIT" ld -nostdlib -static -no-pie \ 947 -Ttext 0x500000 no_pie_start.o -Wl,"$ld_b12_tok" -o ld-secstart-many.elf 948 assert_file_exists ld-wl-section-start-many-file ld-secstart-many.elf 949 # A -Wl,--defsym burst (two owned strings per alias entry) likewise links. 950 run_ok ld-wl-defsym-many "$KIT" ld -nostdlib -static -no-pie \ 951 -Ttext 0x500000 no_pie_start.o \ 952 -Wl,--defsym=a0=_start,--defsym=a1=_start,--defsym=a2=_start,--defsym=a3=_start,--defsym=a4=_start,--defsym=a5=_start,--defsym=a6=_start,--defsym=a7=_start,--defsym=a8=_start,--defsym=a9=_start \ 953 -o ld-defsym-many.elf 954 assert_file_exists ld-wl-defsym-many-file ld-defsym-many.elf 955 # ==== END kernel-FZ Bug 12 ================================================== 956 957 kit_summary build-driver 958 kit_exit