kit

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

commit b7bb677fe3c6a57411463120d5b3e27d60cc085d
parent 771ea6e79386c386bfdc40a35019c81a8de76f22
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu, 16 Jul 2026 10:24:29 -0700

test: cover linked image stripping across formats

Diffstat:
Mdoc/RELEASE_AUDIT_2026_6_0.md | 2+-
Atest/strip/linked.sh | 278+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 279 insertions(+), 1 deletion(-)

diff --git a/doc/RELEASE_AUDIT_2026_6_0.md b/doc/RELEASE_AUDIT_2026_6_0.md @@ -500,7 +500,7 @@ Implementation checkpoint (updated 2026-07-16): | KIT-P2-001 | **Complete** | `test-driver-stack-protector`: 111 pass, 0 fail; cJSON build/test passes | | KIT-P1-015 | **Complete** | 21 multi-source dependency assertions pass in the driver suite | | KIT-P2-003 | **Complete** | `test-driver-diagnostics`: 15 pass, 0 fail | -| KIT-P1-007 | In progress | public ELF/Mach-O/PE rewrite unit: 23 checks, 0 failures; runnable matrix pending | +| KIT-P1-007 | **Complete** | `test-driver-strip`: object API 23/0, relocatable/archive 5/0, linked ELF/Mach-O/PE matrix 91/0 | | KIT-P1-017 | In progress | implementation under focused CLI audit | | KIT-P2-002 | In progress | implementation under focused CLI audit | | KIT-P3-003 | **Complete** | `test-link-macho-symbols` passes aarch64 and x86-64 Kit/platform oracles | diff --git a/test/strip/linked.sh b/test/strip/linked.sh @@ -0,0 +1,278 @@ +#!/bin/sh +# Linked-image strip acceptance: loader metadata is an exact oracle, executable +# modes survive transactional -o rewrites, native images remain runnable, and +# signed Mach-O inputs require an explicit signature-removal opt-in. + +set -u + +script_dir=$(cd "$(dirname "$0")" && pwd) +repo_root=$(cd "$script_dir/../.." && pwd) +KIT_KIT_DIR="$repo_root/test/lib" +. "$repo_root/test/lib/kit_sh_kit.sh" + +KIT=${KIT:-"$repo_root/build/kit"} +export KIT +kit_require_kit strip-linked + +kit_workdir strip-linked +work=$KIT_WORK +kit_report_init + +loader_dump() { # INPUT OUTPUT + "$KIT" objdump -p -R "$1" 2> "$2.err" | + awk '/^ __DWARF[[:space:]]/ { getline; next } { print }' | + sed -e 's|^.*:\t\(.*private headers\)$|<image>:\t\1|' \ + -e 's|^.*:\t\(file format.*\)$|<image>:\t\1|' > "$2" +} + +dynamic_dump() { # INPUT OUTPUT + "$KIT" objdump -T "$1" 2> "$2.err" | + sed -e 's|^.*:\t\(file format.*\)$|<image>:\t\1|' > "$2" +} + +assert_no_debug() { # NAME INPUT + and_name=$1 + and_input=$2 + "$KIT" objdump -h "$and_input" > "$work/$and_name.sections" 2>&1 + if grep -E '(^|[, ])(__DWARF|\.debug_|\.zdebug_)' \ + "$work/$and_name.sections" >/dev/null 2>&1; then + not_ok "$and_name" "$work/$and_name.sections" + else + ok "$and_name" + fi +} + +strip_shape() { # NAME INPUT [REMOVE_SIGNATURE] [PRESERVE_ALL_NLIST] + ss_name=$1 + ss_input=$2 + ss_signature=${3:-0} + ss_preserve_all_nlist=${4:-1} + ss_sigflag= + [ "$ss_signature" -eq 0 ] || ss_sigflag=--remove-signature + + chmod 0751 "$ss_input" + loader_dump "$ss_input" "$work/$ss_name.before" + dynamic_dump "$ss_input" "$work/$ss_name.before.dynamic" + + # shellcheck disable=SC2086 -- optional single flag is intentional. + run_ok "$ss_name-strip-debug" "$KIT" strip --strip-debug $ss_sigflag \ + -o "$work/$ss_name.debug" "$ss_input" + check_mode "$ss_name-debug-mode" "$work/$ss_name.debug" 751 + assert_no_debug "$ss_name-debug-sections" "$work/$ss_name.debug" + loader_dump "$work/$ss_name.debug" "$work/$ss_name.debug.loader" + same_file "$ss_name-debug-loader" "$work/$ss_name.before" \ + "$work/$ss_name.debug.loader" + dynamic_dump "$work/$ss_name.debug" "$work/$ss_name.debug.dynamic" + same_file "$ss_name-debug-dynamic" "$work/$ss_name.before.dynamic" \ + "$work/$ss_name.debug.dynamic" + + # shellcheck disable=SC2086 -- optional single flag is intentional. + run_ok "$ss_name-strip-all" "$KIT" strip --strip-all $ss_sigflag \ + -o "$work/$ss_name.all" "$ss_input" + check_mode "$ss_name-all-mode" "$work/$ss_name.all" 751 + assert_no_debug "$ss_name-all-sections" "$work/$ss_name.all" + loader_dump "$work/$ss_name.all" "$work/$ss_name.all.loader" + same_file "$ss_name-all-loader" "$work/$ss_name.before" \ + "$work/$ss_name.all.loader" + if [ "$ss_preserve_all_nlist" -ne 0 ]; then + dynamic_dump "$work/$ss_name.all" "$work/$ss_name.all.dynamic" + same_file "$ss_name-all-dynamic" "$work/$ss_name.before.dynamic" \ + "$work/$ss_name.all.dynamic" + fi +} + +# Cross-format, cross-architecture real linked images. These do not need a +# hosted sysroot and therefore run on every build host. +cat > "$work/x64-start.c" <<'EOF' +__attribute__((noreturn)) void _start(void) { + register long nr __asm__("rax") = 231; + register long code __asm__("rdi") = 42; + __asm__ volatile("syscall" : : "r"(nr), "r"(code) : "memory"); + __builtin_unreachable(); +} +EOF + +run_ok elf-static-build "$KIT" cc -target x86_64-linux-gnu -g \ + -ffreestanding -fno-stack-protector -fno-PIC -fno-pie -nostdlib -static \ + -Wl,-e,_start "$work/x64-start.c" -o "$work/elf-static" +if [ -f "$work/elf-static" ]; then + strip_shape elf-static "$work/elf-static" +fi + +run_ok elf-pie-build "$KIT" cc -target x86_64-linux-gnu -g \ + -ffreestanding -fno-stack-protector -fPIE -pie -nostdlib \ + -Wl,-e,_start "$work/x64-start.c" -o "$work/elf-pie" +if [ -f "$work/elf-pie" ]; then + strip_shape elf-pie "$work/elf-pie" +fi + +cat > "$work/elf-shared.c" <<'EOF' +extern int imported_value; +_Thread_local int tls_value = 7; +int exported_value(void) { return imported_value + tls_value; } +EOF +run_ok elf-shared-build "$KIT" cc -target x86_64-linux-gnu -g \ + -ffreestanding -fno-stack-protector -fPIC -shared -nostdlib \ + "$work/elf-shared.c" -o "$work/elf-shared.so" +if [ -f "$work/elf-shared.so" ]; then + strip_shape elf-shared "$work/elf-shared.so" +fi + +cat > "$work/pe-start.c" <<'EOF' +void _start(void) { for (;;) {} } +int exported_value(void) { return 7; } +EOF +for pe_arch in x86_64 aarch64; do + run_ok "pe-$pe_arch-build" "$KIT" cc -target "$pe_arch-windows" -g \ + -ffreestanding -fno-stack-protector -nostdlib -Wl,-e,_start \ + "$work/pe-start.c" -o "$work/pe-$pe_arch.exe" + if [ -f "$work/pe-$pe_arch.exe" ]; then + strip_shape "pe-$pe_arch" "$work/pe-$pe_arch.exe" + fi +done + +host_os=$(uname -s 2>/dev/null || printf unknown) +host_arch=$(uname -m 2>/dev/null || printf unknown) + +if [ "$host_os" = Darwin ]; then + # Kit's native output is a dynamically linked PIE carrying an ad-hoc code + # signature. Default failure must leave both input and a pre-existing -o + # destination byte-for-byte unchanged. + printf 'int helper(void) { return 0; } int main(void) { return helper(); }\n' \ + > "$work/native-app.c" + run_ok macho-native-build "$KIT" cc -g "$work/native-app.c" \ + -o "$work/macho-native" + if [ -f "$work/macho-native" ]; then + run_ok macho-native-run-before "$work/macho-native" + cp "$work/macho-native" "$work/macho-native.input-snapshot" + printf 'preexisting output\n' > "$work/macho-signed-destination" + cp "$work/macho-signed-destination" "$work/macho-signed-destination.want" + "$KIT" strip --strip-debug -o "$work/macho-signed-destination" \ + "$work/macho-native" > "$work/macho-signed-default.out" \ + 2> "$work/macho-signed-default.err" + macho_signed_rc=$? + if [ "$macho_signed_rc" -eq 1 ]; then + ok macho-signed-default-status + else + printf 'wanted 1, got %s\n' "$macho_signed_rc" \ + > "$work/macho-signed-default.diag" + not_ok macho-signed-default-status "$work/macho-signed-default.diag" + fi + contains macho-signed-default-guidance "$work/macho-signed-default.err" \ + "remove-signature" + same_file macho-signed-input-unchanged \ + "$work/macho-native.input-snapshot" "$work/macho-native" + same_file macho-signed-output-unchanged \ + "$work/macho-signed-destination.want" \ + "$work/macho-signed-destination" + + # MH_EXECUTE's ordinary external nlist is removable when its export trie + # owns loader visibility; the dylib case below requires an exact dynamic + # symbol view as well. + strip_shape macho-native "$work/macho-native" 1 0 + run_ok macho-native-debug-resign codesign -f -s - \ + "$work/macho-native.debug" + run_ok macho-native-all-resign codesign -f -s - \ + "$work/macho-native.all" + run_ok macho-native-debug-run "$work/macho-native.debug" + run_ok macho-native-all-run "$work/macho-native.all" + fi + + # A platform-linked dylib supplies an independent producer/oracle and proves + # exports, imports, unwind data, and install-name metadata remain usable. + sdk=$(xcrun --sdk macosx --show-sdk-path 2>/dev/null || printf '') + if [ -n "$sdk" ] && command -v clang >/dev/null 2>&1; then + printf 'int linked_answer(void) { return 42; }\n' > "$work/dylib.c" + printf 'int linked_answer(void); int main(void) { return linked_answer() == 42 ? 0 : 1; }\n' \ + > "$work/dylib-main.c" + run_ok macho-dylib-build env SDKROOT="$sdk" clang -isysroot "$sdk" -g \ + -dynamiclib "$work/dylib.c" -install_name @rpath/libanswer.dylib \ + -o "$work/libanswer.dylib" + run_ok macho-dylib-client-build env SDKROOT="$sdk" clang -isysroot "$sdk" \ + "$work/dylib-main.c" -L"$work" -lanswer \ + -Wl,-rpath,@loader_path -o "$work/dylib-client" + run_ok macho-dylib-run-before "$work/dylib-client" + if [ -f "$work/libanswer.dylib" ]; then + strip_shape macho-dylib "$work/libanswer.dylib" 1 + run_ok macho-dylib-debug-resign codesign -f -s - \ + "$work/macho-dylib.debug" + run_ok macho-dylib-all-resign codesign -f -s - \ + "$work/macho-dylib.all" + cp "$work/libanswer.dylib" "$work/libanswer.original" + cp "$work/macho-dylib.debug" "$work/libanswer.dylib" + run_ok macho-dylib-debug-run "$work/dylib-client" + cp "$work/macho-dylib.all" "$work/libanswer.dylib" + run_ok macho-dylib-all-run "$work/dylib-client" + cp "$work/libanswer.original" "$work/libanswer.dylib" + fi + else + skip_test macho-dylib-runtime "native SDK/clang unavailable" + fi +elif [ "$host_os" = Linux ]; then + # Native hosted output covers a runnable dynamic PIE. + printf 'int main(void) { return 0; }\n' > "$work/linux-native.c" + run_ok linux-dynamic-build "$KIT" cc -g "$work/linux-native.c" \ + -o "$work/linux-dynamic" + if [ -f "$work/linux-dynamic" ]; then + strip_shape linux-dynamic "$work/linux-dynamic" + run_ok linux-dynamic-run-before "$work/linux-dynamic" + run_ok linux-dynamic-debug-run "$work/linux-dynamic.debug" + run_ok linux-dynamic-all-run "$work/linux-dynamic.all" + fi + + # Execute a genuinely static freestanding artifact on supported native + # Linux architectures; the cross-format structural cases above remain + # unconditional. + case "$host_arch" in + x86_64|amd64) + native_static_target=x86_64-linux-gnu + cp "$work/x64-start.c" "$work/native-static.c" + ;; + aarch64|arm64) + native_static_target=aarch64-linux-gnu + cat > "$work/native-static.c" <<'EOF' +__attribute__((noreturn)) void _start(void) { + register long nr __asm__("x8") = 94; + register long code __asm__("x0") = 42; + __asm__ volatile("svc #0" : : "r"(nr), "r"(code) : "memory"); + __builtin_unreachable(); +} +EOF + ;; + riscv64) + native_static_target=riscv64-linux-gnu + cat > "$work/native-static.c" <<'EOF' +__attribute__((noreturn)) void _start(void) { + register long nr __asm__("a7") = 94; + register long code __asm__("a0") = 42; + __asm__ volatile("ecall" : : "r"(nr), "r"(code) : "memory"); + __builtin_unreachable(); +} +EOF + ;; + *) native_static_target= ;; + esac + if [ -n "$native_static_target" ]; then + run_ok linux-static-build "$KIT" cc -target "$native_static_target" -g \ + -ffreestanding -fno-stack-protector -fno-PIC -fno-pie -nostdlib \ + -static -Wl,-e,_start "$work/native-static.c" \ + -o "$work/linux-static" + if [ -f "$work/linux-static" ]; then + strip_shape linux-static "$work/linux-static" + "$work/linux-static" >/dev/null 2>&1; static_before_rc=$? + "$work/linux-static.debug" >/dev/null 2>&1; static_debug_rc=$? + "$work/linux-static.all" >/dev/null 2>&1; static_all_rc=$? + if [ "$static_before_rc" -eq 42 ]; then ok linux-static-run-before; + else not_ok linux-static-run-before; fi + if [ "$static_debug_rc" -eq 42 ]; then ok linux-static-debug-run; + else not_ok linux-static-debug-run; fi + if [ "$static_all_rc" -eq 42 ]; then ok linux-static-all-run; + else not_ok linux-static-all-run; fi + fi + else + skip_test linux-static-runtime "unsupported native architecture $host_arch" + fi +fi + +kit_summary strip-linked +kit_exit