1603 lines
67 KiB
Bash
1603 lines
67 KiB
Bash
#!/bin/bash
|
|
# BTC.sh requires GNU Bash 4.0+ (not POSIX sh). It uses:
|
|
# - [[ ... ]] conditionals, == pattern matching, =~ regex, (( )) arithmetic
|
|
# - ${var^^} uppercase expansion, associative arrays, process substitution
|
|
# - set -euo pipefail for strict error handling
|
|
# BTC-${BTC_VERSION} - Build Tool Chain
|
|
# Identity: dcosnet / dcos.net | Multi-Arch Cross-Compilation Build Tool Chain
|
|
# Version: 0.4.2 | Persistence: /opt/BTC | Volatile: ramfs
|
|
# License: GNU AGPLv3 Mandatory Prominent Interactive Notice
|
|
# Copyright (C) 2012-2026 Jeremy Anderson (info@dcos.net)
|
|
|
|
set -euo pipefail
|
|
export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# Single source of truth for the BTC version. Every banner, header comment,
|
|
# manifest, and --help text MUST reference this constant — never hardcode the
|
|
# version string in multiple places.
|
|
# ----------------------------------------------------------------------------
|
|
readonly BTC_VERSION="0.4.2"
|
|
|
|
# ============================================================================
|
|
# 1. AGPL INTERACTIVE LICENSE COMPLIANCE
|
|
# ============================================================================
|
|
function f_agpl_header() {
|
|
clear
|
|
cat << EOF
|
|
===========================================================================
|
|
BTC-${BTC_VERSION} - Build Tool Chain (AGPLv3 PROTECTED)
|
|
Cross-Compilation Build Tool Chain
|
|
===========================================================================
|
|
This program is free software: you can redistribute it and/or modify it
|
|
under the terms of the GNU Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License.
|
|
|
|
REMOTE INTERACTION NOTICE: Per Section 13 of the GNU AGPLv3, if you modify
|
|
this script and offer its toolchain-building capabilities as a service over
|
|
a network, you MUST make your complete modified source code available.
|
|
===========================================================================
|
|
EOF
|
|
if [[ ! -f /var/tmp/BTC-AGPL-ACCEPTED ]]; then
|
|
echo -n "Do you accept the network-sovereignty terms of the AGPLv3? (y/N): "
|
|
read -r reply
|
|
if [[ "${reply}" =~ ^[Yy]$ ]]; then
|
|
touch /var/tmp/BTC-AGPL-ACCEPTED
|
|
else
|
|
echo ">> Build aborted: AGPLv3 acceptance is mandatory for execution."
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# --- Signature Tier Selection ---
|
|
# Skip prompt if tier was already specified via CLI flags.
|
|
if [[ ${BTC_TPM_SEAL} -eq 0 && -z "${BTC_CLUSTER_JOIN:-}" && ! -f "${BTC_ARCHIVE:-/opt/BTC}/.btc-salt" && ! -f "${BTC_ARCHIVE:-/opt/BTC}/.btc-cluster-token" ]]; then
|
|
echo ""
|
|
echo ">> Select forensic signature tier:"
|
|
echo ">> 1) poly — Per-machine random salt (write-once, default)"
|
|
echo ">> 2) tpm — Hardware-bound PCR digest (singular deploys)"
|
|
echo ">> 3) cluster — Join existing cluster with shared token"
|
|
echo -n ">> Choice [1]: "
|
|
read -r sig_choice
|
|
case "${sig_choice:-1}" in
|
|
2)
|
|
BTC_TPM_SEAL=1
|
|
echo ">> [SIG] TPM seal selected."
|
|
;;
|
|
3)
|
|
echo -n ">> Enter cluster token: "
|
|
read -r cluster_tok
|
|
if [[ -n "${cluster_tok}" ]]; then
|
|
export BTC_CLUSTER_JOIN="${cluster_tok}"
|
|
echo ">> [SIG] Cluster join selected."
|
|
else
|
|
echo ">> [WARN] Empty token. Defaulting to poly."
|
|
fi
|
|
;;
|
|
*)
|
|
echo ">> [SIG] Poly (per-machine salt) selected."
|
|
;;
|
|
esac
|
|
echo ""
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# 2. CROSS-COMPILATION TARGET REGISTRY
|
|
#
|
|
# Architecture | Target ID | GCC march | ISA | C Library
|
|
# ------------- | ------------------- | ------------------ | ------ | ----------
|
|
# x86_64 | haswell | haswell | AVX2 | glibc
|
|
# x86_64 | haswell-ep | haswell | AVX2 | glibc
|
|
# x86_64 | broadwell | broadwell | AVX2 | glibc
|
|
# x86_64 | broadwell-ep | broadwell | AVX2 | glibc
|
|
# x86_64 | skylake | skylake | AVX2 | glibc
|
|
# x86_64 | skylake-x | skylake-avx512 | AVX512 | glibc
|
|
# x86_64 | skylake-server | skylake-server | AVX512 | glibc
|
|
# x86_64 | znver1 | znver1 | AVX2 | glibc
|
|
# x86_64 | znver2 | znver2 | AVX2 | glibc
|
|
# x86_64 | znver3 | znver3 | AVX2 | glibc
|
|
# x86_64 | znver4 | znver4 | AVX512 | glibc
|
|
# x86_64 | apu-zn1 | znver1 | AVX2 | glibc
|
|
# x86_64 | apu-zn2 | znver1 | AVX2 | glibc
|
|
# x86_64 | apu-zn3 | znver2 | AVX2 | glibc
|
|
# x86_64 | apu-zn4 | znver3 | AVX2 | glibc
|
|
# x86_64 | atom-silvermont | silvermont | SSE4_2| glibc
|
|
# x86_64 | atom-goldmont | goldmont | SSE4_2| glibc
|
|
# x86_64 | atom-tremont | tremont | SSE4_2| glibc
|
|
# x86_64 | atom-sierraforest | sierraforest | SSE4_2| glibc
|
|
# mipsel | mipselr2 | mips32r2 | MIPS32 | musl
|
|
# arm | armv7 | armv7-a | NEON | musl
|
|
# tilegx | tilegx | tilegx | TILE | musl
|
|
#
|
|
# Host is always an average x86_64 system. All targets listed above
|
|
# are CROSS-COMPILATION targets (CROSS_MODE=1). The --native flag
|
|
# auto-probes the host silicon and builds without a target prefix.
|
|
#
|
|
# Cross targets use SYSROOT-based cross-compilation (CLFS / Buildroot style).
|
|
# ============================================================================
|
|
|
|
# Associative array: target_id → property string
|
|
# Format: "arch|cpu|march|isa|abi|clib|endian|family|desc|kernel_min|gcc_cfg_extra"
|
|
declare -A BTC_TARGETS
|
|
|
|
# --- x86_64 Intel family ---
|
|
BTC_TARGETS[haswell]="x86_64|i686|haswell|AVX2|sysv|glibc|little|intel|Intel Haswell (Xeon E5 v3 / Core i7-4xxx)|4.19|"
|
|
BTC_TARGETS[haswell-ep]="x86_64|i686|haswell|AVX2|sysv|glibc|little|intel|Intel Haswell-EP X99 (Xeon E5 v3 / E7 v3)|4.19|"
|
|
BTC_TARGETS[broadwell]="x86_64|i686|broadwell|AVX2|sysv|glibc|little|intel|Intel Broadwell (Core i7-5xxx / Core i5-5xxx / Xeon E5 v4)|4.19|"
|
|
BTC_TARGETS[broadwell-ep]="x86_64|i686|broadwell|AVX2|sysv|glibc|little|intel|Intel Broadwell-EP (Xeon E5-2600 v4 / E7-4800 v4)|4.19|"
|
|
BTC_TARGETS[skylake]="x86_64|i686|skylake|AVX2|sysv|glibc|little|intel|Intel Skylake (Core i7-6xxx / Xeon v5)|4.19|"
|
|
BTC_TARGETS[skylake-x]="x86_64|i686|skylake-avx512|AVX512|sysv|glibc|little|intel|Intel Skylake-X X299 (Core i9-7xxx Xeon Scalable)|4.19|"
|
|
BTC_TARGETS[skylake-server]="x86_64|i686|skylake-server|AVX512|sysv|glibc|little|intel|Intel Skylake-Server (Xeon SP 1st/2nd Gen Platinum)|4.19|"
|
|
|
|
# --- x86_64 AMD family ---
|
|
BTC_TARGETS[znver1]="x86_64|i686|znver1|AVX2|sysv|glibc|little|amd|AMD Zen1 Ryzen (Ryzen 1000 / EPYC Naples)|4.19|"
|
|
BTC_TARGETS[znver2]="x86_64|i686|znver2|AVX2|sysv|glibc|little|amd|AMD Zen2 Ryzen (Ryzen 3000 / EPYC Rome)|4.19|"
|
|
BTC_TARGETS[znver3]="x86_64|i686|znver3|AVX2|sysv|glibc|little|amd|AMD Zen3 Ryzen (Ryzen 5000 / EPYC Milan)|4.19|"
|
|
BTC_TARGETS[znver4]="x86_64|i686|znver4|AVX512|sysv|glibc|little|amd|AMD Zen4 Ryzen (Ryzen 7000 / EPYC Genoa)|4.19|"
|
|
|
|
# --- x86_64 AMD APU family (mobile/embedded Zen, 15-54W TDP) ---
|
|
# APU series 1 (Raven Ridge, 2400GE/3200GE) = Zen 1, AVX2, Vega graphics
|
|
# APU series 2 (Picasso, 3250U/3500U) = Zen+, AVX2 (GCC march=znver1)
|
|
# APU series 3 (Renoir/Lucienne, 4500U/4700U)= Zen 2, AVX2
|
|
# APU series 4 (Cezanne/Barcelo, 5500U/5700U) = Zen 3, AVX2
|
|
# These are cross-compiled from an average x86_64 host for deployment
|
|
# on APU-based mini-PCs, laptops, and embedded nodes.
|
|
BTC_TARGETS[apu-zn1]="x86_64|i686|znver1|AVX2|sysv|glibc|little|amd-apu|AMD APU Series 1 Zen (Raven Ridge 2400GE / 3200GE Vega)|4.19|"
|
|
BTC_TARGETS[apu-zn2]="x86_64|i686|znver1|AVX2|sysv|glibc|little|amd-apu|AMD APU Series 2 Zen+ (Picasso 3250U / 3500U)|4.19|"
|
|
BTC_TARGETS[apu-zn3]="x86_64|i686|znver2|AVX2|sysv|glibc|little|amd-apu|AMD APU Series 3 Zen2 (Renoir 4500U / 4700U)|4.19|"
|
|
BTC_TARGETS[apu-zn4]="x86_64|i686|znver3|AVX2|sysv|glibc|little|amd-apu|AMD APU Series 4 Zen3 (Cezanne 5500U / 5700U)|4.19|"
|
|
|
|
# --- x86_64 Intel Atom family (low-power embedded, 4-15W TDP) ---
|
|
# silvermont = Bay Trail (Z3000 series, E38xx) — in-order, SSE4.2
|
|
# goldmont = Apollo Lake (x5-Z8350, N4200) — out-of-order, SSE4.2
|
|
# tremont = Elkhart Lake (x6000E series) — improved OoO, SSE4.2
|
|
# sierraforest= Sierra Forest (x7000RE, E-core) — hybrid, SSE4.2, GCC 14+
|
|
# All Atom targets are cross-compiled for edge/IoT gateways, routers,
|
|
# and low-power cluster nodes where AVX is not available.
|
|
BTC_TARGETS[atom-silvermont]="x86_64|i686|silvermont|SSE4_2|sysv|glibc|little|atom|Intel Atom Silvermont (Bay Trail Z3000 / E38xx)|4.14|"
|
|
BTC_TARGETS[atom-goldmont]="x86_64|i686|goldmont|SSE4_2|sysv|glibc|little|atom|Intel Atom Goldmont (Apollo Lake x5-Z8350 / N4200)|4.14|"
|
|
BTC_TARGETS[atom-tremont]="x86_64|i686|tremont|SSE4_2|sysv|glibc|little|atom|Intel Atom Tremont (Elkhart Lake x6000E series)|5.4|"
|
|
BTC_TARGETS[atom-sierraforest]="x86_64|i686|sierraforest|SSE4_2|sysv|glibc|little|atom|Intel Atom Sierra Forest (x7000RE E-core cluster)|6.1|"
|
|
|
|
# --- MIPS (little-endian, soft-float baseline) ---
|
|
# mips32r2 is the ISA baseline for mipselr2 — covers the MALTA-like
|
|
# embedded targets that are the closest thing to a "universal mips"
|
|
# reference platform. Uses musl because glibc MIPS support is
|
|
# fragmented across kernel versions and vendor patches.
|
|
BTC_TARGETS[mipselr2]="mipsel|mips|32r2|MIPS32|o32|musl|little|mips|MIPS32R2 Little-Endian (MALTA / embedded routers)|4.9|--with-arch=mips32r2 --with-float=soft --with-abi=32 --disable-libsanitizer"
|
|
|
|
# --- ARMv7 (hard-float, Thumb-2, NEON) ---
|
|
# armv7-a with NEON and VFPv3-D16 is the "x86 baseline" of the ARM
|
|
# world — it covers Raspberry Pi 2/3 (32-bit), BeagleBone, Odroid,
|
|
# and virtually every Cortex-A7/A9/A15/A17 SoC. Uses musl for
|
|
# cross-compile portability; glibc armv7 is available as a future
|
|
# clib variant.
|
|
BTC_TARGETS[armv7]="arm|arm|armv7-a|NEON|eabihf|musl|little|arm|ARMv7-A Hard-Float NEON (Cortex-A7/A9/A15 RPi2/3 32b)|4.9|--with-arch=armv7-a --with-fpu=vfpv3-d16 --with-float=hard --with-mode=thumb --enable-target-optspace --disable-libsanitizer --with-abi=aapcs-linux"
|
|
|
|
# --- TileGX (Tilera TILE-Gx72/Metor) ---
|
|
# The Tile architecture is a 64-bit VLIW mesh network processor.
|
|
# GCC upstream dropped mainline Tile-Gx support after GCC 11, so
|
|
# we pin GCC 10.3.0 for Tile-Gx targets. The tilegx triple uses
|
|
# linux-gnu-abi64. Uses musl as glibc has no Tile-Gx port.
|
|
BTC_TARGETS[tilegx]="tilegx|tilegx|tilegx|TILE|abi64|musl|little|tile|Tilera TILE-Gx (TILE-Gx72 / TilePro mesh VLIW)|4.14|--with-arch=tilegx --disable-libssp --disable-libquadmath --disable-libatomic"
|
|
|
|
# --- Helper: list all registered targets ---
|
|
function f_list_targets() {
|
|
echo ">> BTC-${BTC_VERSION} Registered Cross-Compilation Targets:"
|
|
echo ">> ==============================================="
|
|
printf ">> %-16s %-10s %-18s %-8s %-6s %s\n" "TARGET_ID" "ARCH" "MARCH" "ISA" "CLIB" "DESCRIPTION"
|
|
printf ">> %-16s %-10s %-18s %-8s %-6s %s\n" "--------" "----" "-----" "---" "----" "-----------"
|
|
for tid in $(echo "${!BTC_TARGETS[@]}" | tr ' ' '\n' | sort); do
|
|
IFS='|' read -r arch cpu march isa abi clib endian family desc kern_min gcc_extra <<< "${BTC_TARGETS[${tid}]}"
|
|
printf ">> %-16s %-10s %-18s %-8s %-6s %s\n" "${tid}" "${arch}" "${march}" "${isa}" "${clib}" "${desc}"
|
|
done
|
|
echo ""
|
|
echo ">> Usage: BTC.sh <target_id> Build cross-toolchain for target"
|
|
echo ">> BTC.sh --native Auto-probe host silicon and build native"
|
|
echo ">> BTC.sh --list Show this target table"
|
|
echo ">> BTC.sh --list-json Emit target table as JSON"
|
|
}
|
|
|
|
function f_list_targets_json() {
|
|
echo '['
|
|
local first=true
|
|
for tid in $(echo "${!BTC_TARGETS[@]}" | tr ' ' '\n' | sort); do
|
|
IFS='|' read -r arch cpu march isa abi clib endian family desc kern_min gcc_extra <<< "${BTC_TARGETS[${tid}]}"
|
|
if [[ "${first}" == "true" ]]; then first=false; else echo ','; fi
|
|
cat << TJSEP
|
|
{
|
|
"id": "${tid}",
|
|
"arch": "${arch}",
|
|
"cpu": "${cpu}",
|
|
"march": "${march}",
|
|
"isa": "${isa}",
|
|
"abi": "${abi}",
|
|
"clib": "${clib}",
|
|
"endian": "${endian}",
|
|
"family": "${family}",
|
|
"description": "${desc}",
|
|
"kernel_min": "${kern_min}",
|
|
"gcc_extra": "${gcc_extra}"
|
|
}
|
|
TJSEP
|
|
done
|
|
echo ']'
|
|
}
|
|
|
|
# ============================================================================
|
|
# 3. TARGET PROBE & SELECTION
|
|
# ============================================================================
|
|
|
|
# Resolved target properties (set by f_resolve_target)
|
|
BTC_T_ARCH=""
|
|
BTC_T_CPU=""
|
|
BTC_T_MARCH=""
|
|
BTC_T_ISA=""
|
|
BTC_T_ABI=""
|
|
BTC_T_CLIB=""
|
|
BTC_T_ENDIAN=""
|
|
BTC_T_FAMILY=""
|
|
BTC_T_DESC=""
|
|
BTC_T_KERN_MIN=""
|
|
BTC_T_GCC_EXTRA=""
|
|
BTC_T_ID=""
|
|
CROSS_MODE=0 # 0 = native, 1 = cross
|
|
|
|
function f_resolve_target() {
|
|
local target_id="$1"
|
|
local spec="${BTC_TARGETS[${target_id}]:-}"
|
|
|
|
if [[ -z "${spec}" ]]; then
|
|
echo ">> [ERROR] Unknown target: '${target_id}'"
|
|
echo ">> Run 'BTC.sh --list' for available targets."
|
|
exit 1
|
|
fi
|
|
|
|
IFS='|' read -r BTC_T_ARCH BTC_T_CPU BTC_T_MARCH BTC_T_ISA BTC_T_ABI BTC_T_CLIB BTC_T_ENDIAN BTC_T_FAMILY BTC_T_DESC BTC_T_KERN_MIN BTC_T_GCC_EXTRA <<< "${spec}"
|
|
BTC_T_ID="${target_id}"
|
|
}
|
|
|
|
function f_silicon_probe() {
|
|
echo ">> Interrogating Core Topology and Instruction Extensions..."
|
|
|
|
# Step-down dispatch: explicit target, explicit native, or default native
|
|
case "${BTC_TARGET_ID:-}" in
|
|
--native|'')
|
|
_probe_native
|
|
;;
|
|
*)
|
|
f_resolve_target "${BTC_TARGET_ID}"
|
|
CROSS_MODE=1
|
|
_configure_from_target
|
|
;;
|
|
esac
|
|
}
|
|
|
|
function _probe_native() {
|
|
local RAW_ARCH
|
|
RAW_ARCH=$(gcc -march=native -Q --help=target 2>/dev/null | grep -m1 "march=" | awk '{print $2}') || true
|
|
|
|
if [[ -z "${RAW_ARCH}" || "${RAW_ARCH}" == "x86-64" ]]; then
|
|
RAW_ARCH="haswell"
|
|
fi
|
|
|
|
# Try to match the probed microarch to a registered target
|
|
local probe_lower="${RAW_ARCH,,}"
|
|
local matched="${BTC_TARGETS[${probe_lower}]:-}"
|
|
if [[ -n "${matched}" ]]; then
|
|
matched="${probe_lower}"
|
|
else
|
|
# Select haswell as the deterministic default for unregistered microarchitectures
|
|
echo ">> [WARN] Probed microarch '${RAW_ARCH}' not in target registry. Selecting 'haswell'."
|
|
matched="haswell"
|
|
fi
|
|
|
|
f_resolve_target "${matched}"
|
|
CROSS_MODE=0
|
|
_configure_from_target
|
|
}
|
|
|
|
function _configure_from_target() {
|
|
# Derive the target triple
|
|
case "${BTC_T_ARCH}" in
|
|
x86_64)
|
|
TARGET="x86_64-dcosnet-linux-gnu"
|
|
HOST_ARCH="x86_64-pc-linux-gnu"
|
|
;;
|
|
mipsel)
|
|
TARGET="mipsel-dcosnet-linux-musl"
|
|
HOST_ARCH="x86_64-pc-linux-gnu"
|
|
;;
|
|
arm)
|
|
TARGET="arm-dcosnet-linux-musleabihf"
|
|
HOST_ARCH="x86_64-pc-linux-gnu"
|
|
;;
|
|
tilegx)
|
|
TARGET="tilegx-dcosnet-linux-gnu"
|
|
HOST_ARCH="x86_64-pc-linux-gnu"
|
|
;;
|
|
*)
|
|
TARGET="${BTC_T_ARCH}-dcosnet-linux-gnu"
|
|
HOST_ARCH="x86_64-pc-linux-gnu"
|
|
;;
|
|
esac
|
|
|
|
# ISA tag for SYS_LABEL
|
|
ISA_TAG="${BTC_T_ISA}"
|
|
OPT_TAG="LTO"
|
|
SYS_LABEL="DCOSNET-${BTC_T_ID^^}-${ISA_TAG}-${OPT_TAG}"
|
|
|
|
# Override SYS_LABEL for cross-compiles to include the arch family
|
|
if [[ "${CROSS_MODE}" -eq 1 ]]; then
|
|
SYS_LABEL="DCOSNET-${BTC_T_FAMILY^^}-${BTC_T_ID^^}-${ISA_TAG}-CROSS"
|
|
fi
|
|
|
|
# Resource-Safe Threading: Allocate 2GB RAM per core floor to prevent LTO thrashing
|
|
local total_cpus
|
|
total_cpus=$(nproc)
|
|
local free_gb
|
|
free_gb=$(free -g | awk '/^Mem:/{print $7}')
|
|
local safe_threads=$(( free_gb / 2 ))
|
|
|
|
if [[ ${safe_threads} -lt 1 ]]; then safe_threads=1; fi
|
|
if [[ ${safe_threads} -gt ${total_cpus} ]]; then safe_threads=${total_cpus}; fi
|
|
export v_threads="-j${safe_threads}"
|
|
|
|
# Reset version overrides to the default baseline before per-target
|
|
# pinning. Without this reset, calling _configure_from_target twice
|
|
# in the same process (e.g. test harnesses, or a future batch-build
|
|
# mode) would leak the tile pin into subsequent non-tile targets.
|
|
# Single-target builds are unaffected, but the reset is cheap and
|
|
# makes the function idempotent.
|
|
v_gcc="${v_gcc_default}"
|
|
v_linux="${v_linux_default}"
|
|
v_linux_headers="${v_linux_headers_default}"
|
|
|
|
# Pin GCC and Linux versions for Tile-Gx.
|
|
# GCC upstream dropped mainline Tile-Gx support after GCC 11.
|
|
# Linux upstream removed the tile architecture in 5.9; the last LTS
|
|
# series that still builds for tilegx is 5.4.x. Both overrides MUST run
|
|
# before f_download(), which captures these globals into A_SRC_URL[].
|
|
if [[ "${BTC_T_FAMILY}" == "tile" ]]; then
|
|
v_gcc="${v_gcc_tile}"
|
|
v_linux="${v_linux_tile}"
|
|
v_linux_headers="${v_linux_headers_tile}"
|
|
fi
|
|
|
|
echo ">> [IDENTITY STAMP] ${SYS_LABEL}"
|
|
echo ">> [TARGET] ${BTC_T_ID} — ${BTC_T_DESC}"
|
|
echo ">> [TRIPLE] ${TARGET}"
|
|
echo ">> [C LIBRARY] ${BTC_T_CLIB}"
|
|
local mode_label="NATIVE"
|
|
if [[ "${CROSS_MODE}" -eq 1 ]]; then mode_label="CROSS-COMPILE"; fi
|
|
echo ">> [MODE] ${mode_label}"
|
|
echo ">> [THREAD ALLOCATION] Probed ${total_cpus} cores -> Throttled to ${v_threads} for LTO Safety."
|
|
}
|
|
|
|
# ============================================================================
|
|
# 4. SYSTEM PATHS & STAGING MATRIX
|
|
# ============================================================================
|
|
export SOURCES_ACTIVE=/usr/src
|
|
export BTC_ARCHIVE=/opt/BTC
|
|
export SOURCE_CACHE=${BTC_ARCHIVE}/src
|
|
export RAMDISK_SIZE="12gb"
|
|
|
|
# Upstream Production Matrices (defaults — can be overridden per-target)
|
|
# Versions track the current LFS stable baseline (LFS 13.0+) where practical,
|
|
# bumped forward to the latest point releases that are actually downloadable
|
|
# from upstream mirrors as of ${BTC_VERSION}.
|
|
#
|
|
# - linux-7.1.7 : latest stable point release on the 7.1.x series
|
|
# - binutils-2.46.1 : LFS-aligned binutils point release
|
|
# - gcc-15.3.0 : current GCC stable (LFS 13.0 ships 14.2.0; we bump
|
|
# to 15.3.0 to pick up znver4/sierraforest support
|
|
# and GCC 15 stricter const-correctness)
|
|
# - glibc-2.43 : current glibc stable
|
|
# - musl-1.2.6 : current musl stable
|
|
#
|
|
# Tile-Gx override: tile architecture was REMOVED from mainline Linux in 5.9
|
|
# (commit 65ad263b1d0f). The last LTS series with tile support is 5.4.x, so
|
|
# tile targets are pinned to linux-5.4.302 (LTS) and gcc-10.3.0 (last GCC
|
|
# release with full tile-gx backend). See _configure_from_target() for the
|
|
# runtime override.
|
|
v_linux='linux-7.1.7'
|
|
v_linux_default='linux-7.1.7'
|
|
v_linux_tile='linux-5.4.302'
|
|
v_binutils='binutils-2.46.1'
|
|
v_gcc='gcc-15.3.0'
|
|
v_gcc_default='gcc-15.3.0'
|
|
v_gcc_tile='gcc-10.3.0'
|
|
v_glibc='glibc-2.43'
|
|
v_libxcrypt='4.5.2'
|
|
v_gmp='gmp-6.3.0'
|
|
v_mpfr='mpfr-4.2.2'
|
|
v_mpc='mpc-1.4.0'
|
|
v_musl='musl-1.2.6'
|
|
v_linux_headers="${v_linux}"
|
|
v_linux_headers_default="${v_linux_default}"
|
|
v_linux_headers_tile="${v_linux_tile}"
|
|
|
|
# These are set after f_resolve_target:
|
|
# NEWROOT, LOGS, HOST_ARCH, TARGET, TARGET_ARCH, GLOBAL_CFLAGS, GLOBAL_LDFLAGS
|
|
|
|
function f_set_paths() {
|
|
export NEWROOT="${SOURCES_ACTIVE}/${SYS_LABEL}-cleanroom"
|
|
export LOGS="${BTC_ARCHIVE}/logs/${SYS_LABEL}"
|
|
|
|
# Architecture-specific optimization flags
|
|
# For non-x86_64, march maps to the per-arch value from the target registry
|
|
local march_flag="${BTC_T_MARCH}"
|
|
|
|
# ISA-specific extra flags (table-driven via case — SEI CERT CTR50-JP)
|
|
local isa_extra=""
|
|
case "${BTC_T_ISA}" in
|
|
AVX512) isa_extra=" -mavx512f -mavx512dq -mavx512vl -mavx512bw" ;;
|
|
AVX2) isa_extra=" -mavx2" ;;
|
|
SSE4_2) isa_extra=" -msse4.2" ;;
|
|
NEON) isa_extra=" -mfpu=neon -mfloat-abi=hard" ;;
|
|
MIPS32) isa_extra="" ;;
|
|
TILE) isa_extra="" ;;
|
|
esac
|
|
|
|
# Unified CFLAGS: --sysroot points at NEWROOT for both glibc and musl targets
|
|
export GLOBAL_CFLAGS="-O3 -march=${march_flag}${isa_extra} -flto -ffat-lto-objects --sysroot=${NEWROOT} -pipe"
|
|
export GLOBAL_LDFLAGS="-Wl,-O1 -Wl,--as-needed -flto --sysroot=${NEWROOT}"
|
|
|
|
# For native x86_64 builds, keep the i686 build cpu
|
|
if [[ "${CROSS_MODE}" -eq 0 && "${BTC_T_ARCH}" == "x86_64" ]]; then
|
|
TARGET_ARCH="${BTC_T_MARCH}"
|
|
else
|
|
TARGET_ARCH="${BTC_T_ID}"
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# 5. HARDWARE SENTINEL & TELEMETRY MODULES
|
|
# ============================================================================
|
|
function f_guard() {
|
|
local max_temp=85
|
|
local min_mem=800
|
|
local cur_temp
|
|
local cur_mem
|
|
|
|
# Thermal zones may not exist in containers; guard gracefully
|
|
if [[ -d /sys/class/thermal ]]; then
|
|
cur_temp=$(cat /sys/class/thermal/thermal_zone*/temp 2>/dev/null | head -n1 | awk '{print $1/1000}') || cur_temp=0
|
|
else
|
|
cur_temp=0
|
|
fi
|
|
cur_mem=$(free -m | awk '/^Mem:/{print $7}')
|
|
|
|
if (( ${cur_temp%.*} > max_temp )); then
|
|
echo ">> [WARNING: THERMAL SPIKE] Temp at ${cur_temp}C. Throttling build for cooling phase..."
|
|
sleep 15
|
|
fi
|
|
if [[ ${cur_mem} -lt ${min_mem} ]]; then
|
|
echo ">> [WARNING: MEMORY SATURATION] Free memory at ${cur_mem}MB. Yielding pipeline execution..."
|
|
sleep 20
|
|
fi
|
|
}
|
|
|
|
function f_entropy_shield() {
|
|
# Modern Linux (>= 5.6) initializes the CRNG at boot via getrandom(2)
|
|
# and the kernel's own jitter entropy collector. Since 5.6, the
|
|
# input-pool counter in /proc/sys/kernel/random/entropy_avail is
|
|
# capped at 256 by design — it does NOT reflect "available" entropy
|
|
# in the 2.6-era sense anymore. Any value >= 256 means "CRNG ready,
|
|
# getrandom will return immediately". The historical "1000" threshold
|
|
# is from the 2.6 era when /dev/random could genuinely block.
|
|
#
|
|
# Behavior on a modern box:
|
|
# - entropy_avail will be 256 essentially always (capped)
|
|
# - getrandom(GRND_NONBLOCK) succeeds instantly
|
|
# - The "ENTROPY DEFICIT" warning would fire on EVERY step and the
|
|
# old `sleep 2` blocked the parent pipeline for no reason.
|
|
#
|
|
# Fix: lower the threshold to 256, drop the blocking sleep entirely,
|
|
# fire the jitter injector in the background (fire-and-forget, never
|
|
# blocks the calling build step). If getrandom isn't usable on this
|
|
# host we still benefit from any extra jitter, but we don't pay any
|
|
# wall-clock time for it.
|
|
local min_entropy=256
|
|
local cur_entropy=0
|
|
if [[ -r /proc/sys/kernel/random/entropy_avail ]]; then
|
|
cur_entropy=$(< /proc/sys/kernel/random/entropy_avail) || cur_entropy=0
|
|
fi
|
|
if [[ ${cur_entropy} -gt 0 && ${cur_entropy} -lt ${min_entropy} ]]; then
|
|
echo ">> [ENTROPY] Pool at ${cur_entropy} (< ${min_entropy}); jitter injector fired in background."
|
|
# Fire-and-forget. Never blocks the calling build step.
|
|
# The injector walks /bin /sbin /usr/bin to feed disk-I/O timing
|
|
# jitter into the kernel input pool. If it fails or finds nothing
|
|
# useful, no harm done — getrandom(2) still works on modern kernels.
|
|
{ find /bin /sbin /usr/bin -type f -exec ls -l {} + > /dev/null 2>&1; } &
|
|
disown 2>/dev/null || true
|
|
fi
|
|
}
|
|
|
|
function f_exec_log() {
|
|
local cmd="$1"
|
|
local log_base="$2"
|
|
local log_file="${LOGS}/${log_base}.log"
|
|
|
|
f_entropy_shield
|
|
f_guard
|
|
|
|
echo ">> Executing: ${log_base}"
|
|
# Note: ${cmd} is sourced from internal build functions only (not user input).
|
|
# The trust boundary is the BTC.sh script itself — do not expose f_exec_log
|
|
# as a public API with externally-supplied command strings.
|
|
#
|
|
# Pipeline: bash -c "$cmd" → stdbuf (line-buffer) → pv (progress bar)
|
|
# → tee (log file) → /dev/null (suppress stdout so the terminal stays clean).
|
|
#
|
|
# On failure: the `if !` form suspends `set -e` for the condition test,
|
|
# captures the pipeline's exit status (with `set -o pipefail` this is
|
|
# the rightmost non-zero exit in the pipe — usually bash -c's exit code),
|
|
# then surfaces the last 40 lines of the log file to stderr so the
|
|
# operator sees the actual error inline instead of having to dig
|
|
# through ${LOGS}/<step>.log post-mortem.
|
|
if ! stdbuf -oL -eL bash -c "${cmd}" 2>&1 | \
|
|
pv -t -r -b -N "${log_base}" | \
|
|
tee -a "${log_file}" > /dev/null; then
|
|
echo ">> [FAILED] ${log_base} exited non-zero." >&2
|
|
echo ">> [FAILED] Last 80 lines of ${log_file}:" >&2
|
|
tail -n 80 "${log_file}" >&2 2>/dev/null || true
|
|
echo "" >&2
|
|
echo ">> [FAILED] Lines matching error patterns (case-insensitive):" >&2
|
|
# Surface lines containing common error markers so the actual root
|
|
# cause is visible even if it's far from the tail (e.g. a linker
|
|
# error mid-log that triggered a cascade of later failures).
|
|
# "exceeds" catches the kernel's UTS_RELEASE length limit;
|
|
# "Error [0-9]" catches make's "*** [target] Error N" lines.
|
|
grep -iE 'error:|error [0-9]|cannot find|undefined reference|no such file|not found|failed|fatal|exceeds|too (long|short|large)' \
|
|
"${log_file}" 2>/dev/null | tail -n 30 >&2 || true
|
|
echo ">> [FAILED] Full log: ${log_file}" >&2
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
function f_tmux_dashboard() {
|
|
if [[ -n "${TMUX:-}" ]]; then
|
|
echo ">> Active Tmux session identified. Splitting target tracking matrix..."
|
|
tmux split-window -h -p 35 "tail -F \"${LOGS}\"/*.log" || true
|
|
tmux split-window -v -p 50 "watch -n 2 'echo \"=== ENTROPY POOL ===\"; cat /proc/sys/kernel/random/entropy_avail; echo \"=== NETWORK BOUND MATRIX ===\"; ss -tunp | grep -v 127.0.0.1'" || true
|
|
tmux select-pane -t 0 || true
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# 6. POLY-SIGNATURE IDENTITY STAMPING LAYER
|
|
#
|
|
# Tier 1 — Cluster: Deterministic token from target+sysroot hash.
|
|
# Joins an existing cluster token if one is provided.
|
|
# Write-once per deployment; never drifts.
|
|
# Tier 2 — TPM: Binds signature to hardware PCR state via TPM 1.2/2.0.
|
|
# Singular-target deployments only. Gated behind --tpm-seal.
|
|
# Tier 3 — Poly: Per-machine random salt (write-once) folded into every
|
|
# binary stamp. Two independent machines produce different
|
|
# forensic signatures even for the same target.
|
|
#
|
|
# Selection priority: cluster-join > tpm-seal > poly (default).
|
|
# State files live under ${BTC_ARCHIVE}/ and persist across builds.
|
|
# ============================================================================
|
|
|
|
# Signature state (set once by f_sig_init, consumed by f_stamp_binary)
|
|
BTC_SIG_TIER=""
|
|
BTC_SIG_TOKEN=""
|
|
BTC_TPM_SEAL=0
|
|
|
|
function f_tpm_pcr_digest() {
|
|
# Attempt TPM 2.0 PCR read via tpm2-tools
|
|
if command -v tpm2_pcrread &>/dev/null; then
|
|
tpm2_pcrread sha256:0,1,2,3,4,5,6,7 2>/dev/null | \
|
|
awk '/^[0-9]+:/{gsub(/[^0-9a-fA-F]/,"", $2); printf "%s", $2}' | \
|
|
sha256sum | awk '{print $1}'
|
|
return 0
|
|
fi
|
|
|
|
# Attempt TPM 1.2 via sysfs PCR export
|
|
if [[ -d /sys/class/tpm/tpm0 ]]; then
|
|
sha256sum /sys/class/tpm/tpm0/pcrs 2>/dev/null | awk '{print $1}'
|
|
return 0
|
|
fi
|
|
|
|
# No TPM hardware detected
|
|
return 1
|
|
}
|
|
|
|
function f_sig_init() {
|
|
local salt_file="${BTC_ARCHIVE}/.btc-salt"
|
|
local cluster_file="${BTC_ARCHIVE}/.btc-cluster-token"
|
|
|
|
# --- Tier 1: Cluster ---
|
|
# If a cluster token exists on disk, this machine has already joined.
|
|
# Honor it unconditionally — no drift, no re-roll.
|
|
if [[ -f "${cluster_file}" ]]; then
|
|
BTC_SIG_TOKEN=$(< "${cluster_file}")
|
|
BTC_SIG_TIER="cluster"
|
|
echo ">> [SIG] Tier 1 (cluster) — Adopting existing cluster token."
|
|
echo ">> [SIG] Token: ${BTC_SIG_TOKEN:0:16}..."
|
|
return 0
|
|
fi
|
|
|
|
# If a cluster join was requested via CLI, adopt the provided token.
|
|
if [[ -n "${BTC_CLUSTER_JOIN:-}" ]]; then
|
|
BTC_SIG_TOKEN="${BTC_CLUSTER_JOIN}"
|
|
BTC_SIG_TIER="cluster"
|
|
mkdir -p "${BTC_ARCHIVE}"
|
|
echo "${BTC_SIG_TOKEN}" > "${cluster_file}"
|
|
echo ">> [SIG] Tier 1 (cluster) — Joined cluster with provided token."
|
|
echo ">> [SIG] Token: ${BTC_SIG_TOKEN:0:16}..."
|
|
return 0
|
|
fi
|
|
|
|
# --- Tier 2: TPM ---
|
|
# Hardware-bound signature. Singular deployments only.
|
|
if [[ "${BTC_TPM_SEAL}" -eq 1 ]]; then
|
|
local pcr_digest
|
|
pcr_digest=$(f_tpm_pcr_digest) || true
|
|
if [[ -n "${pcr_digest}" ]]; then
|
|
BTC_SIG_TOKEN="tpm:${pcr_digest:0:32}"
|
|
BTC_SIG_TIER="tpm"
|
|
echo ">> [SIG] Tier 2 (tpm) — PCR digest sealed into signature."
|
|
echo ">> [SIG] Token: ${BTC_SIG_TOKEN:0:16}..."
|
|
return 0
|
|
else
|
|
echo ">> [WARN] --tpm-seal requested but no TPM hardware detected. Stepping down to poly."
|
|
fi
|
|
fi
|
|
|
|
# --- Tier 3: Poly (default) ---
|
|
# Per-machine random salt. Write-once; subsequent builds reuse it.
|
|
mkdir -p "${BTC_ARCHIVE}"
|
|
if [[ -f "${salt_file}" ]]; then
|
|
BTC_SIG_TOKEN=$(< "${salt_file}")
|
|
else
|
|
BTC_SIG_TOKEN=$(openssl rand -hex 16)
|
|
echo "${BTC_SIG_TOKEN}" > "${salt_file}"
|
|
fi
|
|
BTC_SIG_TIER="poly"
|
|
echo ">> [SIG] Tier 3 (poly) — Per-machine salt deployed."
|
|
echo ">> [SIG] Token: ${BTC_SIG_TOKEN:0:16}..."
|
|
}
|
|
|
|
function f_stamp_binary() {
|
|
local target_bin="$1"
|
|
local log_base="$2"
|
|
|
|
if [[ -f "${target_bin}" && ! -L "${target_bin}" ]]; then
|
|
# Select the assembler matching the build mode
|
|
local assembler
|
|
if [[ "${CROSS_MODE}" -eq 1 ]]; then
|
|
assembler="${TARGET}-gcc"
|
|
else
|
|
assembler="gcc"
|
|
fi
|
|
|
|
# 1. Inject ELF Object Note with poly-signature token
|
|
# The .note.BTC payload now carries the active sig tier and token,
|
|
# making each deployment's forensic identity distinct.
|
|
# Note type 0xB7C is a vendor-specific identifier — it does NOT
|
|
# correspond to any standard ELF note type (the Linux vendor-note
|
|
# namespace 0x0B7C is reserved for out-of-tree consumers).
|
|
cat << EOF > btc_stamp.s
|
|
.section .note.BTC,"a",@note
|
|
.long 2f - 1f
|
|
.long 4f - 3f
|
|
.long 0xB7C
|
|
1: .asciz "DCOSNET"
|
|
2: .align 4
|
|
3: .ascii "Org: dcos.net|K:${v_linux}|Arch:${BTC_T_ID}|Label:${SYS_LABEL}|Stage:${log_base}|SigTier:${BTC_SIG_TIER}|Sig:${BTC_SIG_TOKEN}"
|
|
4: .align 4
|
|
EOF
|
|
${assembler} -c btc_stamp.s -o btc_stamp.o
|
|
${TARGET}-objcopy --add-section .note.BTC=btc_stamp.o "${target_bin}" 2>/dev/null || \
|
|
objcopy --add-section .note.BTC=btc_stamp.o "${target_bin}" 2>/dev/null || true
|
|
rm -f btc_stamp.s btc_stamp.o
|
|
|
|
# 2. Extended Filesystem Attributes — identity now includes sig tier and token
|
|
local bin_hash
|
|
bin_hash=$(sha256sum "${target_bin}" | awk '{print $1}')
|
|
setfattr -n user.btc.identity -v "BTC-${SYS_LABEL}-${v_linux}-${BTC_SIG_TIER}" "${target_bin}" 2>/dev/null || true
|
|
setfattr -n user.btc.hash -v "${bin_hash}" "${target_bin}" 2>/dev/null || true
|
|
setfattr -n user.btc.sig.tier -v "${BTC_SIG_TIER}" "${target_bin}" 2>/dev/null || true
|
|
setfattr -n user.btc.sig.token -v "${BTC_SIG_TOKEN}" "${target_bin}" 2>/dev/null || true
|
|
|
|
# 3. Separate Debug Symbols & Create External Links
|
|
if [[ "${BTC_STRIP_MODE:-1}" -eq 1 ]]; then
|
|
mkdir -p "${BTC_ARCHIVE}/symbols/${SYS_LABEL}"
|
|
${TARGET}-objcopy --only-keep-debug "${target_bin}" "${BTC_ARCHIVE}/symbols/${SYS_LABEL}/${log_base}.debug" 2>/dev/null || \
|
|
objcopy --only-keep-debug "${target_bin}" "${BTC_ARCHIVE}/symbols/${SYS_LABEL}/${log_base}.debug" 2>/dev/null || true
|
|
${TARGET}-strip --strip-unneeded "${target_bin}" 2>/dev/null || \
|
|
strip --strip-unneeded "${target_bin}" 2>/dev/null || true
|
|
${TARGET}-objcopy --add-gnu-debuglink="${BTC_ARCHIVE}/symbols/${SYS_LABEL}/${log_base}.debug" "${target_bin}" 2>/dev/null || \
|
|
objcopy --add-gnu-debuglink="${BTC_ARCHIVE}/symbols/${SYS_LABEL}/${log_base}.debug" "${target_bin}" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# 7. SOURCE ACQUISITION (download / decompress / compress)
|
|
# ============================================================================
|
|
|
|
# Associative array: package stem -> upstream URL
|
|
# Only packages consumed by the build sequence are listed.
|
|
# URLs are official upstream mirrors — replace to point at a local mirror.
|
|
#
|
|
# NOTE: A_SRC_URL is declared here as an empty container and populated lazily
|
|
# inside f_download(). This is required because v_linux, v_gcc, and
|
|
# v_linux_headers may be overridden per-target (e.g. tile targets pin
|
|
# linux-5.4.302 and gcc-10.3.0 because mainline dropped tile support in
|
|
# Linux 5.9 and GCC 12). Building the URL table at parse time would bake
|
|
# in the default linux-7.1.x / gcc-15.3.0 URLs and ignore the per-target
|
|
# overrides, causing f_decompress() to look for the wrong tarball later.
|
|
#
|
|
# The kernel URL path component (v5.x / v6.x / v7.x) is derived from the
|
|
# major version of v_linux so the script works across kernel series without
|
|
# manual URL edits.
|
|
declare -A A_SRC_URL=()
|
|
|
|
function _archive_sane() {
|
|
# Quick integrity test: returns 0 if archive is valid, 1 if corrupt.
|
|
# Handles .tar.xz, .tar.gz, .tar.bz2, .tar.lz, .tar.lrz, .zip
|
|
local f="$1"
|
|
local ext="${f##*.}"
|
|
case "${ext}" in
|
|
xz|bz2|lz|gz) tar -tf "${f}" >/dev/null 2>&1 ;;
|
|
lrz) lrzip -t "${f}" >/dev/null 2>&1 ;;
|
|
zip) unzip -t "${f}" >/dev/null 2>&1 ;;
|
|
*) return 0 ;;
|
|
esac
|
|
}
|
|
|
|
function f_download() {
|
|
# Fetch all upstream source tarballs into SOURCE_CACHE.
|
|
# Skips files already present AND integrity-verified (idempotent — safe to re-run).
|
|
# Corrupted/truncated files are deleted and re-fetched automatically.
|
|
# Produces per-file md5 and sha512 checksum manifests.
|
|
mkdir -p "${SOURCE_CACHE}" "${LOGS}/checksums"
|
|
cd "${SOURCE_CACHE}"
|
|
echo ">> [ACQUIRE] Downloading upstream source tarballs..."
|
|
|
|
# Build the URL table NOW (after any per-target overrides in
|
|
# _configure_from_target have been applied). The kernel URL path
|
|
# component (v5.x / v6.x / v7.x) is derived from the major version
|
|
# of v_linux so tile targets (linux-5.4.x) and mainline targets
|
|
# (linux-7.1.x) both resolve correctly.
|
|
local _linux_major="${v_linux#linux-}"
|
|
_linux_major="${_linux_major%%.*}"
|
|
A_SRC_URL[binutils]="https://ftp.gnu.org/gnu/binutils/${v_binutils}.tar.xz"
|
|
A_SRC_URL[linux]="https://cdn.kernel.org/pub/linux/kernel/v${_linux_major}.x/${v_linux}.tar.xz"
|
|
A_SRC_URL[linux-headers]="https://cdn.kernel.org/pub/linux/kernel/v${_linux_major}.x/${v_linux_headers}.tar.xz"
|
|
A_SRC_URL[gcc]="https://ftp.gnu.org/gnu/gcc/${v_gcc}/${v_gcc}.tar.xz"
|
|
A_SRC_URL[glibc]="https://ftp.gnu.org/gnu/glibc/${v_glibc}.tar.xz"
|
|
A_SRC_URL[libxcrypt]="https://github.com/besser82/libxcrypt/releases/download/v${v_libxcrypt}/libxcrypt-${v_libxcrypt}.tar.xz"
|
|
A_SRC_URL[gmp]="https://ftp.gnu.org/gnu/gmp/${v_gmp}.tar.xz"
|
|
A_SRC_URL[mpfr]="https://ftp.gnu.org/gnu/mpfr/${v_mpfr}.tar.xz"
|
|
A_SRC_URL[mpc]="https://ftp.gnu.org/gnu/mpc/${v_mpc}.tar.xz"
|
|
A_SRC_URL[musl]="https://musl.libc.org/releases/${v_musl}.tar.gz"
|
|
|
|
for key in "${!A_SRC_URL[@]}"; do
|
|
local url="${A_SRC_URL[$key]}"
|
|
local file="${url##*/}"
|
|
|
|
if [[ -f "${file}" ]] && _archive_sane "${file}"; then
|
|
echo ">> [ACQUIRE] ${file} — present, skipping."
|
|
else
|
|
if [[ -f "${file}" ]]; then
|
|
echo ">> [ACQUIRE] ${file} — corrupt/truncated, re-fetching..."
|
|
rm -f "${file}"
|
|
else
|
|
echo ">> [ACQUIRE] ${file} — fetching..."
|
|
fi
|
|
wget -nc -O "${file}" "${url}"
|
|
# Post-download integrity check
|
|
if ! _archive_sane "${file}"; then
|
|
echo ">> [ACQUIRE] ${file} — FAILED integrity check after download!"
|
|
rm -f "${file}"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
md5sum "${file}" >> "${LOGS}/checksums/${file}.md5" 2>/dev/null || true
|
|
sha512sum "${file}" >> "${LOGS}/checksums/${file}.sha512" 2>/dev/null || true
|
|
done
|
|
|
|
echo ">> [ACQUIRE] Source acquisition complete."
|
|
}
|
|
|
|
function f_decompress() {
|
|
# Auto-detect compression type from file extension and extract into
|
|
# SOURCES_ACTIVE (ramfs cleanroom). Accepts either a bare filename
|
|
# (looked up in SOURCE_CACHE) or a full path.
|
|
#
|
|
# Usage: f_decompress <filename> # uses SOURCE_CACHE
|
|
# f_decompress </full/path/to/file> # absolute path
|
|
local in_file
|
|
case "$1" in
|
|
/*) in_file="$1" ;;
|
|
*) in_file="${SOURCE_CACHE}/$1" ;;
|
|
esac
|
|
|
|
local ext="${in_file##*.}"
|
|
cd "${SOURCES_ACTIVE}"
|
|
|
|
case "${ext}" in
|
|
xz) tar -axf "${in_file}" ;;
|
|
gz) tar -xzf "${in_file}" ;;
|
|
bz2) tar -xjf "${in_file}" ;;
|
|
lz) tar -axf "${in_file}" ;;
|
|
lrz) tar -x -I lrzip -f "${in_file}" ;;
|
|
zip) unzip -qo "${in_file}" ;;
|
|
*) echo ">> [WARN] f_decompress: unknown extension '.${ext}' for ${in_file}" ;;
|
|
esac
|
|
}
|
|
|
|
function f_compress() {
|
|
# Compress the finished toolchain tarball using the specified algorithm.
|
|
# Step-down dispatch on USE_COMPRESSOR — SEI CERT CTR50-JP.
|
|
# Usage: f_compress <output_path>
|
|
local out_file="$1"
|
|
case "${USE_COMPRESSOR:-xz}" in
|
|
xz) xz -z -e -9 "${out_file}" ;;
|
|
gz) gzip -9 "${out_file}" ;;
|
|
bz2) bzip2 -z9 "${out_file}" ;;
|
|
lrzip) lrzip -z -L9 -p${total_cpus:-1} -U "${out_file}" ;;
|
|
*) echo ">> [WARN] f_compress: unknown compressor '${USE_COMPRESSOR}', defaulting to xz"; xz -z -e -9 "${out_file}" ;;
|
|
esac
|
|
}
|
|
|
|
# ============================================================================
|
|
# 7b. CLEANROOM MATRIX CONFIGURATION
|
|
# ============================================================================
|
|
function f_setup() {
|
|
echo ">> Preparing Virtualized Cleanroom Environment..."
|
|
mkdir -p "${SOURCE_CACHE}" "${LOGS}" "${BTC_ARCHIVE}/symbols/${SYS_LABEL}"
|
|
|
|
if ! mountpoint -q "${SOURCES_ACTIVE}"; then
|
|
mount -t ramfs -o "size=${RAMDISK_SIZE}" ramfs "${SOURCES_ACTIVE}"
|
|
echo ">> Ramfs Cleanroom mounted at ${SOURCES_ACTIVE} with ceiling ${RAMDISK_SIZE}."
|
|
fi
|
|
|
|
mkdir -p "${NEWROOT}"
|
|
cd "${NEWROOT}"
|
|
|
|
# Create sysroot directory structure
|
|
mkdir -p bin etc lib lib64 sbin usr var include
|
|
|
|
# Architecture-specific sysroot layout
|
|
case "${BTC_T_ARCH}" in
|
|
x86_64)
|
|
ln -sfv lib "${NEWROOT}/lib64"
|
|
;;
|
|
arm)
|
|
# ARM EABI HF uses lib + lib/ld-linux-armhf.so.3
|
|
ln -sfv lib "${NEWROOT}/lib32" 2>/dev/null || true
|
|
;;
|
|
mipsel)
|
|
# MIPS o32 ABI: lib is the primary lib dir
|
|
;;
|
|
tilegx)
|
|
# Tile-Gx 64-bit: lib64 for abi64
|
|
ln -sfv lib "${NEWROOT}/lib64" 2>/dev/null || true
|
|
;;
|
|
esac
|
|
|
|
export PATH="${NEWROOT}/bin:${PATH}"
|
|
}
|
|
|
|
# ============================================================================
|
|
# 8. TOOLCHAIN BUILD FUNCTIONS
|
|
# ============================================================================
|
|
|
|
# --- 8a. Binutils (all architectures) ---
|
|
function f_binutils() {
|
|
cd "${SOURCES_ACTIVE}"
|
|
f_decompress "${v_binutils}.tar.xz"
|
|
mkdir -p "${v_binutils}-build" && cd "${v_binutils}-build"
|
|
|
|
local configure_target="--target=${TARGET}"
|
|
|
|
# Architecture-specific binutils configure patches
|
|
local binutils_extra=""
|
|
case "${BTC_T_ARCH}" in
|
|
arm)
|
|
binutils_extra="--enable-multilib --with-sysroot=${NEWROOT}"
|
|
;;
|
|
mipsel)
|
|
binutils_extra="--enable-multilib --with-sysroot=${NEWROOT}"
|
|
;;
|
|
tilegx)
|
|
binutils_extra="--disable-werror"
|
|
;;
|
|
*)
|
|
binutils_extra="--enable-default-hash-style=gnu"
|
|
;;
|
|
esac
|
|
|
|
local build_cmd="../${v_binutils}/configure \
|
|
--prefix=${NEWROOT} \
|
|
--with-sysroot=${NEWROOT} \
|
|
${configure_target} \
|
|
--disable-nls \
|
|
--enable-gprofng=no \
|
|
--disable-werror \
|
|
${binutils_extra}"
|
|
|
|
f_exec_log "${build_cmd}" "binutils-configure"
|
|
f_exec_log "make ${v_threads}" "binutils-make"
|
|
f_exec_log "make install" "binutils-install"
|
|
}
|
|
|
|
# --- 8b. Kernel Headers (all architectures) ---
|
|
function f_kernel_headers() {
|
|
cd "${SOURCES_ACTIVE}"
|
|
|
|
# Use stable LTS headers for cross targets that may need older kernels
|
|
local kernel_src="${v_linux}"
|
|
if [[ "${BTC_T_FAMILY}" == "tile" ]]; then
|
|
kernel_src="${v_linux_headers}"
|
|
fi
|
|
|
|
# For musl targets, we only need sanitized kernel headers (no full
|
|
# kernel source). For glibc targets we need the full headers.
|
|
f_decompress "${kernel_src}.tar.xz"
|
|
cd "${kernel_src}"
|
|
|
|
f_exec_log "make mrproper" "kernel-headers-clean"
|
|
f_exec_log "make headers" "kernel-headers-generate"
|
|
|
|
find usr/include -type f ! -name '*.h' -delete
|
|
mkdir -p "${NEWROOT}/usr/include"
|
|
cp -rv usr/include/* "${NEWROOT}/usr/include"
|
|
}
|
|
|
|
# --- 8c. GCC Stage 1 (all architectures) ---
|
|
function f_gcc_p1() {
|
|
cd "${SOURCES_ACTIVE}"
|
|
rm -rf "${v_gcc}"
|
|
f_decompress "${v_gcc}.tar.xz"
|
|
cd "${v_gcc}"
|
|
|
|
# Nesting Support Libraries internally for Stage-1 execution isolation
|
|
# f_decompress changes CWD to SOURCES_ACTIVE, so we must re-enter
|
|
# the gcc source tree and use absolute paths for the mv.
|
|
f_decompress "${v_gmp}.tar.xz"
|
|
f_decompress "${v_mpfr}.tar.xz"
|
|
f_decompress "${v_mpc}.tar.xz"
|
|
cd "${SOURCES_ACTIVE}/${v_gcc}"
|
|
rm -rf gmp mpfr mpc
|
|
mv -Tf "${SOURCES_ACTIVE}/${v_gmp}" gmp
|
|
mv -Tf "${SOURCES_ACTIVE}/${v_mpfr}" mpfr
|
|
mv -Tf "${SOURCES_ACTIVE}/${v_mpc}" mpc
|
|
|
|
# Architecture-specific GCC source patches
|
|
case "${BTC_T_ARCH}" in
|
|
x86_64)
|
|
# Enforce 64-bit dynamic linker structural target pathing
|
|
# GCC >= 15 removed t-linux64; t-linux or config.gcc may carry m64=
|
|
if [[ -f gcc/config/i386/t-linux64 ]]; then
|
|
sed -e '/m64=/s/lib64/lib/' -i.bak gcc/config/i386/t-linux64
|
|
elif [[ -f gcc/config/i386/t-linux ]]; then
|
|
sed -e '/m64=/s/lib64/lib/' -i.bak gcc/config/i386/t-linux
|
|
else
|
|
echo ">> [WARN] gcc/config/i386/t-linux64 not found in ${v_gcc}; skipping lib64->lib multilib patch (--disable-multilib is active)"
|
|
fi
|
|
;;
|
|
arm)
|
|
# ARM: default to hard-float ABI
|
|
sed -e 's/#define DEFAULT_ABI_FLOAT SoftF/SoftF_HARDFP/' -i.bak gcc/config/arm/linux-eabi.h 2>/dev/null || true
|
|
;;
|
|
mipsel)
|
|
# MIPS: default to o32 ABI, soft-float
|
|
;;
|
|
tilegx)
|
|
# Tile-Gx: no special patches needed for GCC 10.3.0
|
|
;;
|
|
esac
|
|
|
|
mkdir -p "${SOURCES_ACTIVE}/${v_gcc}-phase1" && cd "${SOURCES_ACTIVE}/${v_gcc}-phase1"
|
|
|
|
# Base configure flags common to all targets.
|
|
#
|
|
# --with-cpu is x86_64-only — see f_gcc_p2() for the same rationale.
|
|
# ARM, MIPS, and Tile-Gx carry their per-target --with-* hints via
|
|
# BTC_T_GCC_EXTRA (set in the registry) which is appended below.
|
|
local gcc_p1_arch_flags=""
|
|
if [[ "${BTC_T_ARCH}" == "x86_64" ]]; then
|
|
gcc_p1_arch_flags="--with-cpu=${BTC_T_MARCH}"
|
|
fi
|
|
|
|
local gcc_base="--target=${TARGET} \
|
|
--prefix=${NEWROOT} \
|
|
--with-sysroot=${NEWROOT} \
|
|
--with-newlib \
|
|
--without-headers \
|
|
--with-arch=${BTC_T_MARCH} \
|
|
${gcc_p1_arch_flags} \
|
|
--enable-default-pie \
|
|
--enable-default-ssp \
|
|
--disable-nls \
|
|
--disable-shared \
|
|
--disable-threads \
|
|
--disable-libatomic \
|
|
--disable-libgomp \
|
|
--disable-libquadmath \
|
|
--disable-libssp \
|
|
--disable-libvtv \
|
|
--disable-libstdcxx \
|
|
--enable-languages=c,c++"
|
|
|
|
# Per-architecture configure adjustments
|
|
local gcc_arch_extra=""
|
|
case "${BTC_T_CLIB}" in
|
|
glibc)
|
|
# x86_64 glibc targets: set glibc version for compatibility checks
|
|
gcc_arch_extra="--with-glibc-version=${v_glibc#*-} --disable-multilib"
|
|
;;
|
|
musl)
|
|
# Cross targets with musl: disable multilib by default, add arch-specific flags
|
|
gcc_arch_extra="--disable-multilib ${BTC_T_GCC_EXTRA}"
|
|
;;
|
|
esac
|
|
|
|
local build_cmd="../${v_gcc}/configure ${gcc_base} ${gcc_arch_extra}"
|
|
|
|
f_exec_log "${build_cmd}" "gcc-p1-configure"
|
|
f_exec_log "make ${v_threads} all-gcc" "gcc-p1-make"
|
|
f_exec_log "make ${v_threads} all-target-libgcc" "gcc-p1-libgcc"
|
|
f_exec_log "make install-gcc" "gcc-p1-install"
|
|
f_exec_log "make install-target-libgcc" "gcc-p1-install-libgcc"
|
|
}
|
|
|
|
# --- 8d. C Library (glibc or musl) ---
|
|
function f_clib() {
|
|
# Dispatch C library build; enforce architecture constraints at the gate
|
|
case "${BTC_T_CLIB}" in
|
|
glibc)
|
|
if [[ "${BTC_T_ARCH}" != "x86_64" ]]; then
|
|
echo ">> [ERROR] glibc does not support ${BTC_T_ARCH}. Select a musl-based target."
|
|
exit 1
|
|
fi
|
|
f_glibc
|
|
;;
|
|
musl)
|
|
f_musl
|
|
;;
|
|
*)
|
|
echo ">> [ERROR] Unsupported C library: ${BTC_T_CLIB}"
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
function f_glibc() {
|
|
cd "${SOURCES_ACTIVE}"
|
|
f_decompress "${v_glibc}.tar.xz"
|
|
mkdir -p "${v_glibc}-build" && cd "${v_glibc}-build"
|
|
|
|
local build_cmd="../${v_glibc}/configure \
|
|
--prefix=/usr \
|
|
--host=${TARGET} \
|
|
--build=${HOST_ARCH} \
|
|
--enable-kernel=${BTC_T_KERN_MIN} \
|
|
--with-headers=${NEWROOT}/usr/include \
|
|
--disable-profile \
|
|
--enable-stack-protector=strong \
|
|
--disable-werror \
|
|
libc_cv_slibdir=/usr/lib"
|
|
|
|
f_exec_log "${build_cmd}" "glibc-configure"
|
|
f_exec_log "make ${v_threads}" "glibc-make"
|
|
f_exec_log "make DESTDIR=${NEWROOT} install" "glibc-install"
|
|
|
|
# Sanitize hardcoded host system configurations from dynamic script linkage
|
|
sed -i "s|${NEWROOT}||g" "${NEWROOT}/usr/bin/ldd"
|
|
}
|
|
|
|
function f_libxcrypt() {
|
|
# libxcrypt is only used with glibc — musl has built-in crypt support
|
|
if [[ "${BTC_T_CLIB}" != "glibc" ]]; then
|
|
echo ">> [SKIP] libxcrypt: not needed for ${BTC_T_CLIB}"
|
|
return 0
|
|
fi
|
|
|
|
cd "${SOURCES_ACTIVE}"
|
|
f_decompress "libxcrypt-${v_libxcrypt}.tar.xz"
|
|
cd "libxcrypt-${v_libxcrypt}"
|
|
|
|
# GCC >= 15 enforces stricter const-correctness (-Wcast-qual, -Wdiscarded-qualifiers).
|
|
# libxcrypt 4.5.2 has known issues in crypt-gost-yescrypt.c and crypt-sm3-yescrypt.c
|
|
# that trigger -Werror. Pass CFLAGS=-Wno-error to configure so it is baked into
|
|
# the generated Makefile. Automake places CFLAGS after WARN_CFLAGS on the compile
|
|
# line, so GCC processes -Werror first then -Wno-error (last wins). This survives
|
|
# Makefile regeneration and is the standard approach for building old deps with
|
|
# newer compilers.
|
|
local build_cmd="CFLAGS=\"-g -O2 -Wno-error\" ./configure \
|
|
--prefix=/usr \
|
|
--host=${TARGET} \
|
|
--build=${HOST_ARCH} \
|
|
--enable-hashes=strong,glibc \
|
|
--enable-obsolete-api=no \
|
|
--disable-static"
|
|
|
|
f_exec_log "${build_cmd}" "libxcrypt-configure"
|
|
f_exec_log "make ${v_threads}" "libxcrypt-make"
|
|
f_exec_log "make DESTDIR=${NEWROOT} install" "libxcrypt-install"
|
|
}
|
|
|
|
function f_musl() {
|
|
# musl: lightweight C library for cross-compilation
|
|
# Used by mipsel, armv7, and tilegx targets.
|
|
# Reference: CLFS (Cross Linux From Scratch) musl cross-compiler chapter.
|
|
cd "${SOURCES_ACTIVE}"
|
|
f_decompress "${v_musl}.tar.gz"
|
|
cd "${v_musl}"
|
|
|
|
# Build a standalone musl cross-compiler that wraps our stage-1 GCC.
|
|
# This produces ${TARGET}-musl-gcc and the musl C library installed
|
|
# into the sysroot.
|
|
local musl_configure="./configure \
|
|
--prefix=/usr \
|
|
--host=${TARGET} \
|
|
--build=${HOST_ARCH} \
|
|
--disable-shared \
|
|
--enable-static"
|
|
|
|
# For some targets, musl needs additional architecture hints
|
|
case "${BTC_T_ARCH}" in
|
|
arm)
|
|
musl_configure="${musl_configure} CFLAGS=\"-O2 -march=armv7-a -mfloat-abi=hard -mfpu=vfpv3-d16\""
|
|
;;
|
|
mipsel)
|
|
musl_configure="${musl_configure} CFLAGS=\"-O2 -march=mips32r2 -mabi=32 -msoft-float\""
|
|
;;
|
|
tilegx)
|
|
musl_configure="${musl_configure} CFLAGS=\"-O2 -march=tilegx\""
|
|
;;
|
|
esac
|
|
|
|
# Pass cross-compiler tools inline to avoid polluting the caller's environment
|
|
local cross_env="CC=${NEWROOT}/bin/${TARGET}-gcc AR=${NEWROOT}/bin/${TARGET}-ar RANLIB=${NEWROOT}/bin/${TARGET}-ranlib"
|
|
|
|
f_exec_log "${cross_env} CROSS_COMPILE=${TARGET}- ${musl_configure}" "musl-configure"
|
|
f_exec_log "${cross_env} make ${v_threads}" "musl-make"
|
|
f_exec_log "${cross_env} make DESTDIR=${NEWROOT} install" "musl-install"
|
|
}
|
|
|
|
# --- 8e. GCC Stage 2 (final compiler) ---
|
|
function f_gcc_p2() {
|
|
cd "${SOURCES_ACTIVE}"
|
|
|
|
# Decompress support libraries (f_decompress changes CWD to SOURCES_ACTIVE).
|
|
f_decompress "${v_gmp}.tar.xz"
|
|
f_decompress "${v_mpfr}.tar.xz"
|
|
f_decompress "${v_mpc}.tar.xz"
|
|
|
|
# Re-enter gcc source tree and nest the extracted libraries where
|
|
# GCC's configure expects to find them (as gmp/, mpfr/, mpc/ subdirs).
|
|
cd "${SOURCES_ACTIVE}/${v_gcc}"
|
|
rm -rf gmp mpfr mpc
|
|
mv -Tf "${SOURCES_ACTIVE}/${v_gmp}" gmp
|
|
mv -Tf "${SOURCES_ACTIVE}/${v_mpfr}" mpfr
|
|
mv -Tf "${SOURCES_ACTIVE}/${v_mpc}" mpc
|
|
|
|
mkdir -p "${SOURCES_ACTIVE}/${v_gcc}-phase2" && cd "${SOURCES_ACTIVE}/${v_gcc}-phase2"
|
|
|
|
# Stage 2 final compiler — runs on BUILD host, targets the sysroot.
|
|
#
|
|
# CRITICAL: --host must be ${HOST_ARCH} (the machine the compiler
|
|
# executes on), NOT ${TARGET}. --target specifies what architecture
|
|
# the produced compiler generates code for. Using --host=${TARGET}
|
|
# caused configure to expect the build machine to be the custom
|
|
# triple, which made all compile-and-run checks fail instantly.
|
|
#
|
|
# --with-sysroot and --with-headers are in the base because both
|
|
# glibc and musl targets need the compiler to find C library
|
|
# headers and runtime in the sysroot.
|
|
#
|
|
# --with-cpu is x86_64-only. ARM, MIPS, and Tile-Gx configure
|
|
# backends reject --with-cpu=<march> (they want --with-cpu=<cpu>
|
|
# e.g. cortex-a9, not the architecture name). Those targets
|
|
# already carry --with-arch=<arch> via BTC_T_GCC_EXTRA from the
|
|
# registry, so we omit --with-cpu entirely for non-x86_64 to
|
|
# avoid "unrecognized argument" failures during configure.
|
|
#
|
|
# LDFLAGS=-Wl,-rpath,/usr/lib/../lib matches the LFS final-GCC
|
|
# recipe. Without it, the freshly-built ${TARGET}-gcc may fail
|
|
# at runtime to locate libc.so in the sysroot, which surfaces as
|
|
# "cannot find libc.so.6" during the Stage 2 libstdc++ configure
|
|
# link tests and aborts Phase 2.
|
|
local gcc_p2_arch_flags=""
|
|
if [[ "${BTC_T_ARCH}" == "x86_64" ]]; then
|
|
gcc_p2_arch_flags="--with-cpu=${BTC_T_MARCH}"
|
|
fi
|
|
|
|
local gcc_p2_base="--prefix=/usr \
|
|
--build=${HOST_ARCH} \
|
|
--host=${HOST_ARCH} \
|
|
--target=${TARGET} \
|
|
--with-sysroot=${NEWROOT} \
|
|
--with-headers=${NEWROOT}/usr/include \
|
|
--with-arch=${BTC_T_MARCH} \
|
|
${gcc_p2_arch_flags} \
|
|
--enable-languages=c,c++ \
|
|
--enable-default-pie \
|
|
--enable-default-ssp \
|
|
--enable-threads=posix \
|
|
--disable-bootstrap \
|
|
LDFLAGS=\"-Wl,-rpath,/usr/lib/../lib\""
|
|
|
|
# Per-C-library configure adjustments.
|
|
#
|
|
# glibc targets: pull in the LFS final-GCC flags so libstdc++
|
|
# configure finds glibc locale support (--enable-clocale=gnu),
|
|
# uses the __cxa_atexit path for static destructors (required by
|
|
# the C++ ABI), and skips PCH generation (--disable-libstdcxx-pch,
|
|
# which would otherwise require running the just-built cross-gcc
|
|
# to emit a .gch file — impossible during a cross build where we
|
|
# cannot execute target binaries).
|
|
#
|
|
# musl targets: point GCC at the musl library path and inherit
|
|
# per-target --with-* hints (arch, fpu, abi, ...) from BTC_T_GCC_EXTRA.
|
|
local gcc_p2_clib=""
|
|
case "${BTC_T_CLIB}" in
|
|
glibc)
|
|
gcc_p2_clib="--disable-multilib \
|
|
--enable-clocale=gnu \
|
|
--enable-__cxa_atexit \
|
|
--disable-libstdcxx-pch"
|
|
;;
|
|
musl)
|
|
gcc_p2_clib="--disable-multilib \
|
|
--with-libs=${NEWROOT}/usr/lib \
|
|
${BTC_T_GCC_EXTRA}"
|
|
;;
|
|
esac
|
|
|
|
local build_cmd="../${v_gcc}/configure ${gcc_p2_base} ${gcc_p2_clib}"
|
|
|
|
f_exec_log "${build_cmd}" "gcc-p2-configure"
|
|
f_exec_log "make ${v_threads}" "gcc-p2-make"
|
|
f_exec_log "make DESTDIR=${NEWROOT} install" "gcc-p2-install"
|
|
|
|
# Stage 2 GCC was installed at ${NEWROOT}/usr/bin/${TARGET}-gcc.
|
|
# Prepend that to PATH so f_kernel_binary() picks up the final
|
|
# compiler instead of the stripped-down Stage 1 cross-gcc at
|
|
# ${NEWROOT}/bin/${TARGET}-gcc (which was built with
|
|
# --disable-shared --disable-threads --disable-libstdcxx and
|
|
# is unsuitable as the production compiler for the kernel and
|
|
# any downstream package builds that link against libgcc_s.
|
|
export PATH="${NEWROOT}/usr/bin:${PATH}"
|
|
echo ">> [PATH] Prepended ${NEWROOT}/usr/bin for Stage 2 GCC."
|
|
}
|
|
|
|
# --- 8f. Kernel Binary (architecture-aware) ---
|
|
function f_kernel_binary() {
|
|
local kernel_src="${v_linux}"
|
|
if [[ "${BTC_T_FAMILY}" == "tile" ]]; then
|
|
kernel_src="${v_linux_headers}"
|
|
fi
|
|
|
|
cd "${SOURCES_ACTIVE}/${kernel_src}"
|
|
|
|
echo ">> Instantiating Silicon Optimized Monolithic Configuration Matrix for ${BTC_T_ARCH}..."
|
|
|
|
# Map BTC's target architecture to the Linux kernel's ARCH= name.
|
|
# The kernel's ARCH= variable accepts only the canonical family name
|
|
# (arm, mips, x86_64, tilegx, ...). Our registry uses "mipsel" to
|
|
# distinguish little-endian MIPS from big-endian, but the kernel's
|
|
# Makefile treats MIPS endianness as a Kconfig selection, not as a
|
|
# separate ARCH. Passing ARCH=mipsel makes the kernel look for
|
|
# arch/mipsel/ which does not exist and the build dies at "make
|
|
# defconfig". Resolve the canonical name here, once, so every
|
|
# downstream make invocation (defconfig, olddefconfig, image, install)
|
|
# uses the same value.
|
|
local kernel_arch
|
|
case "${BTC_T_ARCH}" in
|
|
mipsel) kernel_arch="mips" ;;
|
|
*) kernel_arch="${BTC_T_ARCH}" ;;
|
|
esac
|
|
|
|
# LD_LIBRARY_PATH for Stage 2 GCC's C++ runtime (libstdc++.so, libgcc_s.so).
|
|
# See the long comment near the kernel-bin-make invocation below for the
|
|
# full rationale. Set it BEFORE defconfig because defconfig also probes
|
|
# the cross-compiler (`${CROSS_COMPILE}gcc -print-file-name=...`) and a
|
|
# broken probe produces an empty .config — exactly the 527-byte defconfig
|
|
# output symptom that aborts the build silently.
|
|
local gcc_ver="${v_gcc#gcc-}"
|
|
local stage2_libpath="${NEWROOT}/usr/lib:${NEWROOT}/lib"
|
|
local gcc_libdir="${NEWROOT}/usr/lib/gcc/${TARGET}/${gcc_ver}"
|
|
if [[ -d "${gcc_libdir}" ]]; then
|
|
stage2_libpath="${stage2_libpath}:${gcc_libdir}"
|
|
[[ -d "${gcc_libdir}/32" ]] && stage2_libpath="${stage2_libpath}:${gcc_libdir}/32"
|
|
fi
|
|
export LD_LIBRARY_PATH="${stage2_libpath}${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}"
|
|
echo ">> [LD_LIBRARY_PATH] ${LD_LIBRARY_PATH}"
|
|
|
|
# Select the correct defconfig for the target architecture.
|
|
# These are wrapped in f_exec_log() so the defconfig output is
|
|
# captured in ${LOGS}/kernel-bin-defconfig.log — matching every
|
|
# other build step in the pipeline. Without logging, a defconfig
|
|
# failure surfaces as a bare error on stdout with no archived
|
|
# record for post-mortem.
|
|
local defconfig_target
|
|
case "${BTC_T_ARCH}" in
|
|
arm)
|
|
# multi_v7 is the universal ARMv7 defconfig (covers most Cortex-A SoCs)
|
|
defconfig_target="multi_v7_defconfig"
|
|
;;
|
|
mipsel)
|
|
# MALTA is the reference MIPS32 platform
|
|
defconfig_target="malta_defconfig"
|
|
;;
|
|
tilegx)
|
|
# Tile-Gx has its own defconfig; fall back to defconfig for
|
|
# kernels where tilegx_defconfig was already removed.
|
|
defconfig_target="tilegx_defconfig"
|
|
;;
|
|
*)
|
|
defconfig_target="defconfig"
|
|
;;
|
|
esac
|
|
|
|
# First attempt: the target-specific defconfig.
|
|
# For tilegx on kernels that removed tilegx_defconfig, fall back to
|
|
# the generic defconfig (which still respects ARCH=tilegx).
|
|
#
|
|
# The `if !` form suspends `set -e` for the condition test so a
|
|
# failed defconfig doesn't abort the whole script before we can
|
|
# try the fallback. For non-tile targets, a defconfig failure
|
|
# is a hard error and we return 1.
|
|
if ! f_exec_log "make ARCH=${kernel_arch} ${defconfig_target}" "kernel-bin-defconfig"; then
|
|
if [[ "${BTC_T_ARCH}" == "tilegx" ]]; then
|
|
echo ">> [WARN] ${defconfig_target} not available in ${kernel_src}; falling back to defconfig."
|
|
f_exec_log "make ARCH=${kernel_arch} defconfig" "kernel-bin-defconfig"
|
|
else
|
|
echo ">> [ERROR] ${defconfig_target} failed for ${kernel_src}."
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# Inject Custom Enterprise Swarm Labels & Architecture Parameters
|
|
#
|
|
# NOTE: the LOCALVERSION tag (-dcosnet-${SYS_LABEL}) is passed on the
|
|
# `make` command line below, NOT written into CONFIG_LOCALVERSION here.
|
|
# Writing it to .config AND passing it on the make command line would
|
|
# concatenate the two, producing a version string like
|
|
# "7.1.7-dcosnet-DCOSNET-BROADWELL-AVX2-LTO-dcosnet-DCOSNET-BROADWELL-AVX2-LTO"
|
|
# which exceeds the kernel's 64-byte UTS_RELEASE limit and aborts the
|
|
# build at utsrelease.h generation with "exceeds 64 characters".
|
|
# Pick ONE source of truth — the make command line is the LFS convention.
|
|
sed -i "s|CONFIG_LOCALVERSION=.*|CONFIG_LOCALVERSION=\"\"|" .config || true
|
|
|
|
# Modern Hardening Optimization Suite Injection
|
|
sed -i "s/# CONFIG_MODULES is not set/CONFIG_MODULES=n/" .config || true
|
|
echo "CONFIG_MODULES=n" >> .config
|
|
echo "CONFIG_KALLSYMS=n" >> .config
|
|
echo "CONFIG_DEBUG_FS=n" >> .config
|
|
|
|
# Disable objtool (CONFIG_STACK_VALIDATION) and switch to the
|
|
# frame-pointer unwinder. objtool is a HOST tool that links against
|
|
# libelf; if the host lacks libelf-dev (the -dev package that provides
|
|
# the libelf.so symlink, not just the runtime libelf.so.1), the LINK
|
|
# step for tools/objtool/objtool fails with "cannot find -lelf" and
|
|
# the whole kernel build aborts. Installing libelf-dev on the host
|
|
# would fix it at the source, but we don't want to impose that
|
|
# dependency — the frame-pointer unwinder is functionally equivalent
|
|
# for a toolchain build (slightly larger kernel binary, marginally
|
|
# slower stack traces in production, but no functional regression).
|
|
# The olddefconfig step below will clean up any resulting conflicts.
|
|
echo "CONFIG_STACK_VALIDATION=n" >> .config
|
|
echo "CONFIG_UNWINDER_ORC=n" >> .config
|
|
echo "CONFIG_UNWINDER_FRAME_POINTER=y" >> .config
|
|
echo "CONFIG_OBJTOOL=n" >> .config 2>/dev/null || true
|
|
|
|
# Cross-compile kernel using the canonical ARCH= name and the
|
|
# Stage 2 cross-compiler (now on PATH after f_gcc_p2()).
|
|
#
|
|
# LD_LIBRARY_PATH was already set up earlier in this function (before
|
|
# the defconfig call) so the loader finds Stage 2's libstdc++.so /
|
|
# libgcc_s.so first. See the comment near the top of this function
|
|
# for the full rationale.
|
|
|
|
local kernel_make_vars="ARCH=${kernel_arch} CROSS_COMPILE=${TARGET}-"
|
|
|
|
f_exec_log "make ${kernel_make_vars} olddefconfig" "kernel-bin-config-merge"
|
|
f_exec_log "make ${v_threads} ${kernel_make_vars} LOCALVERSION=-dcosnet-${SYS_LABEL}" "kernel-bin-make"
|
|
|
|
# Install kernel image — path varies by architecture
|
|
mkdir -p "${NEWROOT}/boot"
|
|
case "${BTC_T_ARCH}" in
|
|
x86_64)
|
|
cp -v arch/x86/boot/bzImage "${NEWROOT}/boot/vmlinuz-${v_linux}-${SYS_LABEL}"
|
|
;;
|
|
arm)
|
|
cp -v arch/arm/boot/zImage "${NEWROOT}/boot/vmlinuz-${v_linux}-${SYS_LABEL}" 2>/dev/null || \
|
|
cp -v arch/arm/boot/Image "${NEWROOT}/boot/vmlinuz-${v_linux}-${SYS_LABEL}"
|
|
# Also copy device tree blobs if built
|
|
find arch/arm/boot/dts -name '*.dtb' -exec cp -v {} "${NEWROOT}/boot/" \; 2>/dev/null || true
|
|
;;
|
|
mipsel)
|
|
cp -v vmlinux "${NEWROOT}/boot/vmlinuz-${v_linux}-${SYS_LABEL}"
|
|
;;
|
|
tilegx)
|
|
cp -v arch/tile/boot/vmlinux "${NEWROOT}/boot/vmlinuz-${v_linux}-${SYS_LABEL}" 2>/dev/null || \
|
|
cp -v vmlinux "${NEWROOT}/boot/vmlinuz-${v_linux}-${SYS_LABEL}"
|
|
;;
|
|
esac
|
|
|
|
# Apply Forensic Identity Stamps to all toolchain binaries
|
|
while IFS= read -r bin; do
|
|
f_stamp_binary "${bin}" "$(basename "${bin}")"
|
|
done < <(find "${NEWROOT}/bin" "${NEWROOT}/usr/bin" -type f 2>/dev/null) || true
|
|
}
|
|
|
|
# ============================================================================
|
|
# 9. PACKAGING
|
|
# ============================================================================
|
|
function f_package() {
|
|
echo ">> Packaging Production Golden Image Artifact Target Matrix..."
|
|
cd "${NEWROOT}"
|
|
|
|
# --- Integration Manifest for sorcery-go and Fester ---
|
|
local manifest="${BTC_ARCHIVE}/${SYS_LABEL}-manifest.json"
|
|
# Derive mode label from dispatch state
|
|
local mode_label
|
|
case "${CROSS_MODE}" in
|
|
1) mode_label="cross" ;;
|
|
*) mode_label="native" ;;
|
|
esac
|
|
|
|
cat > "${manifest}" << MANIFEST_EOF
|
|
{
|
|
"btc_version": "${BTC_VERSION}",
|
|
"mode": "${mode_label}",
|
|
"cross_mode": ${CROSS_MODE},
|
|
"sys_label": "${SYS_LABEL}",
|
|
"target_id": "${BTC_T_ID}",
|
|
"target_arch": "${BTC_T_ARCH}",
|
|
"target_cpu": "${BTC_T_CPU}",
|
|
"target_march": "${BTC_T_MARCH}",
|
|
"target_triple": "${TARGET}",
|
|
"host_arch": "${HOST_ARCH}",
|
|
"isa_tag": "${ISA_TAG}",
|
|
"opt_tag": "${OPT_TAG}",
|
|
"abi": "${BTC_T_ABI}",
|
|
"clib": "${BTC_T_CLIB}",
|
|
"endian": "${BTC_T_ENDIAN}",
|
|
"family": "${BTC_T_FAMILY}",
|
|
"description": "${BTC_T_DESC}",
|
|
"kernel_min": "${BTC_T_KERN_MIN}",
|
|
"kernel": "${v_linux}",
|
|
"binutils": "${v_binutils}",
|
|
"gcc": "${v_gcc}",
|
|
"glibc": "${v_glibc}",
|
|
"musl": "${v_musl}",
|
|
"libxcrypt": "libxcrypt-${v_libxcrypt}",
|
|
"golden_image": "${SYS_LABEL}-toolchain-golden.tar.xz",
|
|
"cflags": "${GLOBAL_CFLAGS}",
|
|
"ldflags": "${GLOBAL_LDFLAGS}",
|
|
"stamp_note": ".note.BTC",
|
|
"stamp_xattr_identity": "user.btc.identity",
|
|
"stamp_xattr_hash": "user.btc.hash",
|
|
"stamp_xattr_sig_tier": "user.btc.sig.tier",
|
|
"stamp_xattr_sig_token": "user.btc.sig.token",
|
|
"sig_tier": "${BTC_SIG_TIER}",
|
|
"sig_token_preview": "${BTC_SIG_TOKEN:0:16}...",
|
|
"org": "dcos.net",
|
|
"license": "AGPL-3.0-or-later",
|
|
"integrations": {
|
|
"sorcery-go": {
|
|
"config_key": "toolchain",
|
|
"config_value": "btc",
|
|
"env_btc_path": "SORCERY_GO_BTC_PATH",
|
|
"env_btc_root": "SORCERY_GO_BTC_ROOT",
|
|
"env_btc_syslabel": "SORCERY_GO_BTC_SYS_LABEL"
|
|
},
|
|
"fester": {
|
|
"config_section": "btc",
|
|
"config_yaml_key": "btc.enabled / btc.root / btc.target"
|
|
}
|
|
}
|
|
}
|
|
MANIFEST_EOF
|
|
echo ">> [INTEGRATION] Manifest written to: ${manifest}"
|
|
|
|
tar -cf - . | xz -9 -T 0 > "${BTC_ARCHIVE}/${SYS_LABEL}-toolchain-golden.tar.xz"
|
|
echo ">> [SUCCESS] Archive deployed cleanly to: ${BTC_ARCHIVE}/${SYS_LABEL}-toolchain-golden.tar.xz"
|
|
|
|
# Generate SHA-256 checksums of the golden image for CAS integration.
|
|
local checksum_file="${BTC_ARCHIVE}/${SYS_LABEL}-toolchain-golden.sha256"
|
|
sha256sum "${BTC_ARCHIVE}/${SYS_LABEL}-toolchain-golden.tar.xz" > "${checksum_file}"
|
|
echo ">> [INTEGRATION] SHA-256 checksum written to: ${checksum_file}"
|
|
}
|
|
|
|
# ============================================================================
|
|
# 10. MAIN ENTRY RUNTIME MATRIX
|
|
# ============================================================================
|
|
function f_main() {
|
|
[[ ${EUID} -ne 0 ]] && { echo ">> Error: Root privileges required."; exit 1; }
|
|
|
|
# Parse command-line arguments (multi-flag pass)
|
|
# Flags that exit early are handled first; build flags accumulate.
|
|
local positional=""
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--list)
|
|
f_list_targets
|
|
exit 0
|
|
;;
|
|
--list-json)
|
|
f_list_targets_json
|
|
exit 0
|
|
;;
|
|
--help|-h)
|
|
echo "BTC-${BTC_VERSION} - Cross-Compilation Build Tool Chain"
|
|
echo ""
|
|
echo "Usage: BTC.sh [TARGET_ID | --native | --list | --list-json]"
|
|
echo " BTC.sh --tpm-seal [TARGET_ID]"
|
|
echo " BTC.sh --join-cluster=TOKEN [TARGET_ID]"
|
|
echo ""
|
|
echo " TARGET_ID Build cross-toolchain for the specified target"
|
|
echo " --native Auto-probe host silicon and build native toolchain"
|
|
echo " --list List all registered cross-compilation targets"
|
|
echo " --list-json Emit target registry as JSON"
|
|
echo " --tpm-seal Bind forensic signature to TPM PCR state (singular deploys)"
|
|
echo " --join-cluster=T Adopt cluster token T for unified identity"
|
|
echo ""
|
|
echo "Omitting an argument selects --native automatically."
|
|
echo "Signature tiers: cluster (joined) > tpm (hardware) > poly (per-machine salt)."
|
|
f_list_targets
|
|
exit 0
|
|
;;
|
|
--tpm-seal)
|
|
BTC_TPM_SEAL=1
|
|
shift
|
|
;;
|
|
--join-cluster=*)
|
|
export BTC_CLUSTER_JOIN="${1#*=}"
|
|
shift
|
|
;;
|
|
--native)
|
|
positional="--native"
|
|
shift
|
|
;;
|
|
*)
|
|
positional="$1"
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
export BTC_TARGET_ID="${positional:-}"
|
|
|
|
f_agpl_header
|
|
f_silicon_probe
|
|
f_set_paths
|
|
f_setup
|
|
f_download # Acquire all upstream source tarballs (idempotent)
|
|
f_sig_init
|
|
f_tmux_dashboard
|
|
|
|
# Linear Build Execution Sequence
|
|
f_binutils
|
|
f_kernel_headers
|
|
f_gcc_p1
|
|
f_clib # glibc OR musl (depending on target registry)
|
|
f_libxcrypt # glibc-only (skipped for musl targets)
|
|
f_gcc_p2
|
|
f_kernel_binary
|
|
f_package
|
|
|
|
# Clear volatile memory cleanrooms
|
|
cd /
|
|
umount -l "${SOURCES_ACTIVE}" 2>/dev/null || true
|
|
echo ">> [COMPLETE] Build Tool Chain Finished Successfully under AGPLv3 Framework."
|
|
echo ">> [TARGET] ${BTC_T_ID} (${BTC_T_DESC})"
|
|
echo ">> [LABEL] ${SYS_LABEL}"
|
|
echo ">> [IMAGE] ${BTC_ARCHIVE}/${SYS_LABEL}-toolchain-golden.tar.xz"
|
|
}
|
|
|
|
f_main "$@" |