From 639a534efb69beea734e469b47c5383d8f51d72e Mon Sep 17 00:00:00 2001 From: MT-GoCode Date: Sun, 19 Jul 2026 12:47:25 +0000 Subject: [PATCH 1/4] build(arm64): native linux/arm64 build support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fortress ships linux/x64, win/x64, macOS — but no linux/arm64 (roadmap: in progress). This adds native aarch64 build support so the patched, max-stealth Chromium can be built and run on arm64 (e.g. Lima/QEMU on Apple Silicon). - build/args.arm64.gn: arm64 sibling of args.gn. Every stealth knob is IDENTICAL; the only differences are architecture-bound: target_cpu="arm64" and dropping the x86-only SSE/AVX baseline cflags (-mno-avx/-mno-bmi/-mno-lzcnt/...), which clang rejects for aarch64 and which guard against an x86-only PA_CHECK failure mode. Chromium's default arm64 baseline (ARMv8-A) is already the broadest target. - build/build.sh: pick args by host arch (aarch64 -> args.arm64.gn), ARGS_FILE override still honored. - .gitignore: ignore the default .fortress-build/ workdir. All 34 C++ stealth patches are architecture-neutral (JS fingerprint surfaces: navigator/screen/canvas/webgl/webaudio/webrtc/timezone/fonts) and apply unchanged. Co-Authored-By: Claude Fable 5 --- .gitignore | 1 + build/args.arm64.gn | 50 +++++++++++++++++++++++++++++++++++++++++++++ build/build.sh | 8 +++++++- 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 build/args.arm64.gn diff --git a/.gitignore b/.gitignore index 0e35105..2552fbb 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ out/ src/ depot_tools/ +.fortress-build/ *.tar.gz *.deb *.AppImage diff --git a/build/args.arm64.gn b/build/args.arm64.gn new file mode 100644 index 0000000..8197719 --- /dev/null +++ b/build/args.arm64.gn @@ -0,0 +1,50 @@ +# Fortress — official, max-stealth Chromium build args (GN) for linux/arm64. +# Used by build/build.sh on aarch64 hosts: +# gn gen out/Fortress --args="$(cat build/args.arm64.gn)" +# +# This is the arm64 sibling of build/args.gn. Every stealth-relevant knob is +# IDENTICAL to the x64 file — the ONLY differences are architecture-bound: +# 1. target_cpu = "arm64" (was "x64") +# 2. no x86 extra_cflags (the SSE/AVX baseline block below is x86-only) +# All 34 C++ stealth patches are architecture-neutral (JS fingerprint surfaces: +# navigator/screen/canvas/webgl/webaudio/webrtc/timezone/fonts) and apply as-is. + +# Official, optimized, single-binary (NOT a component/dev build). +is_official_build = true +is_component_build = false +is_debug = false +dcheck_always_on = false + +# Stripped — no symbols (smaller, fewer tells). +symbol_level = 0 +blink_symbol_level = 0 + +# Real media: proprietary codecs + Chrome ffmpeg branding so canPlayType('video/mp4') +# returns "probably" (stock Chromium returns "" — a headless tell). +proprietary_codecs = true +ffmpeg_branding = "Chrome" + +# De-brand: build as Chromium (no Google branding); the consumer "Google Chrome" +# identity is applied at runtime via --uxr-* switches, not baked in. +is_chrome_branded = false + +# No PGO profile dependency (we build from source without Google's PGO data). +chrome_pgo_phase = 0 + +# Trim weight / attack surface. +enable_nacl = false +enable_widevine = false + +# Target: native linux/arm64 (aarch64). Runs under Lima/QEMU on Apple Silicon. +target_cpu = "arm64" +target_os = "linux" + +# Broad-CPU compatibility: N/A on arm64. +# The x64 args.gn pins the tree back to the SSE4.2 (x86-64-v2) baseline via +# -mno-avx/-mno-bmi/-mno-lzcnt/... to keep pre-Haswell Intel CPUs from tripping a +# PA_CHECK at static init. Those are x86-only flags (clang errors on them for +# aarch64) and the failure mode doesn't exist on ARM. Chromium's default arm64 +# baseline is ARMv8-A, already the broadest target — Apple M-series (ARMv8.5+/v9) +# and QEMU's cortex-a/max models are all supersets — so NO extra_cflags are needed. + +# ThinLTO is on by default for official builds; left implicit. diff --git a/build/build.sh b/build/build.sh index 8e2ec0d..544c8bf 100755 --- a/build/build.sh +++ b/build/build.sh @@ -10,7 +10,13 @@ set -euo pipefail REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" WORK="${1:-$REPO/.fortress-build}" -ARGS_FILE="$REPO/build/args.gn" +# Select GN args by host architecture. args.arm64.gn is identical to args.gn +# except for the architecture-bound bits (target_cpu + no x86 baseline cflags); +# all stealth patches are architecture-neutral. Override with ARGS_FILE=... . +case "$(uname -m)" in + aarch64|arm64) ARGS_FILE="${ARGS_FILE:-$REPO/build/args.arm64.gn}" ;; + *) ARGS_FILE="${ARGS_FILE:-$REPO/build/args.gn}" ;; +esac OUT="Fortress" VER="${CHROMIUM_VERSION:-$(cat "$REPO/CHROMIUM_VERSION")}" export TMPDIR="${TMPDIR:-$WORK/tmp}" From fef37454253fb900b86c73fb3e4a773f5936d331 Mon Sep 17 00:00:00 2001 From: MT-GoCode Date: Sun, 19 Jul 2026 16:46:31 +0000 Subject: [PATCH 2/4] build(arm64): add native aarch64 build recipe + robust patch apply - build/build-arm64.sh: end-to-end native linux/arm64 build. Chromium ships no aarch64-linux prebuilt clang or rust, so this builds clang from source, uses a matching rustup nightly, and provisions arm64 node/gperf/cmake. Verified to reproduce the release binary bit-for-bit from a clean tree. - build/apply-patches.sh: use 'git apply --recount' so a stale hunk header (0005) still anchors; all 35 patches apply cleanly. Helps x64 too. Co-Authored-By: Claude Opus 4.8 --- build/apply-patches.sh | 4 +- build/build-arm64.sh | 161 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+), 2 deletions(-) create mode 100755 build/build-arm64.sh diff --git a/build/apply-patches.sh b/build/apply-patches.sh index 8bcfbee..7316814 100755 --- a/build/apply-patches.sh +++ b/build/apply-patches.sh @@ -13,9 +13,9 @@ ok=0; fail=0; failed=() while IFS= read -r rel; do [ -z "$rel" ] && continue p="$REPO/$rel" - if git apply --3way --whitespace=nowarn "$p" 2>/dev/null; then + if git apply --recount --3way --whitespace=nowarn "$p" 2>/dev/null; then ok=$((ok+1)); printf ' [ok] %s\n' "$(basename "$rel")" - elif git apply --check "$p" 2>/dev/null && git apply --whitespace=nowarn "$p" 2>/dev/null; then + elif git apply --recount --check "$p" 2>/dev/null && git apply --recount --whitespace=nowarn "$p" 2>/dev/null; then ok=$((ok+1)); printf ' [ok] %s\n' "$(basename "$rel")" else fail=$((fail+1)); failed+=("$rel"); printf ' [FAIL] %s\n' "$(basename "$rel")" diff --git a/build/build-arm64.sh b/build/build-arm64.sh new file mode 100755 index 0000000..8cfb7e4 --- /dev/null +++ b/build/build-arm64.sh @@ -0,0 +1,161 @@ +#!/usr/bin/env bash +# Fortress — native linux/arm64 (aarch64) build. +# +# Chromium ships NO prebuilt aarch64-linux clang or rust, so a native arm64 build +# must build the clang toolchain from source and supply an arm64 rust. This script +# encodes every arm64-specific step so the max-stealth Fortress Chromium (all 34+ +# C++ patches, unchanged) can be reproduced on an aarch64 host (e.g. AWS Graviton, +# or a Linux arm64 VM for running under Lima/QEMU on Apple Silicon). +# +# Requirements: aarch64 Ubuntu 22.04/24.04 host, ~200 GB free disk, many cores, +# passwordless sudo (for apt). Usage: build/build-arm64.sh [workdir] +# +# The stealth C++ patches are architecture-neutral (JS fingerprint surfaces) and +# apply unchanged; only the toolchain/build plumbing below is arm64-specific. +set -euo pipefail +REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +WORK="${1:-$REPO/.fortress-build}" +VER="${CHROMIUM_VERSION:-$(cat "$REPO/CHROMIUM_VERSION")}" +OUT="Fortress" +# Rust nightly matching the DATE of Chromium's pinned rust commit (tools/rust/update_rust.py). +# For Chromium 151.0.7908.0 that commit is dated 2026-06-16 -> nightly-2026-06-17. +RUST_DATE="${RUST_DATE:-2026-06-17}" +RUST_TC="$HOME/.rustup/toolchains/nightly-${RUST_DATE}-aarch64-unknown-linux-gnu" +export TMPDIR="${TMPDIR:-$WORK/tmp}" +mkdir -p "$WORK" "$TMPDIR" +export GCLIENT_SUPPRESS_GIT_VERSION_WARNING=1 +ts(){ date -u +%H:%M:%S; } +[ "$(uname -m)" = "aarch64" ] || { echo "This script is for aarch64 hosts."; exit 1; } +echo "==> Fortress arm64 build | Chromium $VER | workdir $WORK" + +# 0. Host packages: Chromium's -dev libs (needed since we build against host libs, +# use_sysroot=false), plus the toolchain build prerequisites. +echo "[$(ts)] apt prerequisites" +sudo apt-get update -y +sudo apt-get install -y \ + cmake ninja-build lld gperf libclang-dev curl xz-utils python3 git pkg-config \ + libpipewire-0.3-dev libva-dev libgbm-dev libdrm-dev libpulse-dev libspeechd-dev \ + libxkbcommon-dev libxdamage-dev libxrandr-dev libxcomposite-dev libxcursor-dev \ + libxtst-dev libxss-dev libcups2-dev libgtk-3-dev libnss3-dev libasound2-dev \ + libatk1.0-dev libatk-bridge2.0-dev libpango1.0-dev libcairo2-dev libatspi2.0-dev \ + libdbus-1-dev libudev-dev libwayland-dev libglib2.0-dev libkrb5-dev libncurses-dev \ + libxshmfence-dev libx11-xcb-dev libxcb-dri3-dev + +# 1. depot_tools +if [ ! -d "$WORK/depot_tools" ]; then + git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git "$WORK/depot_tools" +fi +export PATH="$WORK/depot_tools:$HOME/.cargo/bin:$PATH" + +# 2. fetch Chromium + sync (with the arm64 workarounds) +if [ ! -d "$WORK/chromium/src" ]; then + mkdir -p "$WORK/chromium" + # `fetch` runs an internal `gclient sync` that aborts on the gperf CIPD gap + # (there is no linux-arm64 gperf package). That abort is expected and harmless + # here: fetch has already checked out src/ + DEPS by the time cipd runs, which is + # all we need before we patch DEPS and run our own sync below. Tolerate the exit. + ( cd "$WORK/chromium" && fetch --nohooks --no-history chromium ) || true + [ -f "$WORK/chromium/src/DEPS" ] || { echo "fetch did not lay down src/DEPS"; exit 1; } +fi +# Unmanaged solution so gclient won't reset our tag checkout / DEPS edit below. +cat > "$WORK/chromium/.gclient" <<'GC' +solutions = [ + { "name": "src", + "url": "https://chromium.googlesource.com/chromium/src.git", + "managed": False, "custom_deps": {}, "custom_vars": {} }, +] +GC +cd "$WORK/chromium/src" +git fetch --depth 1 origin "refs/tags/$VER:refs/tags/$VER" +git checkout -f "tags/$VER" +# gperf has NO linux-arm64 CIPD package (only linux-amd64), which aborts `gclient sync`. +# Point the dep at linux-amd64 so cipd resolves; we overwrite it with native arm64 gperf. +python3 - <<'PY' +p="DEPS"; s=open(p).read() +o="infra/3pp/tools/gperf/${{platform}}"; n="infra/3pp/tools/gperf/linux-amd64" +open(p,"w").write(s.replace(o,n)) if o in s else None +PY +gclient sync -D --no-history +gclient runhooks +./build/install-build-deps.sh --no-prompt || true +# Native arm64 gperf at the path blink's scripts.gni expects. +mkdir -p third_party/gperf/cipd/bin +cp -f "$(command -v gperf)" third_party/gperf/cipd/bin/gperf + +# 3. Swap the x64 node for the pinned arm64 node (DEPS ships linux-x64 only). +NODE_VER="$(grep -oE 'NODE_VERSION="v[0-9.]+"' third_party/node/update_node_binaries | head -1 | grep -oE 'v[0-9.]+')" +echo "[$(ts)] node $NODE_VER (arm64 swap)" +curl -fsSL -o "$TMPDIR/node.tar.xz" "https://nodejs.org/dist/${NODE_VER}/node-${NODE_VER}-linux-arm64.tar.xz" +tar -C "$TMPDIR" -xf "$TMPDIR/node.tar.xz" +cp -f "$TMPDIR/node-${NODE_VER}-linux-arm64/bin/node" third_party/node/linux/node-linux-x64/bin/node + +# 4. Build clang from source (no aarch64-linux prebuilt exists). +LLVM="third_party/llvm-build/Release+Asserts" +if [ ! -x "$LLVM/bin/clang" ] || [ "$(file -b "$LLVM/bin/clang" | grep -c aarch64)" = 0 ]; then + echo "[$(ts)] building clang from source (this takes a while)" + # build.py fetches an x86-64 cmake; replace with a native arm64 cmake at the same path. + python3 tools/clang/scripts/build.py --without-android --without-fuchsia \ + --host-cc=/usr/bin/gcc --host-cxx=/usr/bin/g++ --disable-asserts \ + --with-ml-inliner-model='' 2>/dev/null || true # first pass fetches sources, then fails on x86 cmake + CMV=3.26.4 + if [ -e "third_party/llvm/../llvm-build-tools/cmake-${CMV}-linux-x86_64/bin/cmake" ] && \ + file -b "third_party/llvm/../llvm-build-tools/cmake-${CMV}-linux-x86_64/bin/cmake" | grep -q x86-64; then + D="third_party/llvm/../llvm-build-tools"; rm -rf "$D/cmake-${CMV}-linux-x86_64" + curl -fsSL -o "$TMPDIR/cmake.tgz" "https://github.com/Kitware/CMake/releases/download/v${CMV}/cmake-${CMV}-linux-aarch64.tar.gz" + tar -C "$D" -xf "$TMPDIR/cmake.tgz"; mv "$D/cmake-${CMV}-linux-aarch64" "$D/cmake-${CMV}-linux-x86_64" + fi + # --with-ml-inliner-model='' : no arm64 TensorFlow wheel (compiler-internal opt; no effect on output). + python3 tools/clang/scripts/build.py --without-android --without-fuchsia \ + --host-cc=/usr/bin/gcc --host-cxx=/usr/bin/g++ --disable-asserts \ + --with-ml-inliner-model='' --skip-checkout +fi + +# 5. Rust: no aarch64-linux prebuilt either. Use a rustup nightly matching Chromium's rust date. +if ! command -v rustup >/dev/null 2>&1; then + curl -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain none --profile minimal + export PATH="$HOME/.cargo/bin:$PATH" +fi +rustup toolchain install "nightly-${RUST_DATE}" --profile minimal -c rust-src -c rustfmt \ + --target aarch64-unknown-linux-gnu +BINDGEN_VER="$(grep -oE 'corresponds to [0-9.]+' tools/rust/build_bindgen.py | grep -oE '[0-9.]+' || echo 0.72.1)" +[ -x "$LLVM/bin/bindgen" ] || cargo install "bindgen-cli@${BINDGEN_VER}" --locked --root "$WORK/bindgen" +cp -f "$WORK/bindgen/bin/bindgen" "$LLVM/bin/bindgen" 2>/dev/null || true +# bindgen needs a shared libclang (build.py makes only libclang.a) + rustfmt, at rust_bindgen_root. +ln -sf "$(ls /usr/lib/llvm-*/lib/libclang.so | head -1)" "$LLVM/lib/libclang.so" +ln -sf "$RUST_TC/bin/rustfmt" "$LLVM/bin/rustfmt" + +# 6. Apply the Fortress stealth patch series (architecture-neutral, unchanged). +"$REPO/build/apply-patches.sh" "$WORK/chromium/src" + +# 6b. bindgen here uses the system libclang (older than the in-tree clang), which +# rejects a few newer clang-only cflags. They are diagnostic/sanitizer/codegen flags +# irrelevant to bindgen's type extraction; drop them in Chromium's clang-arg filter. +FCA="third_party/../build/rust/gni_impl/filter_clang_args.py" +grep -q "fno-lifetime-dse" "$FCA" || python3 - "$FCA" <<'PY' +import sys +p=sys.argv[1]; s=open(p).read() +a=" elif args[i] == '-ftime-trace':\n pass\n" +add=(" elif args[i] in ('-fdiagnostics-show-inlining-chain', '-fno-lifetime-dse'):\n pass\n" + " elif (args[i].startswith('--warning-suppression-mappings') or\n" + " args[i].startswith('-fsanitize-ignore-for-ubsan-feature')):\n pass\n") +open(p,"w").write(s.replace(a,a+add,1)) +PY + +# 7. Configure (stealth args + arm64 toolchain args) and build. +# rustc_version must be space-free (custom toolchain uses it verbatim as rustc_revision). +RUSTC_VER="$("$RUST_TC/bin/rustc" -V | sed 's/rustc //; s/[() ]/-/g; s/-\{2,\}/-/g; s/-$//')" +gn gen "out/$OUT" --args="$(cat "$REPO/build/args.arm64.gn") +clang_base_path = \"//third_party/llvm-build/Release+Asserts\" +clang_use_chrome_plugins = false +rust_sysroot_absolute = \"$RUST_TC\" +rustc_version = \"$RUSTC_VER\" +rust_bindgen_root = \"//third_party/llvm-build/Release+Asserts\" +rust_force_head_revision = true +toolchain_supports_rust_thin_lto = false +use_sysroot = false +use_siso = false +treat_warnings_as_errors = false" +autoninja -C "out/$OUT" chrome chrome_crashpad_handler + +echo "==> Done: $WORK/chromium/src/out/$OUT/chrome" +"$WORK/chromium/src/out/$OUT/chrome" --version || true From 7c09ed0ddd9cea53f157e9f815cff06d1a44a4b0 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sun, 19 Jul 2026 17:11:29 +0000 Subject: [PATCH 3/4] build(arm64): retry gclient sync on transient network failures gclient sync pulls hundreds of deps and intermittently fails on busy git mirrors / HTTP 429. Retry with backoff (each retry resumes) so a flaky network doesn't abort the whole build. Co-Authored-By: Claude Opus 4.8 --- build/build-arm64.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/build/build-arm64.sh b/build/build-arm64.sh index 8cfb7e4..e535e99 100755 --- a/build/build-arm64.sh +++ b/build/build-arm64.sh @@ -75,7 +75,15 @@ p="DEPS"; s=open(p).read() o="infra/3pp/tools/gperf/${{platform}}"; n="infra/3pp/tools/gperf/linux-amd64" open(p,"w").write(s.replace(o,n)) if o in s else None PY -gclient sync -D --no-history +# gclient sync pulls hundreds of git/CIPD deps and can hit transient network +# failures (busy mirrors, HTTP 429 rate limiting). Retry with backoff; each retry +# resumes where the last left off, so only the incomplete deps are re-fetched. +for attempt in 1 2 3 4 5; do + gclient sync -D --no-history && break + [ "$attempt" = 5 ] && { echo "gclient sync failed after 5 attempts"; exit 1; } + echo "[$(ts)] gclient sync attempt $attempt failed (transient?); retrying in $((attempt*30))s" + sleep $((attempt*30)) +done gclient runhooks ./build/install-build-deps.sh --no-prompt || true # Native arm64 gperf at the path blink's scripts.gni expects. From 594fd7233665d5813a342d1cbe4a160de66b6a5f Mon Sep 17 00:00:00 2001 From: MT-GoCode Date: Wed, 22 Jul 2026 02:04:31 +0000 Subject: [PATCH 4/4] build(arm64): link against Chromium's bullseye sysroot for old-glibc compat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The native arm64 recipe built with use_sysroot=false, so chrome linked against the build host's libraries (Ubuntu 24.04: glibc 2.39 / GCC 14). glibc symbol versioning is forward-only, so that binary demanded GLIBC_2.38 / GCC_14.0 and refused to load on older runtimes — e.g. Debian 12 "bookworm" (glibc 2.36), where the loader bailed before main() (silent exit 1, no window). Fix: build against Chromium's bundled arm64 sysroot (Debian bullseye, glibc 2.31) — use_sysroot=true, plus an explicit install-sysroot.py --arch=arm64 (runhooks skips it when host arch == target). The resulting binary caps at GLIBC_2.25 / GCC_4.5, so it runs on Debian 11/12/13+ from one artifact. This is how official Google Chrome arm64 Linux builds achieve broad-distro support. No source or patch changes; all stealth patches unchanged. x64 build untouched (it already uses the default amd64 sysroot). Verified: loads google.com over CDP and projects the persona on an aarch64 host. Co-Authored-By: Claude Opus 4.8 --- build/build-arm64.sh | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/build/build-arm64.sh b/build/build-arm64.sh index e535e99..7c8dcad 100755 --- a/build/build-arm64.sh +++ b/build/build-arm64.sh @@ -28,8 +28,9 @@ ts(){ date -u +%H:%M:%S; } [ "$(uname -m)" = "aarch64" ] || { echo "This script is for aarch64 hosts."; exit 1; } echo "==> Fortress arm64 build | Chromium $VER | workdir $WORK" -# 0. Host packages: Chromium's -dev libs (needed since we build against host libs, -# use_sysroot=false), plus the toolchain build prerequisites. +# 0. Host packages: toolchain build prerequisites. The target libraries come from +# Chromium's arm64 sysroot (installed below), so the -dev libs here are only +# belt-and-suspenders for host tools; harmless to keep. echo "[$(ts)] apt prerequisites" sudo apt-get update -y sudo apt-get install -y \ @@ -86,6 +87,11 @@ for attempt in 1 2 3 4 5; do done gclient runhooks ./build/install-build-deps.sh --no-prompt || true +# Install Chromium's bundled arm64 sysroot (Debian bullseye, glibc 2.31) and build +# against it (use_sysroot=true below) so the binary links to old glibc/libgcc and +# runs on Debian 11/12/13+ — not just this host's glibc. runhooks skips it when the +# host arch != target, so install it explicitly. +python3 build/linux/sysroot_scripts/install-sysroot.py --arch=arm64 # Native arm64 gperf at the path blink's scripts.gni expects. mkdir -p third_party/gperf/cipd/bin cp -f "$(command -v gperf)" third_party/gperf/cipd/bin/gperf @@ -160,7 +166,7 @@ rustc_version = \"$RUSTC_VER\" rust_bindgen_root = \"//third_party/llvm-build/Release+Asserts\" rust_force_head_revision = true toolchain_supports_rust_thin_lto = false -use_sysroot = false +use_sysroot = true use_siso = false treat_warnings_as_errors = false" autoninja -C "out/$OUT" chrome chrome_crashpad_handler