Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
out/
src/
depot_tools/
.fortress-build/
*.tar.gz
*.deb
*.AppImage
Expand Down
4 changes: 2 additions & 2 deletions build/apply-patches.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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")"
Expand Down
50 changes: 50 additions & 0 deletions build/args.arm64.gn
Original file line number Diff line number Diff line change
@@ -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.
175 changes: 175 additions & 0 deletions build/build-arm64.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
#!/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: 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 \
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 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
# 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

# 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 = true
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
8 changes: 7 additions & 1 deletion build/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down