Skip to content

Commit 14ad3d5

Browse files
Merge pull request #549 from roc-lang/feat/static-pie-runtime
Use released linker inputs and support static PIE
2 parents 63cc5f7 + 7921a0f commit 14ad3d5

29 files changed

Lines changed: 463 additions & 66 deletions
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: Cache linker inputs
2+
description: Cache verified runtime release archives by their pinned content identity
3+
inputs:
4+
target:
5+
description: Target restored by this job, or all for multi-target validation
6+
default: all
7+
runs:
8+
using: composite
9+
steps:
10+
- id: directory
11+
shell: bash
12+
run: |
13+
python - <<'PY'
14+
import os
15+
from scripts.linker_inputs import cache_dir
16+
with open(os.environ['GITHUB_OUTPUT'], 'a') as out:
17+
print(f'path={cache_dir()}', file=out)
18+
PY
19+
- uses: actions/cache@v5
20+
with:
21+
path: ${{ steps.directory.outputs.path }}
22+
key: linker-inputs-${{ runner.os }}-${{ inputs.target }}-${{ hashFiles('linker-inputs.lock.json') }}

‎.github/workflows/ci.yml‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,16 @@ jobs:
6767
run: |
6868
rustup toolchain install "${{ needs.rust-version.outputs.version }}" --profile minimal --target x86_64-unknown-linux-musl --component llvm-tools-preview
6969
rustup default "${{ needs.rust-version.outputs.version }}"
70+
- uses: ./.github/actions/linker-inputs-cache
7071
- name: Test release tooling
71-
run: python3 -m unittest scripts/test_check_release_version.py scripts/test_check_release_branch.py scripts/test_check_version_bump.py scripts/test_update_nix_release.py scripts/test_check_roc_nightly.py scripts/test_windows_runtime.py scripts/test_third_party_notices.py
72+
run: python3 -m unittest scripts/test_check_release_version.py scripts/test_check_release_branch.py scripts/test_check_version_bump.py scripts/test_update_nix_release.py scripts/test_check_roc_nightly.py scripts/test_windows_runtime.py scripts/test_third_party_notices.py scripts/test_linker_inputs.py
7273
- name: Check third-party notices are current
7374
run: python3 scripts/third_party_notices.py --check
7475
- name: Check Roc nightly pins are in sync
7576
run: python3 scripts/check_roc_nightly.py
7677
- name: Check OS witness API boundaries
7778
run: python3 scripts/test_system_api.py
79+
- uses: ./.github/actions/linker-inputs-cache
7880
- name: Format, check, and test examples
7981
run: ./scripts/test.py --operation validate
8082
- name: Check musl allocator syscall churn
@@ -120,6 +122,7 @@ jobs:
120122
run: |
121123
rustup toolchain install "${{ needs.rust-version.outputs.version }}" --profile minimal --target x86_64-pc-windows-msvc --component rust-docs,llvm-tools-preview
122124
rustup default "${{ needs.rust-version.outputs.version }}"
125+
- uses: ./.github/actions/linker-inputs-cache
123126
- name: Format, check, and test examples
124127
shell: bash
125128
run: ./scripts/test.py --operation validate
@@ -170,6 +173,10 @@ jobs:
170173
- name: Install LLVM tools for Linux host stripping
171174
if: endsWith(matrix.target, 'musl')
172175
run: rustup component add llvm-tools-preview
176+
- uses: ./.github/actions/linker-inputs-cache
177+
if: endsWith(matrix.target, 'musl') || matrix.target == 'x64win'
178+
with:
179+
target: ${{ matrix.target }}
173180
- name: Cross-compile every example
174181
shell: bash
175182
run: ./scripts/test.py --operation build --target "${{ matrix.target }}" --artifact-dir dist/example-binaries

