Skip to content

Commit cd9975e

Browse files
Merge pull request #479 from DataDog/fix/linux-musl-static-build
fix(release): build Linux binaries as static musl to restore glibc 2.35 compatibility
2 parents f261418 + 69ca572 commit cd9975e

6 files changed

Lines changed: 101 additions & 45 deletions

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: Install musl toolchain
2+
description: Install x86_64 musl-tools (apt) and the cached aarch64 musl cross compiler from cross-tools/musl-cross.
3+
4+
runs:
5+
using: composite
6+
steps:
7+
- name: Cache aarch64 musl cross compiler
8+
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
9+
with:
10+
path: ~/musl-cross
11+
key: aarch64-unknown-linux-musl-cross-tools-20260430
12+
- name: Install musl toolchains
13+
shell: bash
14+
run: |
15+
for i in 1 2 3; do
16+
sudo apt-get update && sudo apt-get install -y musl-tools && break
17+
[ "$i" = 3 ] && exit 1
18+
sleep 5
19+
done
20+
if [ ! -x "$HOME/musl-cross/aarch64-unknown-linux-musl/bin/aarch64-unknown-linux-musl-gcc" ]; then
21+
mkdir -p "$HOME/musl-cross"
22+
curl -fL "https://github.com/cross-tools/musl-cross/releases/download/20260430/aarch64-unknown-linux-musl.tar.xz" \
23+
| tar -xJ -C "$HOME/musl-cross"
24+
fi
25+
{
26+
echo "$HOME/musl-cross/aarch64-unknown-linux-musl/bin"
27+
} >> "$GITHUB_PATH"
28+
{
29+
echo "CC_x86_64_unknown_linux_musl=musl-gcc"
30+
echo "CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER=musl-gcc"
31+
echo "CC_aarch64_unknown_linux_musl=aarch64-unknown-linux-musl-gcc"
32+
echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-unknown-linux-musl-gcc"
33+
} >> "$GITHUB_ENV"

.github/workflows/ci.yml

Lines changed: 26 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -61,53 +61,46 @@ jobs:
6161
name: Cross Compile (Linux)
6262
runs-on: ubuntu-latest
6363
env:
64-
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
65-
CC_aarch64_unknown_linux_gnu: aarch64-linux-gnu-gcc
64+
RUSTFLAGS: "-C target-feature=+crt-static"
6665
steps:
6766
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
67+
- name: Install Rust
68+
run: |
69+
rustup toolchain install stable --profile minimal
70+
rustup default stable
71+
rustup target add x86_64-unknown-linux-musl aarch64-unknown-linux-musl
6872
- uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
6973
with:
7074
path: |
7175
~/.cargo/registry/index/
7276
~/.cargo/registry/cache/
7377
~/.cargo/git/db/
7478
target/
75-
key: cargo-ubuntu-cross-linux-${{ hashFiles('**/Cargo.lock') }}
79+
key: cargo-ubuntu-cross-linux-musl-${{ hashFiles('**/Cargo.lock') }}
7680
restore-keys: cargo-ubuntu-
77-
- name: Install Rust
81+
- uses: ./.github/actions/install-musl-toolchain
82+
- name: Build x86_64 musl
83+
run: cargo build --release --target x86_64-unknown-linux-musl --features vendored-openssl
84+
- name: Verify x86_64 static linkage
7885
run: |
79-
rustup toolchain install stable --profile minimal
80-
rustup default stable
81-
rustup target add aarch64-unknown-linux-gnu
82-
- name: Install aarch64 cross toolchain
86+
file target/x86_64-unknown-linux-musl/release/pup
87+
if readelf -d target/x86_64-unknown-linux-musl/release/pup | grep -q "(NEEDED)"; then
88+
echo "ERROR: binary has dynamic NEEDED entries"
89+
exit 1
90+
fi
91+
- name: Build aarch64 musl
92+
run: cargo build --release --target aarch64-unknown-linux-musl --features vendored-openssl
93+
- name: Verify aarch64 static linkage
8394
run: |
84-
for i in 1 2 3; do
85-
sudo apt-get update && sudo apt-get install -y gcc-aarch64-linux-gnu && exit 0
86-
sleep 5
87-
done
88-
exit 1
89-
- name: Build all targets
90-
run: |
91-
PIDS=()
92-
for target in x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu; do
93-
(set -o pipefail
94-
echo "==> Starting ${target}"
95-
cargo build --release --target "${target}" --features vendored-openssl 2>&1 \
96-
| tee "/tmp/${target}.log"
97-
) &
98-
PIDS+=($!)
99-
done
100-
status=0
101-
for pid in "${PIDS[@]}"; do
102-
wait "$pid" || status=1
103-
done
104-
exit $status
95+
file target/aarch64-unknown-linux-musl/release/pup
96+
if readelf -d target/aarch64-unknown-linux-musl/release/pup | grep -q "(NEEDED)"; then
97+
echo "ERROR: binary has dynamic NEEDED entries"
98+
exit 1
99+
fi
105100
- name: Report sizes
106101
run: |
107-
for target in x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu; do
108-
echo "${target}:"
109-
ls -lh "target/${target}/release/pup"
110-
done
102+
ls -lh target/x86_64-unknown-linux-musl/release/pup
103+
ls -lh target/aarch64-unknown-linux-musl/release/pup
111104
112105
cross-compile-macos:
113106
name: Cross Compile (macOS)

