Skip to content

Commit 8e397b4

Browse files
committed
ci(edriver): static musl build for x86_64 and aarch64
Build the edriver BPF plugin as a fully static musl binary for both x86_64 and aarch64. Both jobs run in ubuntu:24.04 containers using musl-gcc; vendored-libelf compiles elfutils --without-zstd so the resulting libelf.a carries no zstd dependency. Key issues solved along the way: 1. elfutils ./configure (invoked by libbpf-sys build.rs with CC=musl-gcc) requires argp_parse, fts_close, and _obstack_free, which are absent from musl libc. Fix: add a 'Build musl compat libs' CI step that compiles the three void-linux musl-compat shims (identical to Alpine's argp-standalone / musl-fts-dev / musl-obstack-dev packages) with musl-gcc and installs them into musl-gcc's default sysroot search path /usr/lib/${ARCH}-linux-musl/ before cargo build runs. 2. libbpf C compilation (make -C libbpf/src) needs gelf.h at BPF- skeleton-generation time; add libelf-dev and zlib1g-dev to apt install so clang can find the header. 3. .cargo/config.toml: set CC_*_musl=musl-gcc, linker=musl-gcc, target-feature=+crt-static for both musl targets. Remove stale -lzstd link flags (vendored elfutils is --without-zstd). CI matrix: x86_64 ubuntu-latest → x86_64-unknown-linux-musl aarch64 ubuntu-24.04-arm → aarch64-unknown-linux-musl (ubuntu:24.04 container for both; Alpine ARM64 has no Node.js so JS GitHub Actions don't work there)
1 parent 6fca81b commit 8e397b4

18 files changed

Lines changed: 3992 additions & 3916 deletions

File tree

.github/workflows/ci-edriver.yaml

Lines changed: 110 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,54 @@ jobs:
1717
edriver:
1818
name: "Build and test edriver / ${{ matrix.arch }}"
1919
runs-on: ${{ matrix.runner }}
20+
container:
21+
image: ${{ matrix.container }}
2022
strategy:
2123
fail-fast: false
2224
matrix:
2325
include:
2426
- arch: x86_64
2527
runner: ubuntu-latest
2628
target: x86_64-unknown-linux-musl
29+
# Ubuntu + musl-gcc + vendored-libelf (--without-zstd).
30+
# elfutils ./configure needs argp/fts/obstack, which are absent from
31+
# musl libc. We pre-build them (argp-standalone, musl-fts,
32+
# musl-obstack) using musl-gcc and install to /usr/lib/x86_64-linux-musl
33+
# before running `cargo build`, so configure finds them.
34+
container: ubuntu:24.04
35+
cc: musl-gcc
36+
lib_path: ""
37+
extra_cflags: "-I/usr/include -I/usr/include/x86_64-linux-gnu"
38+
cargo_extra_features: ",vendored-libelf"
39+
rustflags: "-C target-feature=+crt-static"
2740
- arch: aarch64
2841
runner: ubuntu-24.04-arm
2942
target: aarch64-unknown-linux-musl
43+
# Same approach as x86_64. ubuntu:24.04 on arm64 supports JS
44+
# actions; Alpine ARM64 does not (no Node.js ARM64 in Alpine).
45+
container: ubuntu:24.04
46+
cc: musl-gcc
47+
lib_path: ""
48+
extra_cflags: "-I/usr/include -I/usr/include/aarch64-linux-gnu"
49+
cargo_extra_features: ",vendored-libelf"
50+
rustflags: "-C target-feature=+crt-static"
3051
steps:
31-
- name: "Git checkout"
52+
- name: Install build dependencies
53+
env:
54+
DEBIAN_FRONTEND: noninteractive
55+
run: |
56+
apt-get update -qq
57+
apt-get install -y --no-install-recommends \
58+
bash curl git ca-certificates \
59+
build-essential linux-headers-generic \
60+
clang llvm \
61+
libelf-dev zlib1g-dev \
62+
musl-tools musl-dev \
63+
protobuf-compiler \
64+
pkg-config \
65+
autoconf automake libtool autopoint flex bison gawk
66+
67+
- name: Git checkout
3268
uses: actions/checkout@v4
3369
with:
3470
submodules: true
@@ -37,33 +73,92 @@ jobs:
3773
uses: dtolnay/rust-toolchain@stable
3874
with:
3975
targets: ${{ matrix.target }}
76+
components: rustfmt
4077

41-
- name: Cache cargo registry & build
78+
- name: Cache cargo registry
4279
uses: actions/cache@v4
4380
with:
4481
path: |
4582
~/.cargo/registry
4683
~/.cargo/git
47-
plugins/edriver/target
48-
key: ${{ runner.os }}-${{ matrix.arch }}-cargo-edriver-${{ hashFiles('plugins/edriver/Cargo.lock') }}
49-
restore-keys: ${{ runner.os }}-${{ matrix.arch }}-cargo-edriver-
84+
key: ${{ matrix.arch }}-cargo-edriver-v9-${{ hashFiles('plugins/edriver/Cargo.lock') }}
85+
restore-keys: ${{ matrix.arch }}-cargo-edriver-v9-
5086

51-
- name: Install build dependencies
87+
# elfutils ./configure (invoked by libbpf-sys vendored-libelf build.rs)
88+
# runs with CC=musl-gcc. musl libc lacks argp_parse, fts_close, and
89+
# _obstack_free, causing configure to abort. We pre-build the three
90+
# void-linux musl-compat shims (identical to Alpine's argp-standalone,
91+
# musl-fts-dev, musl-obstack-dev packages) and install the .a + headers
92+
# into musl-gcc's default sysroot search path so configure finds them.
93+
- name: Build musl compat libs (argp / fts / obstack)
5294
run: |
53-
sudo apt-get update -qq
54-
sudo apt-get install -y --no-install-recommends build-essential pkgconf libelf-dev libzstd-dev musl-tools llvm-14 clang-14 protobuf-compiler
55-
for tool in clang llc llvm-strip
56-
do
57-
sudo rm -f /usr/bin/$tool
58-
sudo ln -s /usr/bin/${tool}-14 /usr/bin/$tool
59-
done
95+
set -eux
96+
ARCH=$(uname -m) # x86_64 or aarch64
97+
MUSL_LIB=/usr/lib/${ARCH}-linux-musl
98+
MUSL_INC=/usr/include/${ARCH}-linux-musl
99+
100+
# 1. argp-standalone – provides argp_parse
101+
# NOTE: Makefile.am uses noinst_LIBRARIES so `make install` does NOT
102+
# install libargp.a. We build and copy manually.
103+
git clone --depth=1 https://github.com/ericonr/argp-standalone /tmp/argp-standalone
104+
cd /tmp/argp-standalone
105+
autoreconf -fiv
106+
CC=musl-gcc ./configure --prefix=/usr
107+
make -j$(nproc)
108+
cp libargp.a ${MUSL_LIB}/
109+
cp argp.h ${MUSL_INC}/
110+
111+
# 2. musl-fts – provides fts_close (NetBSD implementation)
112+
git clone --depth=1 https://github.com/void-linux/musl-fts /tmp/musl-fts
113+
cd /tmp/musl-fts
114+
./bootstrap.sh
115+
CC=musl-gcc ./configure --enable-static --disable-shared \
116+
--prefix=/usr --libdir=${MUSL_LIB} --includedir=${MUSL_INC}
117+
make -j$(nproc) && make install
118+
119+
# 3. musl-obstack – provides _obstack_free (from gcc libiberty)
120+
git clone --depth=1 https://github.com/void-linux/musl-obstack /tmp/musl-obstack
121+
cd /tmp/musl-obstack
122+
./bootstrap.sh
123+
CC=musl-gcc ./configure --enable-static --disable-shared \
124+
--prefix=/usr --libdir=${MUSL_LIB} --includedir=${MUSL_INC}
125+
make -j$(nproc) && make install
126+
127+
# Verify all three libs landed in the musl sysroot
128+
ls -la ${MUSL_LIB}/libargp.a ${MUSL_LIB}/libfts.a ${MUSL_LIB}/libobstack.a
129+
130+
- name: Verify toolchain
131+
run: |
132+
which protoc && protoc --version
133+
which clang && clang --version | head -1
60134
61135
- name: Build
62-
run: cd plugins/edriver && make build
136+
run: |
137+
export PROTOC=$(which protoc)
138+
cd plugins/edriver && make build
63139
env:
64140
PLATFORM: ${{ matrix.arch }}
141+
LIBBPF_SYS_LIBRARY_PATH: ${{ matrix.lib_path }}
142+
# musl-gcc -nostdinc: re-add system includes for vendored libbpf.
143+
LIBBPF_SYS_EXTRA_CFLAGS: ${{ matrix.extra_cflags }}
144+
CC_x86_64_unknown_linux_musl: ${{ matrix.cc }}
145+
CC_aarch64_unknown_linux_musl: ${{ matrix.cc }}
146+
CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER: ${{ matrix.cc }}
147+
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER: ${{ matrix.cc }}
148+
RUSTFLAGS: ${{ matrix.rustflags }}
149+
CARGO_EXTRA_FEATURES: ${{ matrix.cargo_extra_features }}
65150

66151
- name: Test
67-
run: cd plugins/edriver && make test
152+
run: |
153+
export PROTOC=$(which protoc)
154+
cd plugins/edriver && make test
68155
env:
69156
PLATFORM: ${{ matrix.arch }}
157+
LIBBPF_SYS_LIBRARY_PATH: ${{ matrix.lib_path }}
158+
LIBBPF_SYS_EXTRA_CFLAGS: ${{ matrix.extra_cflags }}
159+
CC_x86_64_unknown_linux_musl: ${{ matrix.cc }}
160+
CC_aarch64_unknown_linux_musl: ${{ matrix.cc }}
161+
CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER: ${{ matrix.cc }}
162+
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER: ${{ matrix.cc }}
163+
RUSTFLAGS: ${{ matrix.rustflags }}
164+
CARGO_EXTRA_FEATURES: ${{ matrix.cargo_extra_features }}

.github/workflows/co-re.yaml

Lines changed: 0 additions & 59 deletions
This file was deleted.

.github/workflows/release-driver.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ jobs:
1818
with:
1919
targets: x86_64-unknown-linux-musl
2020

21+
- name: Add musl target
22+
run: rustup target add x86_64-unknown-linux-musl
23+
2124
- name: Cache cargo registry & build
2225
uses: actions/cache@v4
2326
with:
@@ -43,7 +46,8 @@ jobs:
4346
cd plugins/edriver
4447
make build
4548
cd ../..
46-
49+
env:
50+
LIBBPF_SYS_LIBRARY_PATH: /usr/lib/x86_64-linux-gnu
4751
- name: Strip & checksum
4852
run: |
4953
strip plugins/edriver/target/x86_64-unknown-linux-musl/release/edriver

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ agent/deploy/hades-agent
1818

1919
# server
2020
server/webconsole/frontend
21+
server/webconsole/frontend_backup
22+
server/webconsole/frontend_v2
2123
server/frontend
2224

2325
# ignore certs

plugins/edriver/.cargo/config.toml

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
11
[env]
2-
LIBBPF_SYS_LIBRARY_PATH_x86_64_unknown_linux_musl = "/usr/lib/x86_64-linux-gnu:/usr/lib64:/usr/lib"
3-
LIBBPF_SYS_LIBRARY_PATH_aarch64_unknown_linux_musl = "/usr/lib/aarch64-linux-gnu:/usr/lib64:/usr/lib"
2+
LIBBPF_SYS_LIBRARY_PATH_x86_64_unknown_linux_gnu = "/usr/lib/x86_64-linux-gnu:/usr/lib64:/usr/lib"
3+
LIBBPF_SYS_LIBRARY_PATH_aarch64_unknown_linux_gnu = "/usr/lib/aarch64-linux-gnu:/usr/lib64:/usr/lib"
4+
LIBBPF_SYS_LIBRARY_PATH_x86_64_unknown_linux_musl = "/usr/lib/x86_64-linux-musl:/usr/lib/x86_64-linux-gnu:/usr/lib64:/usr/lib"
5+
LIBBPF_SYS_LIBRARY_PATH_aarch64_unknown_linux_musl = "/usr/lib/aarch64-linux-musl:/usr/lib/aarch64-linux-gnu:/usr/lib64:/usr/lib"
6+
# libbpf-sys compiles libbpf C sources using the native gcc.
7+
# For musl targets, we must use musl-gcc so libbpf C code is compiled against
8+
# musl headers (not glibc headers). With glibc headers, _FORTIFY_SOURCE
9+
# injects __snprintf_chk/__sprintf_chk/__realpath_chk etc., which musl doesn't
10+
# provide. musl-gcc wraps gcc with the musl specs (-specs .../musl-gcc.specs)
11+
# and points include paths to musl headers.
12+
CC_x86_64_unknown_linux_musl = "musl-gcc"
13+
CC_aarch64_unknown_linux_musl = "musl-gcc"
414

15+
# vendored-libelf builds elfutils --without-zstd; no zstd dependency anywhere.
516
[target.x86_64-unknown-linux-musl]
6-
linker = "x86_64-linux-musl-gcc"
17+
linker = "musl-gcc"
718
rustflags = ["-C", "target-feature=+crt-static"]
819

920
[target.aarch64-unknown-linux-musl]
10-
linker = "aarch64-linux-musl-gcc"
21+
linker = "musl-gcc"
1122
rustflags = ["-C", "target-feature=+crt-static"]
12-
13-
[target.aarch64-unknown-linux-gnu]
14-
linker = "aarch64-linux-gnu-gcc"

plugins/edriver/Cargo.toml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ description = "Rust version of hades edriver"
88
[features]
99
debug = ["sdk/debug"]
1010
static = ["libbpf-rs/static"]
11+
# Enable vendored elfutils compilation via libbpf-sys.
12+
# Only needed on glibc hosts (Ubuntu) where the system libelf.a is compiled
13+
# with USE_ZSTD. On Alpine/musl the system libelf-static is already
14+
# musl-native and zstd-free, so this is unnecessary (and won't build on musl
15+
# because elfutils configure requires glibc-only argp/fts).
16+
vendored-libelf = ["libbpf-sys/vendored-libelf"]
1117

1218
[dependencies]
1319
anyhow = "1.0"
@@ -19,9 +25,31 @@ lazy_static = "1.5.0"
1925
twox-hash = "2.1"
2026
hex = "0.4"
2127
libbpf-rs = { version = "0.26.2", features = ["static"] }
28+
# Add vendored-zlib directly so libbpf-sys compiles zlib from its vendored
29+
# source (using CC_x86_64_unknown_linux_musl=musl-gcc set in .cargo/config.toml)
30+
# instead of linking the system libz.a. The system libz.a on Ubuntu is
31+
# compiled with glibc headers (_FORTIFY_SOURCE=2), which generates
32+
# __snprintf_chk / __vsnprintf_chk calls that musl doesn't provide.
33+
# Compiling zlib from source with musl-gcc uses musl headers and avoids these.
34+
# vendored-zlib: compile zlib from source so the .a is musl-compatible on
35+
# both Alpine and Ubuntu. vendored-libelf is NOT listed here; it is gated
36+
# behind the optional `vendored-libelf` feature above and activated only on
37+
# glibc CI (ubuntu:24.04 aarch64) via CARGO_EXTRA_FEATURES in the Makefile.
38+
libbpf-sys = { version = "1.7", features = ["vendored-zlib"] }
2239
bitflags = "2.11.1"
2340
moka = { version = "0.12", features = ["sync"] }
2441
sdk = { path = "../../SDK/rust" }
2542

2643
[build-dependencies]
2744
libbpf-cargo = "0.26.2"
45+
# libbpf-cargo default → libbpf-rs/default → libbpf-sys/vendored-libbpf →
46+
# static-libbpf for the HOST build-script. libbpf's make install
47+
# BUILD_STATIC_ONLY=y creates a fat liblibbpf.a that embeds libelf object
48+
# files. Without vendored-libelf here, it embeds the SYSTEM libelf.a which
49+
# on Ubuntu 24.04+ is built with USE_ZSTD, causing ZSTD_* undefined symbol
50+
# errors when linking the HOST build-script binary.
51+
# vendored-libelf forces elfutils to be built with --without-zstd so the
52+
# embedded libelf objects are ZSTD-free.
53+
# NOTE: Do NOT add features=["static"] here (without vendored-libelf) since
54+
# that would make the HOST link system libelf.a directly with ZSTD refs.
55+
libbpf-sys = { version = "1.7", features = ["vendored-libelf", "vendored-zlib"] }

plugins/edriver/Makefile

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
22
CMD_CLANG ?= clang
3+
# Extra cargo features appended to every --features= flag.
4+
# Set to ",vendored-libelf" on glibc hosts (ubuntu aarch64 CI) where the
5+
# system libelf.a is glibc-compiled and vendored compilation is required.
6+
CARGO_EXTRA_FEATURES ?=
37

48
LIB_PATH := ../libs
59
LIBBPF_CFLAGS = "-fPIC"
@@ -28,14 +32,14 @@ build: \
2832
headers/libbpf/libbpf.a
2933

3034
cargo fmt
31-
cargo build --release --bin edriver --target $(PLATFORM)-unknown-linux-$(LIBC) --features=static
35+
cargo build --release --bin edriver --target $(PLATFORM)-unknown-linux-$(LIBC) --features=static$(CARGO_EXTRA_FEATURES)
3236

3337
debug: \
3438
headers/libbpf/libbpf.a
3539

3640
cargo fmt
37-
cargo build --release --bin edriver --target $(PLATFORM)-unknown-linux-$(LIBC) --features=static,debug
41+
cargo build --release --bin edriver --target $(PLATFORM)-unknown-linux-$(LIBC) --features=static,debug$(CARGO_EXTRA_FEATURES)
3842

3943
test:
4044
cargo fmt
41-
cargo test --target $(PLATFORM)-unknown-linux-$(LIBC) --features=static,debug -- --include-ignored --show-output
45+
cargo test --target $(PLATFORM)-unknown-linux-$(LIBC) --features=static,debug$(CARGO_EXTRA_FEATURES) -- --include-ignored --show-output
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[toolchain]
2+
channel = "stable"
3+
targets = [
4+
"x86_64-unknown-linux-musl",
5+
"aarch64-unknown-linux-musl",
6+
]

0 commit comments

Comments
 (0)