‎.github/workflows/linker-inputs.yml‎

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ jobs:
4040
path: dist/linker-inputs/*
4141
if-no-files-found: error
4242
publish:
43+
concurrency:
44+
group: linker-inputs-publication
45+
cancel-in-progress: false
4346
needs: build
4447
if: github.event_name == 'workflow_dispatch' && github.ref_name == github.event.repository.default_branch
4548
runs-on: ubuntu-24.04
@@ -57,10 +60,19 @@ jobs:
5760
GH_TOKEN: ${{ github.token }}
5861
run: |
5962
tag=$(python3 scripts/linker_inputs.py manifest --directory dist/linker-inputs --repository "$GITHUB_REPOSITORY")
60-
if gh release view "$tag" >/dev/null 2>&1; then
61-
mkdir -p target/published-inputs
62-
gh release download "$tag" --dir target/published-inputs
63-
diff -r dist/linker-inputs target/published-inputs
64-
else
65-
gh release create "$tag" dist/linker-inputs/* --target "$GITHUB_SHA" --prerelease --latest=false --title "Linker inputs ${tag#linker-inputs-}" --notes "Independently built native runtime inputs. Consumers pin the attached manifest and archive SHA-256 hashes."
63+
if ! gh release view "$tag" >/dev/null 2>&1; then
64+
gh release create "$tag" --target "$GITHUB_SHA" --draft --prerelease --latest=false --title "Linker inputs ${tag#linker-inputs-}" --notes "Independently built native runtime inputs. Consumers pin the attached manifest and archive SHA-256 hashes."
6665
fi
66+
mkdir -p target/published-inputs
67+
gh release download "$tag" --dir target/published-inputs || true
68+
for file in dist/linker-inputs/*; do
69+
name=$(basename "$file")
70+
if [[ -f "target/published-inputs/$name" ]]; then
71+
cmp "$file" "target/published-inputs/$name"
72+
else
73+
gh release upload "$tag" "$file"
74+
fi
75+
done
76+
gh release download "$tag" --dir target/published-inputs --clobber
77+
diff -r dist/linker-inputs target/published-inputs
78+
gh release edit "$tag" --draft=false --prerelease --latest=false
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# TODO for the release after 0.28.0: pin a Roc nightly containing #12949,
2+
# remove this workflow, and retire the fixed-address musl startup patch.
3+
name: Static runtime compatibility
4+
on:
5+
pull_request:
6+
workflow_dispatch:
7+
permissions: {}
8+
jobs:
9+
native:
10+
permissions:
11+
contents: read
12+
strategy:
13+
fail-fast: false
14+
matrix:
15+
target: [x64musl, arm64musl, x64mac, arm64mac, x64win]
16+
compiler: [pinned, pie]
17+
include:
18+
- {target: x64musl, os: ubuntu-24.04}
19+
- {target: arm64musl, os: ubuntu-24.04-arm}
20+
- {target: x64mac, os: macos-26-intel}
21+
- {target: arm64mac, os: macos-26}
22+
- {target: x64win, os: windows-2025}
23+
runs-on: ${{ matrix.os }}
24+
steps:
25+
- uses: actions/checkout@v7
26+
- uses: mlugg/setup-zig@8d6198c65fb0feaa111df26e6b467fea8345e46f
27+
if: matrix.compiler == 'pie'
28+
with:
29+
version: 0.17.1
30+
- uses: roc-lang/setup-roc@cbe782d6f165b89c87d99f50a59ac4f5f73b4427
31+
if: matrix.compiler == 'pinned'
32+
with:
33+
version: nightly-new-compiler
34+
nightly-tag: nightly-2026-11-06-34f137a
35+
- uses: actions/checkout@v7
36+
if: matrix.compiler == 'pie'
37+
with:
38+
repository: roc-lang/roc
39+
ref: c080bd1bc565531389f74444d3736d1bfb8e5352
40+
path: target/roc-pie
41+
- uses: actions/cache@v5
42+
if: matrix.compiler == 'pie'
43+
id: compiler-cache
44+
with:
45+
path: target/roc-pie/zig-out/bin
46+
key: roc-pie-${{ runner.os }}-${{ runner.arch }}-c080bd1bc565531389f74444d3736d1bfb8e5352-zig-0.17.1-strip
47+
- name: Build the pinned PIE compiler
48+
if: matrix.compiler == 'pie' && steps.compiler-cache.outputs.cache-hit != 'true'
49+
shell: bash
50+
working-directory: target/roc-pie
51+
run: zig build roc -Doptimize=ReleaseFast -Dstrip -j2
52+
- name: Select the PIE compiler
53+
shell: bash
54+
if: matrix.compiler == 'pie'
55+
run: echo "ROC=$GITHUB_WORKSPACE/target/roc-pie/zig-out/bin/roc${{ runner.os == 'Windows' && '.exe' || '' }}" >> "$GITHUB_ENV"
56+
- uses: mlugg/setup-zig@8d6198c65fb0feaa111df26e6b467fea8345e46f
57+
with:
58+
version: 0.17.0
59+
- name: Install the platform Rust toolchain
60+
shell: bash
61+
run: |
62+
version=$(sed -n 's/^channel = "\(.*\)".*/\1/p' rust-toolchain.toml)
63+
rustup toolchain install "$version" --profile minimal --component llvm-tools-preview
64+
if [[ "$RUNNER_OS" == "Windows" ]]; then
65+
rustup component add rust-docs --toolchain "$version"
66+
fi
67+
- uses: ./.github/actions/linker-inputs-cache
68+
if: endsWith(matrix.target, 'musl') || matrix.target == 'x64win'
69+
with:
70+
target: ${{ matrix.target }}
71+
- name: Run native source, build, and execution checks
72+
run: python scripts/test.py --target ${{ matrix.target }}
73+
- name: Exercise development backend startup and callbacks
74+
if: endsWith(matrix.target, 'musl')
75+
shell: bash
76+
env:
77+
TARGET: ${{ matrix.target }}
78+
run: |
79+
mkdir -p dist/dev-compat
80+
compiler="${ROC:-roc}"
81+
"$compiler" build tests/tcp-server.roc --opt=dev --target="$TARGET" --output=dist/dev-compat/tcp-server
82+
dist/dev-compat/tcp-server > target/dev-tcp-server.log
83+
"$compiler" build tests/resource-lifetimes/resource-lifetime.roc --opt=dev --target="$TARGET" --output=dist/dev-compat/resources
84+
dist/dev-compat/resources
85+
python3 scripts/check_static_executables.py dist/dev-compat --format ${{ matrix.compiler }}
86+
- name: Verify executable format
87+
if: endsWith(matrix.target, 'musl')
88+
run: python3 scripts/check_static_executables.py dist/example-binaries/${{ matrix.target }} --format ${{ matrix.compiler }}