.github/workflows/release.yml

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ jobs:
1818
name: Build Linux
1919
runs-on: ubuntu-latest
2020
env:
21-
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
22-
CC_aarch64_unknown_linux_gnu: aarch64-linux-gnu-gcc
21+
RUSTFLAGS: "-C target-feature=+crt-static"
2322
steps:
2423
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
2524
with:
@@ -30,7 +29,7 @@ jobs:
3029
run: |
3130
rustup toolchain install stable --profile minimal
3231
rustup default stable
33-
rustup target add aarch64-unknown-linux-gnu
32+
rustup target add x86_64-unknown-linux-musl aarch64-unknown-linux-musl
3433
- name: Cache Rust dependencies
3534
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
3635
with:
@@ -42,13 +41,7 @@ jobs:
4241
key: ${{ runner.os }}-cargo-release-${{ hashFiles('**/Cargo.lock') }}
4342
restore-keys: |
4443
${{ runner.os }}-cargo-release-
45-
- name: Install aarch64 cross toolchain
46-
run: |
47-
for i in 1 2 3; do
48-
sudo apt-get update && sudo apt-get install -y gcc-aarch64-linux-gnu && exit 0
49-
sleep 5
50-
done
51-
exit 1
44+
- uses: ./.github/actions/install-musl-toolchain
5245
- name: Install syft
5346
uses: anchore/sbom-action/download-syft@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0
5447
- uses: goreleaser/goreleaser-action@1a80836c5c9d9e5755a25cb59ec6f45a3b5f41a8 # v7.2.1

.goreleaser-linux.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ builds:
2020
binary: pup
2121
command: build
2222
targets:
23-
- x86_64-unknown-linux-gnu
24-
- aarch64-unknown-linux-gnu
23+
- x86_64-unknown-linux-musl
24+
- aarch64-unknown-linux-musl
2525
flags:
2626
- --release
2727
- --features=vendored-openssl

docs/ARCHITECTURE.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,17 @@ Technical architecture and design rationale for Pup CLI.
1313
4. **Concurrency** - Async/await with tokio runtime
1414
5. **Strong type system** - Catch errors at compile time with rich enums
1515

16+
### Static Linking for Linux
17+
18+
Linux release binaries are statically linked using musl libc:
19+
- **No glibc dependency** - Works across all Linux distributions, including older systems like Ubuntu 22.04 (Jammy) with glibc 2.35
20+
- **Fully self-contained** - No library compatibility issues
21+
- **Portable** - Same binary works on Debian, Ubuntu, Alpine, CentOS, etc.
22+
23+
Build targets:
24+
- `x86_64-unknown-linux-musl` - Intel/AMD 64-bit processors
25+
- `aarch64-unknown-linux-musl` - ARM 64-bit processors
26+
1627
**Tradeoffs:**
1728
- Steeper learning curve than Go
1829
- Longer compile times

docs/TROUBLESHOOTING.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,32 @@
22

33
Common issues and solutions for Pup CLI.
44

5+
## Compatibility Issues
6+
7+
### GLIBC version error on Linux
8+
9+
**Symptoms:**
10+
```
11+
pup: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.38' not found (required by pup)
12+
pup: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.39' not found (required by pup)
13+
```
14+
15+
**Solution:**
16+
Upgrade to **pup 0.58.4 or later**. Starting with version 0.58.4, Linux binaries are statically linked with musl libc and have **no glibc dependency**.
17+
18+
```bash
19+
# Update to the latest version
20+
brew upgrade pup
21+
22+
# Or download the latest release manually
23+
curl -L https://github.com/DataDog/pup/releases/latest/download/pup_Linux_x86_64.tar.gz | tar xz
24+
```
25+
26+
**Technical details:**
27+
- Versions before 0.58.4 were dynamically linked and required glibc ≥ 2.38
28+
- Versions 0.58.4+ are static musl binaries that work on any Linux distribution
29+
- No action required after upgrading — the new binary "just works"
30+
531
## Authentication Issues
632

733
### OAuth2 Login Fails

0 commit comments

Comments
 (0)