Skip to content

Commit 033cb99

Browse files
authored
Add Big Endian Support (#18)
1 parent 6de617d commit 033cb99

32 files changed

Lines changed: 2684 additions & 959 deletions
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# shellcheck source=shared/common.sh
5+
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/shared/common.sh"
6+
ci_init_paths
7+
8+
export OPENSSL_ARCH_DIR=/usr/lib/arm-linux-gnueabihf
9+
ci_debian_container_bootstrap

.github/workflows/ci/arm32-run.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
CI_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5+
6+
# run-on-arch-action `install` runs at docker build time (no repo mount).
7+
# Install deps here in `run` so arm32-install.sh stays the single source of truth.
8+
bash "$CI_ROOT/arm32-install.sh"
9+
10+
# shellcheck source=shared/common.sh
11+
source "$CI_ROOT/shared/common.sh"
12+
ci_init_paths
13+
14+
ci_build_standard
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# shellcheck source=shared/common.sh
5+
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/shared/common.sh"
6+
ci_init_paths
7+
8+
ci_openssl_hack
9+
ci_build_standard
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/usr/bin/env bash
2+
# TODO(FPC 3.2.4): remove this shim. FPC 3.2.2 hardcodes libssl.1.1.dylib, so
3+
# symlink Homebrew's OpenSSL 3 dylibs to the 1.1 names on macOS.
4+
set -euo pipefail
5+
6+
OSSL_LIB="$(brew --prefix openssl@3)/lib"
7+
sudo mkdir -p /usr/local/lib
8+
sudo ln -sf "$OSSL_LIB/libssl.3.dylib" /usr/local/lib/libssl.1.1.dylib
9+
sudo ln -sf "$OSSL_LIB/libcrypto.3.dylib" /usr/local/lib/libcrypto.1.1.dylib
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/usr/bin/env bash
2+
# TODO(FPC 3.2.4): remove this shim. FPC 3.2.2 hardcodes libssl.so.1.1, so
3+
# symlink the OpenSSL 3.x ELF libraries to the 1.1 sonames on Linux/BSD.
4+
set -euo pipefail
5+
6+
ARCH_DIR="${1:-}"
7+
if [ -z "$ARCH_DIR" ] && command -v gcc >/dev/null 2>&1; then
8+
multiarch="$(gcc -print-multiarch 2>/dev/null || true)"
9+
if [ -n "$multiarch" ] && [ -f "/usr/lib/$multiarch/libssl.so.3" ]; then
10+
ARCH_DIR="/usr/lib/$multiarch"
11+
fi
12+
fi
13+
if [ -z "$ARCH_DIR" ] || [ ! -f "$ARCH_DIR/libssl.so.3" ]; then
14+
for candidate in \
15+
/usr/lib/powerpc64-linux-gnu \
16+
/usr/lib/ppc64-linux-gnu \
17+
/usr/lib/arm-linux-gnueabihf \
18+
/usr/lib/aarch64-linux-gnu \
19+
/usr/lib/x86_64-linux-gnu \
20+
/usr/lib; do
21+
if [ -f "$candidate/libssl.so.3" ]; then
22+
ARCH_DIR="$candidate"
23+
break
24+
fi
25+
done
26+
fi
27+
28+
if [ ! -f "$ARCH_DIR/libssl.so.3" ]; then
29+
echo "openssl-libssl11-shim-unix: libssl.so.3 not found under $ARCH_DIR" >&2
30+
exit 1
31+
fi
32+
33+
ln_cmd=(ln -sf)
34+
if [ "${OPENSSL_USE_SUDO:-1}" = "1" ] && [ "$(id -u)" -ne 0 ]; then
35+
if command -v sudo >/dev/null 2>&1; then
36+
ln_cmd=(sudo ln -sf)
37+
fi
38+
fi
39+
40+
"${ln_cmd[@]}" "$ARCH_DIR/libssl.so.3" "$ARCH_DIR/libssl.so.1.1"
41+
"${ln_cmd[@]}" "$ARCH_DIR/libcrypto.so.3" "$ARCH_DIR/libcrypto.so.1.1"
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
: "${GITHUB_WORKSPACE:?GITHUB_WORKSPACE is required}"
5+
: "${FPC_VERSION:?FPC_VERSION is required}"
6+
: "${FPC_TARGET:?FPC_TARGET is required}"
7+
: "${MAKE_BUILD_BACKEND:?MAKE_BUILD_BACKEND is required}"
8+
9+
CI_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10+
CI_SHARED="$CI_ROOT/shared"
11+
# shellcheck source=ppc64-be-images.env
12+
source "$CI_ROOT/ppc64-be-images.env"
13+
14+
# Cross-compile glibc csu stubs on the x86 host. gcc inside QEMU ppc64
15+
# user-mode often SIGSEGVs; install-fpc-lazarus.sh expects CSU_STUBS_PREBUILT.
16+
STUB_C="$(mktemp --suffix=.c)"
17+
STUB_OBJ="$(mktemp --suffix=.o)"
18+
CSU_STUBS_IN_CONTAINER=/csu_stubs_prebuilt.o
19+
trap 'rm -f "$STUB_C" "$STUB_OBJ"' EXIT
20+
21+
cp "$CI_SHARED/csu-stubs.c" "$STUB_C"
22+
23+
sudo apt-get update -qq
24+
sudo apt-get install -y -qq gcc-powerpc64-linux-gnu
25+
powerpc64-linux-gnu-gcc -c -fPIC -o "$STUB_OBJ" "$STUB_C"
26+
if [ ! -s "$STUB_OBJ" ]; then
27+
echo "::error::cross-compile did not produce csu stubs object at $STUB_OBJ" >&2
28+
exit 1
29+
fi
30+
echo "csu stubs cross-compiled: $(wc -c < "$STUB_OBJ") bytes"
31+
32+
docker run --rm --platform linux/ppc64 \
33+
--security-opt seccomp=unconfined \
34+
-v "${GITHUB_WORKSPACE}:/work" -w /work \
35+
-v "${STUB_OBJ}:${CSU_STUBS_IN_CONTAINER}:ro" \
36+
-e FPC_VERSION \
37+
-e FPC_TARGET \
38+
-e MAKE_BUILD_BACKEND \
39+
-e MAKE_PACKAGE_SCOPE \
40+
-e LAZARUS_BRANCH \
41+
-e LAZARUS_REPO \
42+
-e CI_DEBUG \
43+
-e DEBIAN_FRONTEND=noninteractive \
44+
-e QEMU_CPU=power8 \
45+
-e CSU_STUBS_PREBUILT="${CSU_STUBS_IN_CONTAINER}" \
46+
"$PPC64_RUNTIME_IMAGE" \
47+
bash .github/workflows/ci/ppc64-be-inner.sh
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Pinned image for the linux-powerpc64-be CI flow (sourced by ppc64-be-build.sh).
2+
#
3+
# QEMU (the ppc64 big-endian emulator) is NOT pinned here: it is installed from
4+
# the Ubuntu runner's apt as qemu-user-static (currently QEMU ~8.2) by
5+
# ppc64-qemu-setup.sh. Rationale:
6+
# - multiarch/qemu-user-static is abandoned (frozen at QEMU 7.2.0, Jan 2023).
7+
# - tonistiigi/binfmt ships only little-endian ppc64le, not big-endian ppc64.
8+
# The distro package gives us a current big-endian qemu-ppc64 and auto-registers
9+
# its binfmt handler with the F (fix-binary) flag. See ppc64-qemu-setup.sh.
10+
#
11+
# Runtime rootfs: Debian-ports ppc64 (big-endian) exists only in sid, so there is
12+
# no stable release to track. We currently track the rolling tag and let the
13+
# floating distro QEMU (see above) keep pace with the userland. If sid ever drifts
14+
# ahead of the emulator again (intermittent SIGSEGV / "ppcXXX can't be executed"),
15+
# re-pin by digest: swap the active line below for the commented one and refresh via
16+
# docker buildx imagetools inspect urbanogilson/debian-debootstrap-ports:ppc64-forky-sid
17+
# (full variant — qemu-ppc64-static embedded upstream).
18+
# https://github.com/urbanogilson/debian-debootstrap-ports
19+
PPC64_RUNTIME_IMAGE=urbanogilson/debian-debootstrap-ports:ppc64-forky-sid
20+
# Last-known-good digest (2026-05-15, gcc 15 userland) — uncomment to pin:
21+
# PPC64_RUNTIME_IMAGE=urbanogilson/debian-debootstrap-ports:ppc64-forky-sid@sha256:5a0b62beaeb64dec7ed941f3a28d30827ea0f773b9d10a3f66c79dce48f36841
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# shellcheck source=shared/common.sh
5+
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/shared/common.sh"
6+
ci_init_paths
7+
8+
export OPENSSL_ARCH_DIR=/usr/lib/powerpc64-linux-gnu
9+
ci_debian_container_bootstrap gcc binutils
10+
ci_build_standard
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Register the ppc64 (big-endian) binfmt handler on the runner host so that
5+
# `docker run --platform linux/ppc64 ...` (ppc64-be-build.sh) transparently
6+
# executes big-endian binaries under QEMU user-mode.
7+
#
8+
# We install QEMU from the Ubuntu runner's apt (qemu-user-static, currently
9+
# ~8.2) rather than multiarch/qemu-user-static (abandoned at 7.2.0) or
10+
# tonistiigi/binfmt (little-endian ppc64le only). The package's postinst
11+
# registers each handler with the F (fix-binary) flag via update-binfmts, so
12+
# the interpreter fd is preserved into the container and qemu does not need to
13+
# exist inside the rootfs. See ppc64-be-images.env for the rationale.
14+
15+
export DEBIAN_FRONTEND=noninteractive
16+
sudo apt-get update -qq
17+
sudo apt-get install -y -qq qemu-user-static binfmt-support
18+
19+
# Idempotent: enable the handler in case it was installed but left disabled.
20+
sudo update-binfmts --enable qemu-ppc64 2>/dev/null || true
21+
22+
BINFMT_FILE=/proc/sys/fs/binfmt_misc/qemu-ppc64
23+
if [ ! -e "$BINFMT_FILE" ]; then
24+
echo "::error::qemu-ppc64 binfmt handler not registered"
25+
ls /proc/sys/fs/binfmt_misc/ || true
26+
exit 1
27+
fi
28+
29+
echo "qemu-ppc64-static: $(qemu-ppc64-static --version 2>/dev/null | head -1 || echo 'unknown')"
30+
echo "binfmt handler ${BINFMT_FILE}:"
31+
cat "$BINFMT_FILE"
32+
33+
# F (fix-binary) is required so the interpreter works inside the container.
34+
if ! grep -q 'flags:.*F' "$BINFMT_FILE"; then
35+
echo "::error::qemu-ppc64 binfmt flags missing F (fix-binary mode); got:" >&2
36+
cat "$BINFMT_FILE" >&2
37+
exit 1
38+
fi
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Resolve which targets CI should run, from the registry in targets.json.
5+
#
6+
# Inputs:
7+
# INPUT_TARGETS workflow_dispatch CSV. Empty => default set (default=true).
8+
# Outputs (GITHUB_OUTPUT):
9+
# enabled_targets CSV of selected ids; gates the qemu/vm jobs in make.yml.
10+
# native_matrix JSON array of enabled kind=native entries; consumed as the
11+
# native job's strategy.matrix.include (empty => job skipped).
12+
#
13+
# targets.json is the single source of truth. Opt-in targets (default=false,
14+
# e.g. netbsd, dragonflybsd) are excluded from the default and must be named
15+
# explicitly via INPUT_TARGETS.
16+
17+
CI_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
18+
REGISTRY="$CI_ROOT/targets.json"
19+
20+
VALID_TARGETS="$(jq -r '[.targets[].id] | join(",")' "$REGISTRY")"
21+
DEFAULT="$(jq -r '[.targets[] | select(.default) | .id] | join(",")' "$REGISTRY")"
22+
23+
if [ -z "${INPUT_TARGETS// /}" ]; then
24+
TARGETS="$DEFAULT"
25+
SOURCE="default"
26+
else
27+
TARGETS="${INPUT_TARGETS// /}"
28+
SOURCE="workflow_dispatch input"
29+
fi
30+
31+
# Warn (don't fail) on unknown ids so a typo is visible but harmless: an
32+
# unrecognised id simply matches no job.
33+
IFS=',' read -r -a _selected <<< "$TARGETS"
34+
IFS=',' read -r -a _valid <<< "$VALID_TARGETS"
35+
for _id in "${_selected[@]}"; do
36+
[ -z "$_id" ] && continue
37+
_found=0
38+
for _v in "${_valid[@]}"; do
39+
if [ "$_id" = "$_v" ]; then
40+
_found=1
41+
break
42+
fi
43+
done
44+
if [ "$_found" -eq 0 ]; then
45+
echo "::warning::Unknown target id \"${_id}\" (valid: ${VALID_TARGETS})"
46+
fi
47+
done
48+
49+
# Filter the registry to enabled native entries. Bind .id before switching the
50+
# pipe context to the split list; index() returns null when absent (falsy) and
51+
# an integer otherwise (0 is truthy in jq).
52+
NATIVE_MATRIX="$(jq -c --arg ids "$TARGETS" \
53+
'[.targets[] | select(.kind == "native") | select(.id as $i | ($ids | split(",") | index($i)))]' \
54+
"$REGISTRY")"
55+
56+
{
57+
echo "enabled_targets=${TARGETS}"
58+
echo "native_matrix=${NATIVE_MATRIX}"
59+
} >> "${GITHUB_OUTPUT:?GITHUB_OUTPUT is required}"
60+
61+
echo "Enabled targets (${SOURCE}): ${TARGETS}"
62+
echo "Native matrix: ${NATIVE_MATRIX}"

0 commit comments

Comments
 (0)