‎.github/workflows/release.yml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ jobs:
8888
RELEASE_VERSION: ${{ needs.release-info.outputs.release_version }}
8989
run: python scripts/check_release_version.py "$RELEASE_VERSION" --update
9090

91+
- uses: ./.github/actions/linker-inputs-cache
9192
- name: Build Windows host
9293
shell: pwsh
9394
run: python scripts/build.py
@@ -151,6 +152,7 @@ jobs:
151152
RELEASE_VERSION: ${{ needs.release-info.outputs.release_version }}
152153
run: python3 scripts/check_release_version.py "$RELEASE_VERSION" --update
153154

155+
- uses: ./.github/actions/linker-inputs-cache
154156
- name: Build platform for all targets
155157
run: ./scripts/build.py --all
156158

‎.gitignore‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,6 @@ __pycache__/
100100

101101
# Locally downloaded Roc nightly
102102
.roc-bin/
103+
104+
# Native inputs are fetched from content-addressed linker-input releases.
105+
platform/targets/

‎CONTRIBUTING.md‎

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,24 @@ reviewed set in `scripts/windows_runtime.py` stops it, as does a package with no
125125
license file. CI and the release workflow run it with `--check` and fail when
126126
the committed file is stale.
127127

128-
Prebuilt binaries such as musl's `libc.a` and `crt1.o` and LLVM's `libunwind.a`
129-
are recorded in `licenses/native-inputs.json` with their SHA-256, origin and
130-
license text in `licenses/`. Any file under a macOS or Linux target directory
131-
other than the generated `libhost.a` must be recorded there. When replacing one,
132-
record where the new file came from and its license before regenerating.
128+
Native runtime inputs are built by the independent Linker inputs workflow.
129+
`linker-inputs.lock.json` pins each release archive by URL and SHA-256. Normal
130+
host builds fetch the required target into a shared content-addressed cache,
131+
verify it, and restore the runtime alongside the freshly built Rust host.
132+
GitHub Actions caches the same archives. Published platform bundles contain all
133+
runtime inputs, provenance, and licenses needed by applications.
134+
135+
To update runtime inputs, change the producer recipe, run its native matrix,
136+
and dispatch Linker inputs on the default branch. Download its published lock
137+
and run `python3 scripts/pin_linker_inputs.py /path/to/linker-inputs.lock.json`,
138+
then regenerate third-party notices and commit the lock and text metadata.
139+
Generated target objects and libraries must stay outside Git.
140+
141+
Linux startup supports fixed-address executables from the pinned Roc nightly
142+
and static PIE from the compiler's ASLR change. The compatibility workflow tests
143+
both compilers against the same pinned runtime release. Linux Rust and C host
144+
code is built as PIC. The producer also validates startup with stored function
145+
pointers, thread-local data, and threads on both Linux architectures.
133146

