kit

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

run.sh (50636B)


      1 #!/bin/sh
      2 # Driver-level checks for the byte-utility tools: xxd, cmp, hash, disas, mc,
      3 # compress.
      4 # Self-checking (no golden files): round-trips and known encodings/digests are
      5 # asserted inline via the shared kit_* verbs (ok/run_ok/run_fail/contains/
      6 # same_file) recorded over $work.
      7 
      8 set -u
      9 
     10 script_dir=$(cd "$(dirname "$0")" && pwd)
     11 repo_root=$(cd "$script_dir/../.." && pwd)
     12 
     13 KIT="${KIT:-$repo_root/build/kit}"
     14 
     15 if [ ! -x "$KIT" ]; then
     16     echo "tools: kit binary not found at $KIT" >&2
     17     exit 2
     18 fi
     19 
     20 work=$(mktemp -d "${TMPDIR:-/tmp}/kit-tools-test.XXXXXX")
     21 trap 'rm -rf "$work"' EXIT
     22 
     23 KIT_KIT_DIR="$repo_root/test/lib"
     24 . "$repo_root/test/lib/kit_sh_kit.sh"
     25 kit_report_init
     26 
     27 expect_status() { # WANT NAME COMMAND...
     28     es_want=$1
     29     es_name=$2
     30     shift 2
     31     "$@" > "$work/$es_name.out" 2> "$work/$es_name.err"
     32     es_got=$?
     33     if [ "$es_got" -eq "$es_want" ]; then
     34         ok "$es_name"
     35     else
     36         printf 'wanted %s, got %s\n' "$es_want" "$es_got" > "$work/$es_name.diag"
     37         cat "$work/$es_name.err" >> "$work/$es_name.diag"
     38         not_ok "$es_name" "$work/$es_name.diag"
     39     fi
     40 }
     41 
     42 # A fixture with text, newlines, NUL and high bytes — exercises the ASCII
     43 # column and binary round-tripping.
     44 printf 'Hello, kit!\nLine two.\n\000\001\377\376' > "$work/data.bin"
     45 
     46 # ---- xxd -------------------------------------------------------------------
     47 "$KIT" xxd "$work/data.bin" > "$work/dump.txt" 2> "$work/xxd.err"
     48 "$KIT" xxd -r "$work/dump.txt" > "$work/rt.bin" 2>> "$work/xxd.err"
     49 same_file xxd-roundtrip "$work/data.bin" "$work/rt.bin"
     50 
     51 "$KIT" xxd -p "$work/data.bin" > "$work/p.txt" 2>> "$work/xxd.err"
     52 "$KIT" xxd -r -p "$work/p.txt" > "$work/rtp.bin" 2>> "$work/xxd.err"
     53 same_file xxd-plain-roundtrip "$work/data.bin" "$work/rtp.bin"
     54 
     55 "$KIT" xxd -i "$work/data.bin" > "$work/inc.txt" 2>> "$work/xxd.err"
     56 contains xxd-include-array "$work/inc.txt" "unsigned char"
     57 contains xxd-include-len "$work/inc.txt" "_len = 26"
     58 
     59 # ---- cmp -------------------------------------------------------------------
     60 cp "$work/data.bin" "$work/data2.bin"
     61 run_ok cmp-identical "$KIT" cmp "$work/data.bin" "$work/data2.bin"
     62 
     63 printf 'Hello, kit!\nLine TWO.\n\000\001\377\376' > "$work/diff.bin"
     64 run_fail cmp-differ-exit "$KIT" cmp "$work/data.bin" "$work/diff.bin"
     65 "$KIT" cmp "$work/data.bin" "$work/diff.bin" > "$work/cmp.out" 2>&1
     66 contains cmp-differ-message "$work/cmp.out" "differ: char"
     67 run_fail cmp-silent-exit "$KIT" cmp -s "$work/data.bin" "$work/diff.bin"
     68 
     69 # GNU-compatible count/skip spellings, checked overflow, -- paths, and the
     70 # operational-vs-usage status split.
     71 printf 'abc' > "$work/cmp-short"
     72 printf 'zabcX' > "$work/cmp-prefixed"
     73 run_ok cmp-ignore-initial-long "$KIT" cmp --ignore-initial=0:1 --bytes=3 \
     74     "$work/cmp-short" "$work/cmp-prefixed"
     75 run_ok cmp-ignore-initial-short "$KIT" cmp -i0:1 -n3 \
     76     "$work/cmp-short" "$work/cmp-prefixed"
     77 expect_status 1 cmp-missing-operational "$KIT" cmp "$work/missing" \
     78     "$work/cmp-short"
     79 expect_status 1 cmp-silent-missing-operational "$KIT" cmp -s \
     80     "$work/missing" "$work/cmp-short"
     81 contains cmp-silent-missing-diagnostic \
     82     "$work/cmp-silent-missing-operational.err" "failed to read"
     83 if [ ! -s "$work/cmp-silent-missing-operational.out" ]; then
     84     ok cmp-silent-missing-stdout-empty
     85 else
     86     not_ok cmp-silent-missing-stdout-empty \
     87         "$work/cmp-silent-missing-operational.out"
     88 fi
     89 expect_status 2 cmp-count-overflow "$KIT" cmp --bytes=18446744073709551616 \
     90     "$work/cmp-short" "$work/cmp-short"
     91 cp "$work/cmp-short" "$work/-cmp-input"
     92 (cd "$work" && "$KIT" cmp -- -cmp-input cmp-short) \
     93     > "$work/cmp-dash.out" 2> "$work/cmp-dash.err"
     94 if [ "$?" -eq 0 ]; then ok cmp-double-dash; else
     95     not_ok cmp-double-dash "$work/cmp-dash.err"
     96 fi
     97 
     98 # ---- hash (CLI smoke; vectors covered by the test-hash unit test) ----------
     99 printf '123456789' > "$work/crc.in"
    100 "$KIT" hash -a crc32 "$work/crc.in" > "$work/crc.out" 2>&1
    101 contains hash-crc32 "$work/crc.out" "cbf43926"
    102 printf 'abc' | "$KIT" hash -a sha256 > "$work/sha.out" 2>&1
    103 contains hash-sha256 "$work/sha.out" \
    104     "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
    105 
    106 # Standard-named aliases pin the algorithm and match `hash -a` exactly.
    107 printf 'abc' | "$KIT" sha256sum > "$work/sha256sum.out" 2>&1
    108 contains alias-sha256sum "$work/sha256sum.out" \
    109     "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad  -"
    110 printf 'abc' | "$KIT" b2sum > "$work/b2sum.out" 2>&1
    111 printf 'abc' | "$KIT" hash -a blake2b > "$work/b2ref.out" 2>&1
    112 same_file alias-b2sum-matches-hash "$work/b2sum.out" "$work/b2ref.out"
    113 "$KIT" crc32 "$work/crc.in" > "$work/crc32.out" 2>&1
    114 contains alias-crc32 "$work/crc32.out" "cbf43926"
    115 # A pinned alias rejects -a (its algorithm is fixed).
    116 run_fail alias-sha256sum-no-a "$KIT" sha256sum -a blake2b "$work/crc.in"
    117 
    118 # Check manifests use the same generic hashing path, enforce exact 256-bit
    119 # widths, and preserve the 0/1/2 contract.
    120 printf 'manifest payload\n' > "$work/check payload.bin"
    121 "$KIT" sha256sum "$work/check payload.bin" > "$work/sha256.manifest"
    122 expect_status 0 sha256-check-ok "$KIT" sha256sum -c "$work/sha256.manifest"
    123 contains sha256-check-ok-row "$work/sha256-check-ok.out" ": OK"
    124 expect_status 0 sha256-check-manifest-stdin sh -c \
    125     '"$1" sha256sum -c - < "$2"' sh "$KIT" "$work/sha256.manifest"
    126 sed 's/  / */' "$work/sha256.manifest" > "$work/sha256.binary.manifest"
    127 expect_status 0 sha256-check-binary-mode "$KIT" sha256sum -c \
    128     "$work/sha256.binary.manifest"
    129 expect_status 0 sha256-check-quiet "$KIT" sha256sum -c --quiet \
    130     "$work/sha256.manifest"
    131 if [ ! -s "$work/sha256-check-quiet.out" ]; then ok sha256-check-quiet-empty;
    132 else not_ok sha256-check-quiet-empty "$work/sha256-check-quiet.out"; fi
    133 printf 'changed\n' > "$work/check payload.bin"
    134 expect_status 1 sha256-check-mismatch "$KIT" sha256sum --check \
    135     "$work/sha256.manifest"
    136 contains sha256-check-failed-row "$work/sha256-check-mismatch.out" ": FAILED"
    137 expect_status 1 sha256-check-status "$KIT" sha256sum -c --status \
    138     "$work/sha256.manifest"
    139 if [ ! -s "$work/sha256-check-status.out" ]; then ok sha256-check-status-empty;
    140 else not_ok sha256-check-status-empty "$work/sha256-check-status.out"; fi
    141 printf 'not a manifest\n' > "$work/malformed.manifest"
    142 expect_status 1 sha256-check-malformed "$KIT" sha256sum -c \
    143     "$work/malformed.manifest"
    144 printf '%063d  %s\n' 0 "$work/check payload.bin" > "$work/short.manifest"
    145 expect_status 1 sha256-check-exact-width "$KIT" sha256sum -c \
    146     "$work/short.manifest"
    147 sed 's#check payload\.bin#missing-payload.bin#' "$work/sha256.manifest" \
    148     > "$work/unreadable.manifest"
    149 expect_status 1 sha256-check-unreadable "$KIT" sha256sum -c \
    150     "$work/unreadable.manifest"
    151 expect_status 2 crc32-check-rejected "$KIT" crc32 -c "$work/sha256.manifest"
    152 
    153 printf 'manifest payload\n' > "$work/check payload.bin"
    154 "$KIT" b2sum "$work/check payload.bin" > "$work/b2.manifest"
    155 expect_status 0 b2-check-ok "$KIT" b2sum -c "$work/b2.manifest"
    156 printf '%0128d  %s\n' 0 "$work/check payload.bin" > "$work/b2-512.manifest"
    157 expect_status 1 b2-check-reject-512 "$KIT" b2sum -c "$work/b2-512.manifest"
    158 contains b2-check-reject-512-message "$work/b2-check-reject-512.err" \
    159     "BLAKE2b-512 manifest is incompatible"
    160 
    161 sha_stdin=$("$KIT" sha256sum "$work/check payload.bin" | sed 's/  .*//')
    162 printf '%s  -\n' "$sha_stdin" > "$work/stdin.manifest"
    163 expect_status 1 sha256-check-dual-stdin sh -c \
    164     '"$1" sha256sum -c - < "$2"' sh "$KIT" "$work/stdin.manifest"
    165 contains sha256-check-dual-message "$work/sha256-check-dual-stdin.err" \
    166     "manifest and payload cannot both read standard input"
    167 
    168 # Installed multicall aliases exercise argv[0] routing as well as `kit TOOL`.
    169 ln -s "$KIT" "$work/sha256sum"
    170 expect_status 0 sha256-check-installed "$work/sha256sum" -c \
    171     "$work/sha256.manifest"
    172 
    173 # Shared bounded suggestions stay on stderr, retain usage status, and never
    174 # autocorrect the input.
    175 expect_status 2 suggest-command "$KIT" objdmp
    176 contains suggest-command-value "$work/suggest-command.err" "did you mean 'objdump'"
    177 if [ ! -s "$work/suggest-command.out" ]; then ok suggest-command-stdout-empty;
    178 else not_ok suggest-command-stdout-empty "$work/suggest-command.out"; fi
    179 expect_status 2 suggest-cc-option "$KIT" cc --hlep
    180 contains suggest-cc-option-value "$work/suggest-cc-option.err" "did you mean '--help'"
    181 expect_status 2 suggest-target "$KIT" cc -target aarch65-none-elf -c missing.c
    182 contains suggest-target-value "$work/suggest-target.err" "did you mean"
    183 expect_status 2 suggest-target-discovery "$KIT" cc -target aardvark-none-elf \
    184     -c missing.c
    185 contains suggest-target-discovery-value "$work/suggest-target-discovery.err" \
    186     "kit targets"
    187 expect_status 2 suggest-language "$KIT" build-obj -x see missing.c
    188 contains suggest-language-value "$work/suggest-language.err" "did you mean"
    189 
    190 # Canonical version output and all help routes agree; version remains internal
    191 # (the install aliases are tested by the install suite).
    192 "$KIT" --version > "$work/version-top"
    193 "$KIT" version > "$work/version-command"
    194 same_file version-command-identical "$work/version-top" "$work/version-command"
    195 expect_status 0 version-help-route "$KIT" help version
    196 expect_status 0 version-short-help "$KIT" version -h
    197 expect_status 0 version-long-help "$KIT" version --help
    198 expect_status 2 version-not-installable "$KIT" install \
    199     "$work/version aliases" version
    200 if [ ! -e "$work/version aliases/version" ]; then ok version-alias-absent;
    201 else not_ok version-alias-absent; fi
    202 
    203 # Raw assembler stdin uses a stable synthetic source name. Exercise direct and
    204 # installed argv[0] dispatch, plus `--` for a leading-dash source pathname.
    205 run_ok conventional-install-aliases "$KIT" install \
    206     "$work/tool aliases" as cc
    207 mkdir -p "$work/dash paths"
    208 printf '.text\n.globl _start\n_start:\n  ret\n' | \
    209     "$KIT" as -target x86_64-none-elf -o "$work/stdin-direct.o" - \
    210     > "$work/as-stdin-direct.out" 2> "$work/as-stdin-direct.err"
    211 if [ "$?" -eq 0 ]; then ok as-stdin-direct; else
    212     not_ok as-stdin-direct "$work/as-stdin-direct.err"
    213 fi
    214 printf '.text\n.globl _start\n_start:\n  ret\n' | \
    215     "$work/tool aliases/as" -target x86_64-none-elf \
    216     -o "$work/stdin-alias.o" - \
    217     > "$work/as-stdin-alias.out" 2> "$work/as-stdin-alias.err"
    218 if [ "$?" -eq 0 ]; then ok as-stdin-installed; else
    219     not_ok as-stdin-installed "$work/as-stdin-alias.err"
    220 fi
    221 same_file as-stdin-direct-installed "$work/stdin-direct.o" \
    222     "$work/stdin-alias.o"
    223 printf 'definitely_not_an_instruction\n' | \
    224     "$KIT" as -target x86_64-none-elf -o "$work/stdin-bad.o" - \
    225     > "$work/as-stdin-bad.out" 2> "$work/as-stdin-bad.err"
    226 as_bad_rc=$?
    227 if [ "$as_bad_rc" -eq 1 ]; then ok as-stdin-error-status; else
    228     printf 'status=%s\n' "$as_bad_rc" > "$work/as-stdin-bad.diag"
    229     not_ok as-stdin-error-status "$work/as-stdin-bad.diag"
    230 fi
    231 contains as-stdin-diagnostic-name "$work/as-stdin-bad.err" "<stdin>"
    232 printf '.text\n.globl dash_source\ndash_source:\n  ret\n' \
    233     > "$work/dash paths/-source.s"
    234 (cd "$work/dash paths" && "$KIT" as -target x86_64-none-elf \
    235     -o dash-source.o -- -source.s) \
    236     > "$work/as-dash.out" 2> "$work/as-dash.err"
    237 if [ "$?" -eq 0 ]; then ok as-double-dash; else
    238     not_ok as-double-dash "$work/as-dash.err"
    239 fi
    240 
    241 # Every supported compiler-to-linker spelling must produce the same linked
    242 # image. The script lives at a path containing spaces and a leading-dash
    243 # basename so comma forwarding and argv pairing are both exercised.
    244 mkdir -p "$work/link scripts"
    245 link_script="$work/link scripts/-layout script.lds"
    246 cat > "$link_script" <<'EOF'
    247 ENTRY(_start)
    248 SECTIONS {
    249   . = 0x10000;
    250   .text : ALIGN(1) { *(.text .text.*) }
    251   /DISCARD/ : { *(.note.*) *(.comment) *(.eh_frame) }
    252 }
    253 EOF
    254 link_common='-target x86_64-none-elf -nostdlib -static -no-pie'
    255 # shellcheck disable=SC2086 -- intentionally expand the fixed common argv.
    256 run_ok link-flags-separate "$KIT" cc $link_common -T "$link_script" \
    257     -e _start "$work/stdin-direct.o" -o "$work/link-separate.elf"
    258 # shellcheck disable=SC2086
    259 run_ok link-flags-joined "$KIT" cc $link_common -T"$link_script" \
    260     -e_start "$work/stdin-direct.o" -o "$work/link-joined.elf"
    261 # shellcheck disable=SC2086
    262 run_ok link-flags-equal "$KIT" cc $link_common --script="$link_script" \
    263     --entry=_start "$work/stdin-direct.o" -o "$work/link-equal.elf"
    264 # shellcheck disable=SC2086
    265 run_ok link-flags-wl "$KIT" cc $link_common \
    266     "-Wl,-T,$link_script,-e,_start" "$work/stdin-direct.o" \
    267     -o "$work/link-wl.elf"
    268 # shellcheck disable=SC2086
    269 run_ok link-flags-xlinker "$KIT" cc $link_common \
    270     -Xlinker -T -Xlinker "$link_script" -Xlinker -e -Xlinker _start \
    271     "$work/stdin-direct.o" -o "$work/link-xlinker.elf"
    272 # shellcheck disable=SC2086
    273 run_ok link-flags-installed "$work/tool aliases/cc" $link_common \
    274     "-Wl,-T,$link_script,-e,_start" "$work/stdin-direct.o" \
    275     -o "$work/link-installed.elf"
    276 for linked in joined equal wl xlinker installed; do
    277     same_file "link-flags-identical-$linked" "$work/link-separate.elf" \
    278         "$work/link-$linked.elf"
    279 done
    280 
    281 # `--` is shared by the file-oriented byte utilities, including argv[0]
    282 # multicall aliases and leading-dash paths in a directory containing spaces.
    283 mkdir -p "$work/dash paths"
    284 printf 'printable payload\n' > "$work/dash paths/-payload.bin"
    285 (cd "$work/dash paths" && "$KIT" strings -- -payload.bin) \
    286     > "$work/strings-dash.out" 2> "$work/strings-dash.err"
    287 contains strings-double-dash "$work/strings-dash.out" "printable payload"
    288 ln -s "$KIT" "$work/strings"
    289 (cd "$work/dash paths" && "$work/strings" -- -payload.bin) \
    290     > "$work/strings-alias-dash.out" 2> "$work/strings-alias-dash.err"
    291 contains strings-alias-double-dash "$work/strings-alias-dash.out" \
    292     "printable payload"
    293 (cd "$work/dash paths" && "$KIT" xxd -p -- -payload.bin) \
    294     > "$work/xxd-dash.out" 2> "$work/xxd-dash.err"
    295 contains xxd-double-dash "$work/xxd-dash.out" "7072696e7461626c65207061796c6f6164"
    296 printf '\303' > "$work/dash paths/-code.bin"
    297 (cd "$work/dash paths" && "$KIT" disas -target x86_64 -- -code.bin) \
    298     > "$work/disas-dash.out" 2> "$work/disas-dash.err"
    299 contains disas-double-dash "$work/disas-dash.out" "ret"
    300 
    301 # ---- disas -----------------------------------------------------------------
    302 "$KIT" disas -target aarch64 -x "1f 20 03 d5" > "$work/d-aa64.out" 2>&1
    303 contains disas-aa64-nop "$work/d-aa64.out" "nop"
    304 "$KIT" disas -target x86_64 -x "c3" > "$work/d-x64.out" 2>&1
    305 contains disas-x64-ret "$work/d-x64.out" "ret"
    306 run_fail disas-bad-hex "$KIT" disas -target aarch64 -x "zz"
    307 
    308 # ---- mc --------------------------------------------------------------------
    309 "$KIT" mc -target aarch64 "add x0, x1, x2" > "$work/mc.out" 2>&1
    310 contains mc-aa64-encoding "$work/mc.out" "encoding: [0x20,0x00,0x02,0x8b]"
    311 
    312 # mc -p (raw hex) piped back through disas must recover the mnemonic.
    313 "$KIT" mc -target aarch64 -p "add x0, x1, x2" > "$work/mc.hex" 2>&1
    314 "$KIT" disas -target aarch64 -x "$(cat "$work/mc.hex")" > "$work/mc-rt.out" 2>&1
    315 contains mc-disas-roundtrip "$work/mc-rt.out" "add x0, x1, x2"
    316 
    317 # A branch to an undefined symbol should surface a relocation, not an error.
    318 "$KIT" mc -target aarch64 "b somewhere" > "$work/mc-rel.out" 2>&1
    319 contains mc-reloc "$work/mc-rel.out" "reloc"
    320 
    321 # ---- compress --------------------------------------------------------------
    322 # gzip + LZ4-frame round-trips (text, empty, binary), -o files, auto-detect,
    323 # error exits, and interop with the system gzip/lz4 CLIs when present.
    324 printf 'hello compression world\nthe quick brown fox jumps\n' > "$work/cz.txt"
    325 : > "$work/cz.empty"
    326 head -c 200000 "$KIT" > "$work/cz.bin" # a real, varied, deterministic blob
    327 
    328 cz_roundtrip() { # FMT LABEL FILE : compress | decompress | compare original
    329     "$KIT" compress -z "$1" "$3" 2> "$work/cz.err" \
    330         | "$KIT" compress -d 2>> "$work/cz.err" > "$work/cz.rt"
    331     same_file "compress-roundtrip-$2" "$3" "$work/cz.rt"
    332 }
    333 for f in txt empty bin; do
    334     cz_roundtrip gzip "gzip-$f" "$work/cz.$f"
    335     cz_roundtrip lz4 "lz4-$f" "$work/cz.$f"
    336 done
    337 
    338 # File input + -o output, then decode.
    339 "$KIT" compress -z gzip -o "$work/cz.gz" "$work/cz.txt"
    340 "$KIT" compress -d -o "$work/cz.ungz" "$work/cz.gz"
    341 same_file compress-file-out-gzip "$work/cz.txt" "$work/cz.ungz"
    342 
    343 # Auto-detect on decompress (no -z) from the container magic bytes.
    344 for fmt in gzip lz4; do
    345     "$KIT" compress -z "$fmt" "$work/cz.txt" | "$KIT" compress -d > "$work/cz.det"
    346     same_file "compress-autodetect-$fmt" "$work/cz.txt" "$work/cz.det"
    347 done
    348 
    349 # Default format is gzip.
    350 "$KIT" compress "$work/cz.txt" | "$KIT" compress -d > "$work/cz.def"
    351 same_file compress-default-gzip "$work/cz.txt" "$work/cz.def"
    352 
    353 # Standard-named aliases: gzip/gunzip and lz4 round-trip through each other and
    354 # through the generic tool; gunzip is just `compress -d` pinned to gzip.
    355 "$KIT" gzip "$work/cz.txt" | "$KIT" gunzip > "$work/cz.gzalias"
    356 same_file alias-gzip-gunzip "$work/cz.txt" "$work/cz.gzalias"
    357 "$KIT" gzip "$work/cz.txt" | "$KIT" compress -d > "$work/cz.gz2generic"
    358 same_file alias-gzip-to-generic "$work/cz.txt" "$work/cz.gz2generic"
    359 "$KIT" lz4 "$work/cz.txt" | "$KIT" lz4 -d > "$work/cz.lz4alias"
    360 same_file alias-lz4-roundtrip "$work/cz.txt" "$work/cz.lz4alias"
    361 "$KIT" lz4c "$work/cz.txt" | "$KIT" lz4 -d > "$work/cz.lz4calias"
    362 same_file alias-lz4c-roundtrip "$work/cz.txt" "$work/cz.lz4calias"
    363 # gzip -d flips direction; the common gzip flags (-c, level -9) are no-ops.
    364 "$KIT" gzip -9 -c "$work/cz.txt" | "$KIT" gzip -d > "$work/cz.compat"
    365 same_file alias-gzip-compat-flags "$work/cz.txt" "$work/cz.compat"
    366 # A pinned alias rejects -z, and gunzip will not auto-detect a foreign container.
    367 run_fail alias-gzip-no-z "$KIT" gzip -z lz4 "$work/cz.txt"
    368 "$KIT" lz4 "$work/cz.txt" > "$work/cz.lz4only"
    369 run_fail alias-gunzip-wrong-format "$KIT" gunzip "$work/cz.lz4only"
    370 
    371 # Error exits: bad format / two inputs / missing file all exit non-zero.
    372 run_fail compress-err-bad-format "$KIT" compress -z bogus "$work/cz.txt"
    373 expect_status 2 compress-format-suggestion "$KIT" compress -z gzi "$work/cz.txt"
    374 contains compress-format-suggestion-value "$work/compress-format-suggestion.err" \
    375     "did you mean 'gzip'"
    376 run_fail compress-err-two-inputs "$KIT" compress "$work/cz.txt" "$work/cz.empty"
    377 run_fail compress-err-missing-file "$KIT" compress "$work/cz.nope"
    378 if printf 'plain text not compressed' | "$KIT" compress -d >/dev/null 2>&1; then
    379     not_ok compress-err-undetectable
    380 else
    381     ok compress-err-undetectable
    382 fi
    383 
    384 # Interop with the system tools, both directions; skip cleanly when absent.
    385 if command -v gzip >/dev/null 2>&1 && command -v gunzip >/dev/null 2>&1; then
    386     "$KIT" compress -z gzip "$work/cz.bin" | gunzip 2>/dev/null > "$work/cz.gunzip"
    387     same_file compress-interop-kit-to-gunzip "$work/cz.bin" "$work/cz.gunzip"
    388     gzip -c "$work/cz.bin" | "$KIT" compress -d > "$work/cz.fromgzip"
    389     same_file compress-interop-gzip-to-kit "$work/cz.bin" "$work/cz.fromgzip"
    390 else
    391     kit_skip_na compress-interop-gzip "system gzip/gunzip not found"
    392 fi
    393 if command -v lz4 >/dev/null 2>&1; then
    394     "$KIT" compress -z lz4 "$work/cz.bin" | lz4 -d 2>/dev/null > "$work/cz.unlz4"
    395     same_file compress-interop-kit-to-lz4 "$work/cz.bin" "$work/cz.unlz4"
    396     lz4 -c "$work/cz.bin" 2>/dev/null | "$KIT" compress -d > "$work/cz.fromlz4"
    397     same_file compress-interop-lz4-to-kit "$work/cz.bin" "$work/cz.fromlz4"
    398 else
    399     kit_skip_na compress-interop-lz4 "system lz4 not found"
    400 fi
    401 
    402 # ---- image ---------------------------------------------------------------
    403 expect_status 2 image-format-suggestion "$KIT" image --format bing missing.o
    404 contains image-format-suggestion-value "$work/image-format-suggestion.err" \
    405     "did you mean 'bin'"
    406 cat > "$work/kernel.s" <<'EOF'
    407 .globl _start
    408 .section .text
    409 _start:
    410   ret
    411 .section .rodata
    412   .byte 0x11, 0x22, 0x33, 0x44
    413 EOF
    414 
    415 run_ok image-build-elf "$KIT" build-exe -target x86_64-none-elf \
    416     -nostdlib -static -no-pie -e _start "$work/kernel.s" -o "$work/kernel.elf"
    417 run_ok image-bin "$KIT" image --format bin "$work/kernel.elf" \
    418     -o "$work/kernel.bin"
    419 cp "$work/kernel.elf" "$work/-kernel.elf"
    420 (cd "$work" && "$KIT" image --format bin -o kernel.dash.bin -- -kernel.elf) \
    421     > "$work/image-dash.out" 2> "$work/image-dash.err"
    422 if [ "$?" -eq 0 ]; then ok image-double-dash; else
    423     not_ok image-double-dash "$work/image-dash.err"
    424 fi
    425 same_file image-double-dash-bytes "$work/kernel.bin" "$work/kernel.dash.bin"
    426 cp "$work/kernel.elf" "$work/-size.elf"
    427 (cd "$work" && "$KIT" size -- -size.elf) \
    428     > "$work/size-dash.out" 2> "$work/size-dash.err"
    429 if [ "$?" -eq 0 ] && grep -q 'text' "$work/size-dash.out"; then
    430     ok size-double-dash
    431 else
    432     not_ok size-double-dash "$work/size-dash.err"
    433 fi
    434 ln -s "$KIT" "$work/size"
    435 (cd "$work" && ./size -- -size.elf) \
    436     > "$work/size-alias-dash.out" 2> "$work/size-alias-dash.err"
    437 if [ "$?" -eq 0 ] && grep -q 'text' "$work/size-alias-dash.out"; then
    438     ok size-alias-double-dash
    439 else
    440     not_ok size-alias-double-dash "$work/size-alias-dash.err"
    441 fi
    442 run_ok image-paddr "$KIT" image --format bin --addr paddr "$work/kernel.elf" \
    443     -o "$work/kernel.paddr.bin"
    444 same_file image-paddr-matches-vaddr "$work/kernel.bin" "$work/kernel.paddr.bin"
    445 run_ok objcopy-binary "$KIT" objcopy -O binary "$work/kernel.elf" \
    446     "$work/kernel.objcopy.bin"
    447 same_file image-objcopy-binary "$work/kernel.bin" "$work/kernel.objcopy.bin"
    448 
    449 # Linked Mach-O and PE inputs must take the loader-segment path. Keep the
    450 # segment-selected output beside an explicitly section-selected output: the
    451 # former includes loader-owned bytes/alignment and is therefore observably
    452 # larger than the logical text section. Default objcopy raw output is required
    453 # to agree byte-for-byte with the image command over the same linked image.
    454 cat > "$work/linked-cross.c" <<'EOF'
    455 volatile unsigned long linked_data = 0x11223344UL;
    456 const unsigned char linked_rodata[] = { 0x55, 0x66, 0x77, 0x88 };
    457 void _start(void) {
    458   linked_data += linked_rodata[0];
    459   for (;;) {}
    460 }
    461 EOF
    462 
    463 run_ok image-linked-macho-build "$KIT" cc -target x86_64-macos \
    464     -ffreestanding -fno-stack-protector -nostdlib -Wl,-e,_start \
    465     "$work/linked-cross.c" -o "$work/linked.macho"
    466 run_ok image-linked-macho "$KIT" image --format bin \
    467     --metadata "$work/linked.macho.json" "$work/linked.macho" \
    468     -o "$work/linked.macho.bin"
    469 contains image-linked-macho-source "$work/linked.macho.json" \
    470     '"source": "segments"'
    471 contains image-linked-macho-format "$work/linked.macho.json" \
    472     '"object_format": "macho"'
    473 run_ok image-linked-macho-objcopy "$KIT" objcopy -O binary \
    474     "$work/linked.macho" "$work/linked.macho.objcopy.bin"
    475 same_file image-linked-macho-objcopy-bytes "$work/linked.macho.bin" \
    476     "$work/linked.macho.objcopy.bin"
    477 run_ok image-linked-macho-text-segment "$KIT" image --format bin \
    478     --segment __TEXT --metadata "$work/linked.macho.text.json" \
    479     "$work/linked.macho" -o "$work/linked.macho.text.bin"
    480 contains image-linked-macho-text-selection "$work/linked.macho.text.json" \
    481     '"selection": ["__TEXT"]'
    482 contains image-linked-macho-text-load-address \
    483     "$work/linked.macho.text.json" '"base_is_load_address": true'
    484 run_ok image-linked-macho-text-section "$KIT" image --format bin \
    485     --only-section __TEXT,__text "$work/linked.macho" \
    486     -o "$work/linked.macho.section.bin"
    487 macho_segment_size=$(wc -c < "$work/linked.macho.text.bin" | tr -d ' ')
    488 macho_section_size=$(wc -c < "$work/linked.macho.section.bin" | tr -d ' ')
    489 if [ "$macho_segment_size" -gt "$macho_section_size" ]; then
    490     ok image-linked-macho-segment-span
    491 else
    492     printf 'segment=%s section=%s\n' "$macho_segment_size" \
    493         "$macho_section_size" > "$work/image-linked-macho-segment-span.diag"
    494     not_ok image-linked-macho-segment-span \
    495         "$work/image-linked-macho-segment-span.diag"
    496 fi
    497 head -c 4 "$work/linked.macho.text.bin" > "$work/linked.macho.magic"
    498 "$KIT" xxd -p "$work/linked.macho.magic" > "$work/linked.macho.magic.hex"
    499 contains image-linked-macho-segment-header "$work/linked.macho.magic.hex" \
    500     "cffaedfe"
    501 
    502 run_ok image-linked-pe-build "$KIT" cc -target x86_64-windows \
    503     -ffreestanding -fno-stack-protector -nostdlib -Wl,-e,_start \
    504     "$work/linked-cross.c" -o "$work/linked.exe"
    505 run_ok image-linked-pe "$KIT" image --format bin \
    506     --metadata "$work/linked.pe.json" "$work/linked.exe" \
    507     -o "$work/linked.pe.bin"
    508 contains image-linked-pe-source "$work/linked.pe.json" \
    509     '"source": "segments"'
    510 contains image-linked-pe-format "$work/linked.pe.json" \
    511     '"object_format": "coff"'
    512 run_ok image-linked-pe-objcopy "$KIT" objcopy -O binary \
    513     "$work/linked.exe" "$work/linked.pe.objcopy.bin"
    514 same_file image-linked-pe-objcopy-bytes "$work/linked.pe.bin" \
    515     "$work/linked.pe.objcopy.bin"
    516 run_ok image-linked-pe-text-segment "$KIT" image --format bin \
    517     --segment .text --metadata "$work/linked.pe.text.json" \
    518     "$work/linked.exe" -o "$work/linked.pe.text.bin"
    519 contains image-linked-pe-text-selection "$work/linked.pe.text.json" \
    520     '"selection": [".text"]'
    521 contains image-linked-pe-text-load-address "$work/linked.pe.text.json" \
    522     '"base_is_load_address": true'
    523 run_ok image-linked-pe-text-section "$KIT" image --format bin \
    524     --only-section .text "$work/linked.exe" \
    525     -o "$work/linked.pe.section.bin"
    526 pe_segment_size=$(wc -c < "$work/linked.pe.text.bin" | tr -d ' ')
    527 pe_section_size=$(wc -c < "$work/linked.pe.section.bin" | tr -d ' ')
    528 if [ "$pe_segment_size" -gt "$pe_section_size" ]; then
    529     ok image-linked-pe-segment-span
    530 else
    531     printf 'segment=%s section=%s\n' "$pe_segment_size" \
    532         "$pe_section_size" > "$work/image-linked-pe-segment-span.diag"
    533     not_ok image-linked-pe-segment-span \
    534         "$work/image-linked-pe-segment-span.diag"
    535 fi
    536 
    537 run_ok image-ihex "$KIT" image --format ihex "$work/kernel.elf" \
    538     -o "$work/kernel.hex"
    539 run_ok objcopy-ihex "$KIT" objcopy -O ihex "$work/kernel.elf" \
    540     "$work/kernel.objcopy.hex"
    541 same_file image-objcopy-ihex "$work/kernel.hex" "$work/kernel.objcopy.hex"
    542 contains image-ihex-eof "$work/kernel.hex" ":00000001FF"
    543 run_ok image-srec "$KIT" image --format srec "$work/kernel.elf" \
    544     -o "$work/kernel.srec"
    545 run_ok objcopy-srec "$KIT" objcopy -O srec "$work/kernel.elf" \
    546     "$work/kernel.objcopy.srec"
    547 same_file image-objcopy-srec "$work/kernel.srec" "$work/kernel.objcopy.srec"
    548 contains image-srec-header "$work/kernel.srec" "S00600004B495411"
    549 if [ -s "$work/kernel.bin" ]; then
    550     ok image-bin-nonempty
    551 else
    552     not_ok image-bin-nonempty
    553 fi
    554 
    555 image_size=$(wc -c < "$work/kernel.bin" | tr -d ' ')
    556 pad_size=$((image_size + 16))
    557 run_ok image-pad-fill "$KIT" image --format bin --pad-to "$pad_size" \
    558     --fill 0xaa "$work/kernel.elf" -o "$work/kernel.pad.bin"
    559 pad_got=$(wc -c < "$work/kernel.pad.bin" | tr -d ' ')
    560 if [ "$pad_got" = "$pad_size" ]; then
    561     ok image-pad-size
    562 else
    563     echo "got $pad_got want $pad_size" > "$work/image-pad-size.diag"
    564     not_ok image-pad-size "$work/image-pad-size.diag"
    565 fi
    566 tail -c 16 "$work/kernel.pad.bin" > "$work/kernel.pad.tail"
    567 "$KIT" xxd -p "$work/kernel.pad.tail" > "$work/kernel.pad.hex"
    568 contains image-pad-fill-byte "$work/kernel.pad.hex" \
    569     "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
    570 run_fail image-max-size "$KIT" image --format bin --max-size 1 \
    571     "$work/kernel.elf" -o "$work/kernel.too-small.bin"
    572 
    573 # Text image formats must carry full 32-bit addresses. This fixture is placed
    574 # above 16 MiB so Intel HEX needs a non-trivial extended-linear-address record;
    575 # the checksum pins the full 16-bit high address (0x0102), not just its low byte.
    576 cat > "$work/highaddr.s" <<'EOF'
    577 .globl _start
    578 .section .text
    579 _start:
    580   ret
    581   .byte 0x11, 0x22, 0x33, 0x44
    582 EOF
    583 cat > "$work/highaddr.lds" <<'EOF'
    584 ENTRY(_start)
    585 SECTIONS {
    586   . = 0x010203f0;
    587   .text : ALIGN(1) { *(.text .text.*) }
    588   /DISCARD/ : { *(.note.*) *(.comment) *(.eh_frame) }
    589 }
    590 EOF
    591 run_ok image-text-high-build "$KIT" build-exe -target x86_64-none-elf \
    592     -nostdlib -static -no-pie -e _start -T "$work/highaddr.lds" \
    593     "$work/highaddr.s" -o "$work/highaddr.elf"
    594 run_ok image-ihex-high "$KIT" image --format ihex "$work/highaddr.elf" \
    595     -o "$work/highaddr.hex"
    596 contains image-ihex-high-ela "$work/highaddr.hex" ":020000040102F7"
    597 run_ok objcopy-ihex-high "$KIT" objcopy -O ihex "$work/highaddr.elf" \
    598     "$work/highaddr.objcopy.hex"
    599 same_file image-objcopy-ihex-high "$work/highaddr.hex" \
    600     "$work/highaddr.objcopy.hex"
    601 run_ok image-srec-high "$KIT" image --format srec "$work/highaddr.elf" \
    602     -o "$work/highaddr.srec"
    603 contains image-srec-high-address "$work/highaddr.srec" "010203F0"
    604 run_ok objcopy-srec-high "$KIT" objcopy -O srec "$work/highaddr.elf" \
    605     "$work/highaddr.objcopy.srec"
    606 same_file image-objcopy-srec-high "$work/highaddr.srec" \
    607     "$work/highaddr.objcopy.srec"
    608 
    609 cat > "$work/over32.lds" <<'EOF'
    610 ENTRY(_start)
    611 SECTIONS {
    612   . = 0x100010000;
    613   .text : ALIGN(1) { *(.text .text.*) }
    614   /DISCARD/ : { *(.note.*) *(.comment) *(.eh_frame) }
    615 }
    616 EOF
    617 run_ok image-text-over32-build "$KIT" build-exe -target x86_64-none-elf \
    618     -nostdlib -static -no-pie -e _start -T "$work/over32.lds" \
    619     "$work/highaddr.s" -o "$work/over32.elf"
    620 run_fail image-ihex-over32 "$KIT" image --format ihex "$work/over32.elf" \
    621     -o "$work/over32.hex"
    622 run_fail image-srec-over32 "$KIT" image --format srec "$work/over32.elf" \
    623     -o "$work/over32.srec"
    624 
    625 # A signed bias applies to records and the declared entry alike. Rebasing this
    626 # image down by exactly 4 GiB makes both text formats representable; pin their
    627 # entry records as well as the data-address path.
    628 run_ok image-ihex-negative-bias "$KIT" image --format ihex \
    629     --bias -4294967296 --metadata "$work/biased.json" "$work/over32.elf" \
    630     -o "$work/biased.hex"
    631 contains image-ihex-biased-ela "$work/biased.hex" ":020000040001F9"
    632 contains image-ihex-biased-entry "$work/biased.hex" ":0400000500010000F6"
    633 contains image-meta-original-base "$work/biased.json" \
    634     '"original_base": "0x100010000"'
    635 contains image-meta-emitted-base "$work/biased.json" \
    636     '"emitted_base": "0x10000"'
    637 contains image-meta-original-entry "$work/biased.json" \
    638     '"original_entry": "0x100010000"'
    639 contains image-meta-emitted-entry "$work/biased.json" \
    640     '"emitted_entry": "0x10000"'
    641 run_ok image-srec-negative-bias "$KIT" image --format srec \
    642     --bias -4294967296 "$work/over32.elf" -o "$work/biased.srec"
    643 contains image-srec-biased-entry "$work/biased.srec" "S804010000FA"
    644 expect_status 2 image-ihex-base-rejected "$KIT" image --format ihex \
    645     --base 0 "$work/highaddr.elf" -o "$work/base.hex"
    646 contains image-ihex-base-hint "$work/image-ihex-base-rejected.err" \
    647     "rebase IHEX/SREC addresses"
    648 run_fail image-negative-bias-underflow "$KIT" image --format ihex \
    649     --bias -9223372036854775808 "$work/kernel.elf" -o "$work/underflow.hex"
    650 if [ ! -e "$work/underflow.hex" ]; then ok image-underflow-transaction;
    651 else not_ok image-underflow-transaction "$work/underflow.hex"; fi
    652 
    653 # The top 32-bit byte is representable; a two-byte range at the same address
    654 # crosses the boundary and must fail before an output transaction commits.
    655 cat > "$work/boundary.s" <<'EOF'
    656 .globl _start
    657 .section .text
    658 _start:
    659   ret
    660 EOF
    661 cat > "$work/boundary.lds" <<'EOF'
    662 ENTRY(_start)
    663 SECTIONS {
    664   . = 0xffffffff;
    665   .text : ALIGN(1) { *(.text .text.*) }
    666   /DISCARD/ : { *(.note.*) *(.comment) *(.eh_frame) }
    667 }
    668 EOF
    669 run_ok image-text-boundary-build "$KIT" build-exe -target x86_64-none-elf \
    670     -nostdlib -static -no-pie -e _start -T "$work/boundary.lds" \
    671     "$work/boundary.s" -o "$work/boundary.elf"
    672 run_ok image-ihex-boundary "$KIT" image --format ihex "$work/boundary.elf" \
    673     -o "$work/boundary.hex"
    674 contains image-ihex-boundary-entry "$work/boundary.hex" \
    675     ":04000005FFFFFFFFFB"
    676 run_ok image-srec-boundary "$KIT" image --format srec "$work/boundary.elf" \
    677     -o "$work/boundary.srec"
    678 contains image-srec-boundary-entry "$work/boundary.srec" "S705FFFFFFFFFE"
    679 cat > "$work/boundary-cross.s" <<'EOF'
    680 .globl _start
    681 .section .text
    682 _start:
    683   ret
    684   nop
    685 EOF
    686 run_ok image-text-boundary-cross-build "$KIT" build-exe \
    687     -target x86_64-none-elf -nostdlib -static -no-pie -e _start \
    688     -T "$work/boundary.lds" "$work/boundary-cross.s" \
    689     -o "$work/boundary-cross.elf"
    690 run_fail image-ihex-boundary-cross "$KIT" image --format ihex \
    691     "$work/boundary-cross.elf" -o "$work/boundary-cross.hex"
    692 run_fail image-srec-boundary-cross "$KIT" image --format srec \
    693     "$work/boundary-cross.elf" -o "$work/boundary-cross.srec"
    694 if [ ! -e "$work/boundary-cross.hex" ] && \
    695    [ ! -e "$work/boundary-cross.srec" ]; then
    696     ok image-boundary-cross-transaction
    697 else
    698     not_ok image-boundary-cross-transaction
    699 fi
    700 
    701 # Relocatable objects use allocated section contents. Sections retain their
    702 # addresses, later selected overlaps win, filters apply before layout, and no
    703 # synthetic entry record is invented. The same library path backs objcopy.
    704 cat > "$work/raw-rel.s" <<'EOF'
    705 .globl raw_entry
    706 .section .text
    707 raw_entry:
    708   ret
    709 .section .data
    710   .byte 0x42
    711 EOF
    712 run_ok image-rel-build "$KIT" as -target x86_64-none-elf \
    713     "$work/raw-rel.s" -o "$work/raw-rel.o"
    714 run_ok objcopy-rel-binary "$KIT" objcopy -O binary "$work/raw-rel.o" \
    715     "$work/raw-rel.bin"
    716 "$KIT" xxd -p "$work/raw-rel.bin" > "$work/raw-rel.hexbytes"
    717 contains image-rel-overlap-later-wins "$work/raw-rel.hexbytes" "42"
    718 run_ok objcopy-rel-only-text "$KIT" objcopy --only-section .text -O binary \
    719     "$work/raw-rel.o" "$work/raw-rel.text.bin"
    720 "$KIT" xxd -p "$work/raw-rel.text.bin" > "$work/raw-rel.text.hexbytes"
    721 contains image-rel-only-text-byte "$work/raw-rel.text.hexbytes" "c3"
    722 run_ok objcopy-rel-remove-data "$KIT" objcopy --remove-section .data \
    723     -O binary "$work/raw-rel.o" "$work/raw-rel.nodata.bin"
    724 same_file image-rel-remove-matches-only "$work/raw-rel.text.bin" \
    725     "$work/raw-rel.nodata.bin"
    726 run_ok objcopy-rel-ihex "$KIT" objcopy -O ihex "$work/raw-rel.o" \
    727     "$work/raw-rel.ihex"
    728 if ! grep -q '^:04000005' "$work/raw-rel.ihex"; then
    729     ok image-rel-ihex-no-entry
    730 else
    731     not_ok image-rel-ihex-no-entry "$work/raw-rel.ihex"
    732 fi
    733 run_ok objcopy-rel-srec "$KIT" objcopy -O srec "$work/raw-rel.o" \
    734     "$work/raw-rel.srec"
    735 if ! grep -q '^S[789]' "$work/raw-rel.srec"; then
    736     ok image-rel-srec-no-entry
    737 else
    738     not_ok image-rel-srec-no-entry "$work/raw-rel.srec"
    739 fi
    740 
    741 # Reject an unapplied relocation in a selected allocated section.
    742 cat > "$work/raw-reloc.s" <<'EOF'
    743 .globl raw_reloc
    744 .section .text
    745 raw_reloc:
    746   call missing_target
    747 EOF
    748 run_ok image-reloc-build "$KIT" as -target x86_64-none-elf \
    749     "$work/raw-reloc.s" -o "$work/raw-reloc.o"
    750 run_fail objcopy-reloc-rejected "$KIT" objcopy -O binary \
    751     "$work/raw-reloc.o" "$work/raw-reloc.bin"
    752 if [ ! -e "$work/raw-reloc.bin" ]; then ok image-reloc-transaction;
    753 else not_ok image-reloc-transaction "$work/raw-reloc.bin"; fi
    754 
    755 # Object-format conversions remain format-neutral at the image API boundary.
    756 run_ok image-rel-macho-build "$KIT" objcopy -O mach-o "$work/raw-rel.o" \
    757     "$work/raw-rel.macho"
    758 run_ok image-rel-macho "$KIT" objcopy --only-section __TEXT,__text -O binary \
    759     "$work/raw-rel.macho" "$work/raw-rel.macho.bin"
    760 same_file image-rel-macho-bytes "$work/raw-rel.text.bin" \
    761     "$work/raw-rel.macho.bin"
    762 run_ok image-rel-coff-build "$KIT" objcopy -O coff "$work/raw-rel.o" \
    763     "$work/raw-rel.coff"
    764 run_ok image-rel-coff "$KIT" objcopy --only-section .text -O binary \
    765     "$work/raw-rel.coff" "$work/raw-rel.coff.bin"
    766 same_file image-rel-coff-bytes "$work/raw-rel.text.bin" \
    767     "$work/raw-rel.coff.bin"
    768 run_ok image-rel-wasm "$KIT" objcopy --only-section code -O binary \
    769     "$repo_root/test/objdump/wasm/cases/add.wasm" "$work/raw-rel.wasm.bin"
    770 if [ -s "$work/raw-rel.wasm.bin" ]; then ok image-rel-wasm-nonempty;
    771 else not_ok image-rel-wasm-nonempty "$work/raw-rel.wasm.bin"; fi
    772 
    773 # ---- image: --format sections ---------------------------------------------
    774 # Concatenate named sections in DECLARED order. The fixture's .text is a lone
    775 # `ret` (0xc3) and .rodata is 0x11223344, so the concatenation and its reverse
    776 # are exact, checkable byte strings.
    777 run_ok image-sections "$KIT" image --format sections \
    778     --section .text --section .rodata "$work/kernel.elf" -o "$work/sec.bin"
    779 "$KIT" xxd -p "$work/sec.bin" > "$work/sec.hex"
    780 contains image-sections-order "$work/sec.hex" "c311223344"
    781 run_ok image-sections-rev "$KIT" image --format sections \
    782     --section .rodata --section .text "$work/kernel.elf" -o "$work/sec.rev.bin"
    783 "$KIT" xxd -p "$work/sec.rev.bin" > "$work/sec.rev.hex"
    784 contains image-sections-rev-order "$work/sec.rev.hex" "11223344c3"
    785 # Determinism: same input + options twice is byte-identical.
    786 run_ok image-sections-again "$KIT" image --format sections \
    787     --section .text --section .rodata "$work/kernel.elf" -o "$work/sec.again.bin"
    788 same_file image-sections-deterministic "$work/sec.bin" "$work/sec.again.bin"
    789 # A missing named section is a hard error.
    790 run_fail image-sections-missing "$KIT" image --format sections \
    791     --section .nope "$work/kernel.elf" -o "$work/sec.miss.bin"
    792 # --section is rejected for the plain bin format.
    793 run_fail image-section-needs-format "$KIT" image --format bin \
    794     --section .text "$work/kernel.elf" -o "$work/sec.bad.bin"
    795 
    796 # ---- image: --format rom --------------------------------------------------
    797 # ROM is a fixed-size flat binary; it requires an explicit --pad-to. Driving it
    798 # from the 5-byte section concatenation gives a small, exact payload to pad.
    799 run_fail image-rom-needs-size "$KIT" image --format rom \
    800     --section .text --section .rodata "$work/kernel.elf" -o "$work/rom.bad.bin"
    801 run_ok image-rom "$KIT" image --format rom --section .text --section .rodata \
    802     --pad-to 16 --fill 0xff "$work/kernel.elf" -o "$work/rom.bin"
    803 rom_got=$(wc -c < "$work/rom.bin" | tr -d ' ')
    804 if [ "$rom_got" = "16" ]; then
    805     ok image-rom-size
    806 else
    807     echo "got $rom_got want 16" > "$work/image-rom-size.diag"
    808     not_ok image-rom-size "$work/image-rom-size.diag"
    809 fi
    810 "$KIT" xxd -p "$work/rom.bin" > "$work/rom.hex"
    811 contains image-rom-payload-and-fill "$work/rom.hex" "c311223344ffffffffffffffffffffff"
    812 # Determinism: identical bytes on a second run.
    813 run_ok image-rom-again "$KIT" image --format rom --section .text \
    814     --section .rodata --pad-to 16 --fill 0xff "$work/kernel.elf" \
    815     -o "$work/rom.again.bin"
    816 same_file image-rom-deterministic "$work/rom.bin" "$work/rom.again.bin"
    817 # Payload larger than the requested size fails (5 payload bytes, pad-to 4).
    818 run_fail image-rom-overflow "$KIT" image --format rom --section .text \
    819     --section .rodata --pad-to 4 "$work/kernel.elf" -o "$work/rom.of.bin"
    820 
    821 # ---- image: --metadata sidecar --------------------------------------------
    822 # Deterministic JSON: stable keys, no host paths/timestamps. Assert presence,
    823 # a few stable fields, and byte-identical re-emission.
    824 run_ok image-metadata "$KIT" image --format bin \
    825     --metadata "$work/meta.json" "$work/kernel.elf" -o "$work/meta.bin"
    826 assert_file_exists image-metadata-file "$work/meta.json"
    827 contains image-metadata-format "$work/meta.json" '"format": "bin"'
    828 contains image-metadata-objfmt "$work/meta.json" '"object_format": "elf"'
    829 contains image-metadata-buildid "$work/meta.json" '"build_id":'
    830 run_ok image-metadata-again "$KIT" image --format bin \
    831     --metadata "$work/meta2.json" "$work/kernel.elf" -o "$work/meta2.bin"
    832 same_file image-metadata-deterministic "$work/meta.json" "$work/meta2.json"
    833 # Section-source metadata records the section selection and marks the base as
    834 # not a load address.
    835 run_ok image-metadata-sections "$KIT" image --format sections \
    836     --section .text --section .rodata --metadata "$work/meta.sec.json" \
    837     "$work/kernel.elf" -o "$work/meta.sec.bin"
    838 contains image-metadata-sections-source "$work/meta.sec.json" '"source": "sections"'
    839 contains image-metadata-sections-base "$work/meta.sec.json" \
    840     '"base_is_load_address": false'
    841 
    842 # ---- image: validation flags ----------------------------------------------
    843 # The fixture is a static -no-pie ELF with an _start entry and a .text section.
    844 run_ok image-require-entry-ok "$KIT" image --require-entry --format bin \
    845     "$work/kernel.elf" -o "$work/req.bin"
    846 run_ok image-require-symbol-ok "$KIT" image --require-symbol _start \
    847     --format bin "$work/kernel.elf" -o "$work/req.bin"
    848 run_fail image-require-symbol-missing "$KIT" image \
    849     --require-symbol no_such_symbol --format bin "$work/kernel.elf" \
    850     -o "$work/req.bad.bin"
    851 run_ok image-require-section-ok "$KIT" image --require-section .text \
    852     --format bin "$work/kernel.elf" -o "$work/req.bin"
    853 run_fail image-require-section-missing "$KIT" image \
    854     --require-section .nope --format bin "$work/kernel.elf" -o "$work/req.bad.bin"
    855 # A static, non-dynamic image passes --no-dynamic.
    856 run_ok image-no-dynamic-ok "$KIT" image --no-dynamic --format bin \
    857     "$work/kernel.elf" -o "$work/nd.bin"
    858 # --format elf is deferred to objcopy/strip and reports so.
    859 run_fail image-format-elf-deferred "$KIT" image --format elf \
    860     "$work/kernel.elf" -o "$work/elf.bin"
    861 
    862 # ==== BEGIN kernel-FZ Bug 10: metadata selection mirrors the emitter =========
    863 # The sidecar `selection` must list exactly the segments that contributed bytes.
    864 # The emitter strips a leading PT_ and folds case (PT_LOAD == load == LOAD), so a
    865 # selection written with exact-byte matching would diverge: --segment PT_LOAD and
    866 # --segment load both select the loadable segment yet must not yield an empty
    867 # list. (Regression guard for the hand-rolled-vs-emitter policy drift.)
    868 run_ok image-meta-seg-ptload "$KIT" image --format bin --segment PT_LOAD \
    869     --metadata "$work/meta.ptload.json" "$work/kernel.elf" -o "$work/sel.ptload.bin"
    870 contains image-meta-seg-ptload-selected "$work/meta.ptload.json" '"selection": ["LOAD"'
    871 run_ok image-meta-seg-lowerload "$KIT" image --format bin --segment load \
    872     --metadata "$work/meta.load.json" "$work/kernel.elf" -o "$work/sel.load.bin"
    873 contains image-meta-seg-lowerload-selected "$work/meta.load.json" '"selection": ["LOAD"'
    874 # The default-ELF (no --segment) selection also lists the loadable segment(s).
    875 run_ok image-meta-default-sel "$KIT" image --format bin \
    876     --metadata "$work/meta.def.json" "$work/kernel.elf" -o "$work/sel.def.bin"
    877 contains image-meta-default-selected "$work/meta.def.json" '"selection": ["LOAD"'
    878 # Explicit section filters switch to allocated-section ranges; an empty
    879 # keep-set is a hard failure and cannot leave misleading metadata/output.
    880 run_fail image-meta-remove-all "$KIT" image --format bin \
    881     --only-section .nope --metadata "$work/meta.rm.json" "$work/kernel.elf" \
    882     -o "$work/sel.rm.bin"
    883 # ==== END kernel-FZ Bug 10 ===================================================
    884 
    885 # ---- image: flat-kernel Image header (--image-header) ----------------------
    886 # A freestanding kernel whose first 64 bytes are an Image header AREA: an entry
    887 # branch in the first 8 bytes (code0/code1) and 56 reserved bytes the emitter
    888 # fills. A 4 KiB .bss makes the in-memory span exceed the file bytes, so a
    889 # correct image_size must count BSS. The skeleton leaves the metadata zero so a
    890 # plain `--format bin` (raw) is a witness that the emitter, not the author,
    891 # wrote the magic and size.
    892 cat > "$work/khdr_a64.S" <<'EOF'
    893     .section .text, "ax"
    894     .globl _start
    895 _start:
    896     b       kstart      /* 0x00 code0: branch over the header */
    897     .long   0           /* 0x04 code1 */
    898     .quad   0           /* 0x08 text_offset (kit fills) */
    899     .quad   0           /* 0x10 image_size  (kit fills) */
    900     .quad   0           /* 0x18 flags       (kit fills) */
    901     .quad   0           /* 0x20 res2 */
    902     .quad   0           /* 0x28 res3 */
    903     .quad   0           /* 0x30 res4 */
    904     .long   0           /* 0x38 magic       (kit fills) */
    905     .long   0           /* 0x3c res5 */
    906 kstart:
    907     ret
    908     .section .data, "aw"
    909     .byte 0x42
    910     .section .bss, "aw", %nobits
    911     .balign 16
    912     .skip 4096
    913 EOF
    914 cat > "$work/khdr_a64.lds" <<'EOF'
    915 ENTRY(_start)
    916 SECTIONS {
    917   . = 0x40080000;
    918   .text : ALIGN(8) { *(.text .text.*) }
    919   .data : ALIGN(8) { *(.data .data.*) }
    920   .bss  : ALIGN(16) { *(.bss .bss.*) }
    921   _end = .;
    922   /DISCARD/ : { *(.note.*) *(.comment) *(.eh_frame) }
    923 }
    924 EOF
    925 run_ok image-hdr-a64-build "$KIT" build-exe -target aarch64-none-elf \
    926     -nostdlib -nostartfiles -static -no-pie -e _start -T "$work/khdr_a64.lds" \
    927     "$work/khdr_a64.S" -o "$work/khdr_a64.elf"
    928 run_ok image-hdr-a64 "$KIT" image --format bin --image-header=arm64 \
    929     --metadata "$work/khdr_a64.json" "$work/khdr_a64.elf" -o "$work/khdr_a64.img"
    930 # Plain bin is the witness for code0/code1, total size, and the empty skeleton.
    931 run_ok image-hdr-a64-raw "$KIT" image --format bin "$work/khdr_a64.elf" \
    932     -o "$work/khdr_a64.raw"
    933 # The entry branch (code0/code1) is preserved verbatim.
    934 head -c 8 "$work/khdr_a64.img" > "$work/a64.code0"
    935 head -c 8 "$work/khdr_a64.raw" > "$work/a64.code0.raw"
    936 same_file image-hdr-a64-code0-preserved "$work/a64.code0.raw" "$work/a64.code0"
    937 # Overlay, not prepend: same length as plain bin.
    938 if [ "$(wc -c < "$work/khdr_a64.img")" = "$(wc -c < "$work/khdr_a64.raw")" ]; then
    939     ok image-hdr-a64-overlay-size
    940 else
    941     not_ok image-hdr-a64-overlay-size
    942 fi
    943 # arm64 magic "ARM\x64" lands at offset 56; the skeleton (raw) is still zero
    944 # there, proving the emitter synthesized it.
    945 tail -c +57 "$work/khdr_a64.img" | head -c 4 | "$KIT" xxd -p > "$work/a64.magic"
    946 contains image-hdr-a64-magic "$work/a64.magic" "41524d64"
    947 tail -c +57 "$work/khdr_a64.raw" | head -c 4 | "$KIT" xxd -p > "$work/a64.rawmagic"
    948 contains image-hdr-a64-skeleton-zero "$work/a64.rawmagic" "00000000"
    949 # image_size (header offset 16) is written and counts the 4 KiB BSS: it exceeds
    950 # the file image and the metadata mirrors it.
    951 tail -c +17 "$work/khdr_a64.img" | head -c 8 | "$KIT" xxd -p > "$work/a64.szf.hex"
    952 if [ "$(cat "$work/a64.szf.hex")" != "0000000000000000" ]; then
    953     ok image-hdr-a64-size-written
    954 else
    955     not_ok image-hdr-a64-size-written
    956 fi
    957 a64_imgsz=$(grep '"image_size"' "$work/khdr_a64.json" | tr -dc '0-9')
    958 a64_filesz=$(wc -c < "$work/khdr_a64.img" | tr -d ' ')
    959 if [ "$a64_imgsz" -ge 4096 ] && [ "$a64_imgsz" -gt "$a64_filesz" ]; then
    960     ok image-hdr-a64-size-counts-bss
    961 else
    962     echo "image_size=$a64_imgsz file=$a64_filesz" > "$work/a64sz.diag"
    963     not_ok image-hdr-a64-size-counts-bss "$work/a64sz.diag"
    964 fi
    965 # Determinism: identical bytes on a second run.
    966 run_ok image-hdr-a64-again "$KIT" image --format bin --image-header=arm64 \
    967     "$work/khdr_a64.elf" -o "$work/khdr_a64.img2"
    968 same_file image-hdr-a64-deterministic "$work/khdr_a64.img" "$work/khdr_a64.img2"
    969 
    970 # riscv64: same shape, exercised through AUTO arch inference (bare flag). The
    971 # riscv tail differs — version 2 at offset 32 and magic2 "RSC\x05" at 56.
    972 cat > "$work/khdr_rv.S" <<'EOF'
    973     .section .text, "ax"
    974     .globl _start
    975 _start:
    976     j       kstart      /* 0x00 code0 */
    977     .long   0           /* 0x04 code1 */
    978     .quad   0           /* 0x08 text_offset */
    979     .quad   0           /* 0x10 image_size */
    980     .quad   0           /* 0x18 flags */
    981     .long   0           /* 0x20 version (kit fills) */
    982     .long   0           /* 0x24 res1 */
    983     .quad   0           /* 0x28 res2 */
    984     .quad   0           /* 0x30 deprecated magic */
    985     .long   0           /* 0x38 magic2 (kit fills) */
    986     .long   0           /* 0x3c res3 */
    987 kstart:
    988     ret
    989     .section .data, "aw"
    990     .byte 0x42
    991     .section .bss, "aw", %nobits
    992     .balign 16
    993     .skip 8192
    994 EOF
    995 cat > "$work/khdr_rv.lds" <<'EOF'
    996 ENTRY(_start)
    997 SECTIONS {
    998   . = 0x80200000;
    999   .text : ALIGN(8) { *(.text .text.*) }
   1000   .data : ALIGN(8) { *(.data .data.*) }
   1001   .bss  : ALIGN(16) { *(.bss .bss.*) }
   1002   _end = .;
   1003   /DISCARD/ : { *(.note.*) *(.comment) *(.eh_frame) }
   1004 }
   1005 EOF
   1006 run_ok image-hdr-rv-build "$KIT" build-exe -target riscv64-none-elf \
   1007     -nostdlib -nostartfiles -static -no-pie -e _start -T "$work/khdr_rv.lds" \
   1008     "$work/khdr_rv.S" -o "$work/khdr_rv.elf"
   1009 run_ok image-hdr-rv-auto "$KIT" image --format bin --image-header \
   1010     --metadata "$work/khdr_rv.json" "$work/khdr_rv.elf" -o "$work/khdr_rv.img"
   1011 contains image-hdr-rv-auto-detected "$work/khdr_rv.json" '"image_header": "riscv"'
   1012 tail -c +57 "$work/khdr_rv.img" | head -c 4 | "$KIT" xxd -p > "$work/rv.magic"
   1013 contains image-hdr-rv-magic2 "$work/rv.magic" "52534305"
   1014 tail -c +33 "$work/khdr_rv.img" | head -c 4 | "$KIT" xxd -p > "$work/rv.ver"
   1015 contains image-hdr-rv-version "$work/rv.ver" "02000000"
   1016 rv_imgsz=$(grep '"image_size"' "$work/khdr_rv.json" | tr -dc '0-9')
   1017 if [ "$rv_imgsz" -ge 8192 ]; then
   1018     ok image-hdr-rv-size-counts-bss
   1019 else
   1020     echo "image_size=$rv_imgsz" > "$work/rvsz.diag"
   1021     not_ok image-hdr-rv-size-counts-bss "$work/rvsz.diag"
   1022 fi
   1023 
   1024 # Pass-through: an author who writes the full header keeps it byte-exact through
   1025 # a plain `--format bin` (no --image-header). image_size = _end - _start and the
   1026 # magic are the author's own bytes, untouched by the emitter.
   1027 cat > "$work/khdr_pt.S" <<'EOF'
   1028     .section .text, "ax"
   1029     .globl _start
   1030 _start:
   1031     b       kstart
   1032     .long   0
   1033     .quad   0x80000             /* text_offset */
   1034     .quad   0x4000              /* image_size (author-written constant) */
   1035     .quad   0xa                 /* flags: 4K, LE */
   1036     .quad   0
   1037     .quad   0
   1038     .quad   0
   1039     .ascii  "ARM\x64"           /* magic */
   1040     .long   0
   1041 kstart:
   1042     ret
   1043     .section .bss, "aw", %nobits
   1044     .balign 16
   1045     .skip 256
   1046 EOF
   1047 run_ok image-hdr-pt-build "$KIT" build-exe -target aarch64-none-elf \
   1048     -nostdlib -nostartfiles -static -no-pie -e _start -T "$work/khdr_a64.lds" \
   1049     "$work/khdr_pt.S" -o "$work/khdr_pt.elf"
   1050 run_ok image-hdr-pt-bin "$KIT" image --format bin "$work/khdr_pt.elf" \
   1051     -o "$work/khdr_pt.bin"
   1052 tail -c +57 "$work/khdr_pt.bin" | head -c 4 | "$KIT" xxd -p > "$work/pt.magic"
   1053 contains image-hdr-pt-magic-preserved "$work/pt.magic" "41524d64"
   1054 tail -c +17 "$work/khdr_pt.bin" | head -c 8 | "$KIT" xxd -p > "$work/pt.sz.hex"
   1055 if [ "$(cat "$work/pt.sz.hex")" != "0000000000000000" ]; then
   1056     ok image-hdr-pt-size-preserved
   1057 else
   1058     not_ok image-hdr-pt-size-preserved
   1059 fi
   1060 run_ok image-hdr-pt-again "$KIT" image --format bin "$work/khdr_pt.elf" \
   1061     -o "$work/khdr_pt.bin2"
   1062 same_file image-hdr-pt-deterministic "$work/khdr_pt.bin" "$work/khdr_pt.bin2"
   1063 
   1064 # Negative: a section source has no memory model, AUTO needs a known arch, the
   1065 # page-size flag is arm64-only, and the header sub-options need --image-header.
   1066 run_fail image-hdr-rejects-sections "$KIT" image --format sections \
   1067     --section .text --image-header=arm64 "$work/kernel.elf" -o "$work/h.bad.bin"
   1068 run_fail image-hdr-auto-unknown-arch "$KIT" image --format bin --image-header \
   1069     "$work/kernel.elf" -o "$work/h.bad.bin"
   1070 run_fail image-hdr-page-size-riscv "$KIT" image --format bin --image-header=riscv \
   1071     --image-page-size 16k "$work/khdr_rv.elf" -o "$work/h.bad.bin"
   1072 run_fail image-hdr-opts-need-header "$KIT" image --format bin \
   1073     --image-text-offset 0x80000 "$work/kernel.elf" -o "$work/h.bad.bin"
   1074 
   1075 kit_summary tools-driver
   1076 kit_exit