Files
MontaukOS/scripts/check-arch-neutral.sh
T

102 lines
3.8 KiB
Bash
Executable File

#!/bin/sh
# check-arch-neutral.sh - Verify the kernel's architecture boundary.
#
# 1. Static rules: outside kernel/src/Arch/, kernel code may not contain
# inline assembly, assembly sources, architecture #ifdefs, or includes of
# a specific port's headers (it must use the interfaces in Arch/*.hpp).
# 2. If clang is installed, compile the whole kernel for a second
# architecture (default aarch64) and report any symbol the generic code
# needs that the port does not define. Links too when ld.lld is found.
#
# Usage: ./scripts/check-arch-neutral.sh [arch]
#
# Copyright (c) 2026 Daniel Hammer
set -e
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
SRC="$ROOT/kernel/src"
ARCH="${1:-aarch64}"
status=0
cd "$SRC"
# Files allowed to name an architecture: the Limine adapter reads the
# per-architecture MP structures limine.h defines, and Io/IoPort.hpp is an
# interface header that dispatches to the port like Arch/*.hpp do.
ALLOW='^(limine\.h|Boot/Protocols/LimineProtocol\.cpp|Io/IoPort\.hpp)$'
generic=$(find . -type f \( -name '*.c' -o -name '*.cpp' -o -name '*.h' -o -name '*.hpp' \) \
! -path './Arch/*' | sed 's|^\./||' | LC_ALL=C sort)
report() {
echo "check-arch-neutral: $1" >&2
echo "$2" | sed 's/^/ /' >&2
status=1
}
hits=$(grep -nE '(^|[^_[:alnum:]])(asm|__asm__)[[:space:]]*(volatile|\()' $generic || true)
[ -n "$hits" ] && report "inline assembly outside src/Arch/:" "$hits"
hits=$(find . -type f \( -name '*.asm' -o -name '*.S' -o -name '*.s' \) ! -path './Arch/*')
[ -n "$hits" ] && report "assembly sources outside src/Arch/:" "$hits"
hits=$(echo "$generic" | grep -vE "$ALLOW" | xargs grep -nE \
'__(x86_64|i386|aarch64|arm|riscv|loongarch[0-9]*)__|MONTAUK_ARCH_(X86|AARCH|RISCV|LOONG)[A-Z0-9_]*' || true)
[ -n "$hits" ] && report "architecture conditionals outside src/Arch/:" "$hits"
hits=$(echo "$generic" | grep -vE "$ALLOW" | xargs grep -nE \
'#[[:space:]]*include[[:space:]]*[<"]Arch/[^/>"]+/' || true)
[ -n "$hits" ] && report "includes of a port's private headers:" "$hits"
if [ $status -eq 0 ]; then
echo "check-arch-neutral: static rules OK"
fi
# ---- Cross-compile check ----
if ! command -v clang >/dev/null 2>&1; then
echo "check-arch-neutral: clang not found, skipping the $ARCH build"
exit $status
fi
if [ ! -d "Arch/$ARCH" ]; then
echo "check-arch-neutral: no src/Arch/$ARCH port" >&2
exit 1
fi
echo "check-arch-neutral: compiling the kernel for $ARCH"
make -C "$ROOT/kernel" ARCH="$ARCH" objects -j"$(nproc 2>/dev/null || echo 4)" \
>/dev/null 2>"$ROOT/kernel/check-arch-$ARCH.log" || {
echo "check-arch-neutral: $ARCH compile failed (kernel/check-arch-$ARCH.log):" >&2
grep -E 'error' "$ROOT/kernel/check-arch-$ARCH.log" | head -20 >&2
exit 1
}
rm -f "$ROOT/kernel/check-arch-$ARCH.log"
NM="$(clang -print-prog-name=llvm-nm)"
command -v "$NM" >/dev/null 2>&1 || NM=nm
CXXFILT="$(clang -print-prog-name=llvm-cxxfilt)"
command -v "$CXXFILT" >/dev/null 2>&1 || CXXFILT=cat
cd "$ROOT/kernel"
objs=$(find "obj-$ARCH" -name '*.o')
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT
"$NM" --defined-only -j $objs "cc-runtime-$ARCH/cc-runtime.a" 2>/dev/null | LC_ALL=C sort -u > "$tmp/def"
"$NM" --undefined-only -j $objs | LC_ALL=C sort -u > "$tmp/undef"
# Provided by the linker script.
printf '%s\n' KernelStartSymbol KernelEndSymbol __init_array __init_array_end >> "$tmp/def"
LC_ALL=C sort -u -o "$tmp/def" "$tmp/def"
missing=$(LC_ALL=C comm -23 "$tmp/undef" "$tmp/def" | "$CXXFILT")
if [ -n "$missing" ]; then
report "symbols the $ARCH port must still provide:" "$missing"
exit 1
fi
echo "check-arch-neutral: $ARCH objects compile and every symbol resolves"
if command -v "${KERNEL_LD:-ld.lld}" >/dev/null 2>&1; then
make ARCH="$ARCH" >/dev/null
echo "check-arch-neutral: linked kernel/bin-$ARCH/kernel"
fi
exit $status