134147
For changes to the Nix release outputs, run `nix flake check`. This builds and
135148
runs an application against the pinned published platform inside the Nix sandbox;

‎THIRD_PARTY_LICENSES.md‎

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,19 @@ against `licenses/native-inputs.json`, which records where each one came from.
5252

5353
| File | Component | SHA-256 |
5454
| --- | --- | --- |
55-
| `platform/targets/x64musl/crt1.o` | musl libc 1.2.4 | `ffb51a69191a69fc34acaec1003fabe245d8841da7036d124d3445718415f9ea` |
56-
| `platform/targets/x64musl/libc.a` | musl libc 1.2.4 | `85f5eb2316bc6f86be7bd66008488c7d2eca290942b01ad2e86f02f8b5e8c90f` |
57-
| `platform/targets/arm64musl/crt1.o` | musl libc 1.2.2 | `0245544b81293be7bc86df1a8c0140ce1c0345c6abef11dcb20d0cfebc237ef1` |
58-
| `platform/targets/arm64musl/libc.a` | musl libc 1.2.2 | `209b1743f3b0108f5e3f2196dcef9dd40027928c89dad29fafea896772c84878` |
59-
| `platform/targets/arm64musl/libunwind.a` | LLVM libunwind 20.1.1 | `7d246ebb58d7c90ea3ee1907afc3e7e8f8dc5b8699173dd0373d9c813fff017d` |
60-
| `platform/targets/x64musl/libunwind.a` | LLVM libunwind 20.1.1 | `e59505f4e8e9bb8e19833e40a56555fc68805ec0cecf4084957e8865520ef7f4` |
55+
| `platform/targets/x64musl/libc.a` | musl libc 1.2.6 | `0575ab9542e6faa7f09271478a6b257e6f44334d9c8cd605d94453e52d6f3711` |
56+
| `platform/targets/x64musl/rcrt1.o` | musl libc 1.2.6 | `4594f82520509e2abdae76fedb2eb07887c1e1068f3b8fc167892e08706ea7e8` |
57+
| `platform/targets/x64musl/libunwind.a` | LLVM libunwind Zig 0.17.0 bundled LLVM libunwind | `99bb43428246200d61e7b62c99134cdb4b9712b130db14cf107d889be2200bed` |
58+
| `platform/targets/arm64musl/libc.a` | musl libc 1.2.6 | `29422237a038f967575982f49520716d56220099f2d397704d94d024bf17b21e` |
59+
| `platform/targets/arm64musl/rcrt1.o` | musl libc 1.2.6 | `bcc4ddca200e6a8a0069a3f4a3066414f150c55f68802d973506ef440cf36c31` |
60+
| `platform/targets/arm64musl/libunwind.a` | LLVM libunwind Zig 0.17.0 bundled LLVM libunwind | `788ae40461cba9a5a425ac22bba9440306fe041ee5cfd3789ddee604156e83a3` |
6161

6262
### musl libc
6363

64-
- 1.2.4 (MIT): Alpine Linux 3.18 package musl-dev 1.2.4-r3 (x86_64): https://dl-cdn.alpinelinux.org/alpine/v3.18/main/x86_64/musl-dev-1.2.4-r3.apk, files usr/lib/libc.a and usr/lib/crt1.o.
65-
License: https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT?h=v1.2.4
66-
- 1.2.2 (MIT): Debian 11 package musl-dev 1.2.2-1 (arm64): https://deb.debian.org/debian/pool/main/m/musl/musl-dev_1.2.2-1_arm64.deb, files usr/lib/aarch64-linux-musl/libc.a and usr/lib/aarch64-linux-musl/crt1.o.
67-
License: https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT?h=v1.2.2
64+
- 1.2.6 (MIT): https://github.com/roc-lang/basic-cli/releases/download/linker-inputs-548c70b9bde20eceee81ddbfd3a16fbbf178ec262db362980e65eda3f6881521/x64musl-36af1c9d069a4c8e270bae59fff49aacdae30aa6d1ab402e20c02e359c70f7ce.tar.gz.
65+
License: https://musl.libc.org/releases/musl-1.2.6.tar.gz
66+
- 1.2.6 (MIT): https://github.com/roc-lang/basic-cli/releases/download/linker-inputs-548c70b9bde20eceee81ddbfd3a16fbbf178ec262db362980e65eda3f6881521/arm64musl-ef2f4332bee02ca5aeaab1eed455a18f738ca6da20fa8d7a649aae24c05818cc.tar.gz.
67+
License: https://musl.libc.org/releases/musl-1.2.6.tar.gz
6868

6969
```text
7070
musl as a whole is licensed under the following standard MIT license:
@@ -143,6 +143,7 @@ Kylie McClain
143143
Leah Neukirchen
144144
Luca Barbato
145145
Luka Perkov
146+
Lynn Ochs
146147
M Farkas-Dyck (Strake)
147148
Mahesh Bodapati
148149
Markus Wichmann
@@ -172,7 +173,6 @@ Stefan O'Rear
172173
Szabolcs Nagy
173174
Timo Teräs
174175
Trutz Behn
175-
Valentin Ochs
176176
Will Dietz
177177
William Haddon
178178
William Pitcock
@@ -212,7 +212,7 @@ domain. The code also comes with a fallback permissive license for use
212212
in jurisdictions that may not recognize the public domain.
213213
214214
The smoothsort implementation (src/stdlib/qsort.c) is Copyright © 2011
215-
Valentin Ochs and is licensed under an MIT-style license.
215+
Lynn Ochs and is licensed under an MIT-style license.
216216
217217
The x86_64 port was written by Nicholas J. Kain and is licensed under
218218
the standard MIT terms.
@@ -264,8 +264,10 @@ obstacle to adoption, that text has been removed.
264264

265265
### LLVM libunwind
266266

267-
- 20.1.1 (Apache-2.0 WITH LLVM-exception): Rust 1.87.0 self-contained musl runtime, built from rust-lang/llvm-project acaea3d2bb8f351b740db7ebce7d7a40b9e21488: https://static.rust-lang.org/dist/rust-std-1.87.0-x86_64-unknown-linux-musl.tar.xz and https://static.rust-lang.org/dist/rust-std-1.87.0-aarch64-unknown-linux-musl.tar.xz, file lib/rustlib/<target>/lib/self-contained/libunwind.a.
268-
License: https://github.com/rust-lang/llvm-project/blob/acaea3d2bb8f351b740db7ebce7d7a40b9e21488/libunwind/LICENSE.TXT
267+
- Zig 0.17.0 bundled LLVM libunwind (Apache-2.0 WITH LLVM-exception): https://github.com/roc-lang/basic-cli/releases/download/linker-inputs-548c70b9bde20eceee81ddbfd3a16fbbf178ec262db362980e65eda3f6881521/x64musl-36af1c9d069a4c8e270bae59fff49aacdae30aa6d1ab402e20c02e359c70f7ce.tar.gz.
268+
License: https://ziglang.org/download/0.17.0/
269+
- Zig 0.17.0 bundled LLVM libunwind (Apache-2.0 WITH LLVM-exception): https://github.com/roc-lang/basic-cli/releases/download/linker-inputs-548c70b9bde20eceee81ddbfd3a16fbbf178ec262db362980e65eda3f6881521/arm64musl-ef2f4332bee02ca5aeaab1eed455a18f738ca6da20fa8d7a649aae24c05818cc.tar.gz.
270+
License: https://ziglang.org/download/0.17.0/
269271

270272
```text
271273
==============================================================================

‎licenses/musl-COPYRIGHT.txt‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ Kylie McClain
7474
Leah Neukirchen
7575
Luca Barbato
7676
Luka Perkov
77+
Lynn Ochs
7778
M Farkas-Dyck (Strake)
7879
Mahesh Bodapati
7980
Markus Wichmann
@@ -103,7 +104,6 @@ Stefan O'Rear
103104
Szabolcs Nagy
104105
Timo Teräs
105106
Trutz Behn
106-
Valentin Ochs
107107
Will Dietz
108108
William Haddon
109109
William Pitcock
@@ -143,7 +143,7 @@ domain. The code also comes with a fallback permissive license for use
143143
in jurisdictions that may not recognize the public domain.
144144

145145
The smoothsort implementation (src/stdlib/qsort.c) is Copyright © 2011
146-
Valentin Ochs and is licensed under an MIT-style license.
146+
Lynn Ochs and is licensed under an MIT-style license.
147147

148148
The x86_64 port was written by Nicholas J. Kain and is licensed under
149149
the standard MIT terms.

0 commit comments

Comments
 (0)