diff --git a/.github/workflows/gates.yml b/.github/workflows/gates.yml index 4d536e2..75d7ffa 100644 --- a/.github/workflows/gates.yml +++ b/.github/workflows/gates.yml @@ -150,7 +150,7 @@ jobs: - name: every headline number has exactly one home run: | fail=0 - for n in 3470 358 63/63 60/60 45/45 40/40 26/26; do + for n in 3470 358 64/64 60/60 45/45 40/40 26/26; do homes=$(git grep -lF "$n" -- '*.md' ':(exclude)docs/history/*' | sed 's|^docs/report/.*|docs/report/|' | sort -u | tr '\n' ' ') count=$(printf '%s' "$homes" | wc -w) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 96c4a8d..1777037 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -95,16 +95,15 @@ jobs: # x86-64 job with "Exec format error". docs/building.md says what it cost. runs-on: ubuntu-latest timeout-minutes: 30 - # ⭐ Two variants, so nobody has to build one by hand. `default` reads - # APPDIR as well as CROSS_LIBC_DLOPEN_ROOT, because an AppImage runtime - # exports APPDIR on its own. `portable` reads only this project's name, - # which is what a consumer who wants one spelling asked for. - # src/cld-env.h has the argument; E87 and E88 measure both arms. + # ⭐ One variant ships. It reads CROSS_LIBC_DLOPEN_ROOT with APPDIR as the + # fallback, because an AppImage runtime exports APPDIR on its own; the + # strict build that reads only this project's name stays a build-time + # choice (`cd src && make portable`), which is where the choice belongs. + # src/cld-env.h has the argument; E87 and E88 measure the strict arm. strategy: fail-fast: false matrix: arch: [x86_64, aarch64, riscv64, ppc64, ppc64le, loongarch64] - variant: [default, portable] steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -118,13 +117,8 @@ jobs: - name: build on the floor run: | set -eu - if [ "${{ matrix.variant }}" = portable ]; then - sh scripts/build.sh --engine docker --arch ${{ matrix.arch }} --portable - echo "BUILD_DIR=${{ matrix.arch }}-portable" >> "$GITHUB_ENV" - else - sh scripts/build.sh --engine docker --arch ${{ matrix.arch }} - echo "BUILD_DIR=${{ matrix.arch }}" >> "$GITHUB_ENV" - fi + sh scripts/build.sh --engine docker --arch ${{ matrix.arch }} + echo "BUILD_DIR=${{ matrix.arch }}" >> "$GITHUB_ENV" - name: the manifest run: cat "build/$BUILD_DIR/build-manifest.json" @@ -139,7 +133,7 @@ jobs: - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: release-${{ matrix.arch }}-${{ matrix.variant }} + name: release-${{ matrix.arch }} path: | dist/ build/*/build-manifest.json @@ -194,7 +188,7 @@ jobs: # added above without widening this list would ship silently as a # release without that architecture, which is exactly the defect this # step exists to refuse. - - name: collect every architecture and variant + - name: collect every architecture run: | set -eu arches="x86_64 aarch64 riscv64 ppc64 ppc64le loongarch64" @@ -208,12 +202,10 @@ jobs: done mkdir -p dist build for a in $arches; do - for v in default portable; do - src=staged/release-$a-$v - [ -d "$src/dist" ] || { echo "no dist/ for $a/$v"; exit 1; } - cp -a "$src/dist/." dist/ - cp -a "$src/build/." build/ - done + src=staged/release-$a + [ -d "$src/dist" ] || { echo "no dist/ for $a"; exit 1; } + cp -a "$src/dist/." dist/ + cp -a "$src/build/." build/ done ls -la dist echo "--- manifests ---" @@ -229,9 +221,8 @@ jobs: run: | set -eu fail=0 - # ⚠ Only the DEFAULT variant ships loose files to check this way. - # The portable set ships as archives, and package-release.sh already - # checked every object in it against its manifest before archiving. + # ⚠ The default build ships loose files, and there is one variant + # now; every downloaded manifest has its loose set checked here. for a in x86_64 aarch64 riscv64 ppc64 ppc64le loongarch64; do man=build/$a/build-manifest.json for f in $(jq -r '.artifacts | keys[]' "$man"); do diff --git a/docs/building.md b/docs/building.md index a636935..ef7ddb4 100644 --- a/docs/building.md +++ b/docs/building.md @@ -119,33 +119,37 @@ cd src && make portable ``` It is `sh scripts/build.sh --portable` with the orchestration taken away, and -it produces the same objects: built with `-DCLD_STRICT_ENV`, and without -`-fcf-protection=full`. +it produces the same objects as the default build except for one flag: +`-DCLD_STRICT_ENV`. The release ships the default build only; the strict +build stays a build-time choice, because whoever assembles the bundle knows +whether an AppImage runtime is going to export `APPDIR` into the process and +a library cannot. E87 and E88 in `experiments/30-run-tests.sh` measure both +arms. ⛔ **`make portable` says nothing about which glibc you build on.** The floor rule above still decides whether the result loads. -### What the two flags do +### The two flags | flag | effect | |---|---| -| `-DCLD_STRICT_ENV` | the objects read `CROSS_LIBC_DLOPEN_ROOT` and ignore `APPDIR`. The default reads both, because an AppImage runtime exports `APPDIR` into every process it starts, and a consumer who wants one spelling asked for this | -| no `-fcf-protection=full` | the build stops REQUESTING CET | - -⚠ **Dropping the CET flag removes the request, not always the instructions.** A -toolchain that enables CET by default still emits `endbr64`, and that is the -distribution's choice rather than this project's. Measured on a gcc whose -`-Q --help=common` reports `-fcf-protection=full`: the default and portable -builds carry 202 each, identical. The flag is dropped because it does no -protective work here, which +| `-DCLD_STRICT_ENV` | the objects read `CROSS_LIBC_DLOPEN_ROOT` and ignore `APPDIR`. The default reads both, because an AppImage runtime exports `APPDIR` into every process it starts, and `CROSS_LIBC_DLOPEN_ROOT` wins when both are set | +| `-fcf-protection=full` | none by default: no build here asks for CET. It stays askable with `make CET_CFLAGS=-fcf-protection=full` | + +⛔ **No build here requests CET, and that is a measurement rather than an +omission.** The flag adds six `endbr64` to the shims and cannot produce the +IBT property note, because glibc's `crti.o` carries no property on any floor +image and the linker ANDs that absence across the link. Without the note a +CET-enforcing loader turns indirect-branch tracking off for the whole +process, so the instructions the flag emits are ones no loader will honour. [`report/09-the-second-boundary.md`](report/09-the-second-boundary.md) 9.13 -measures, and because a toolchain that does not support it treats being asked -as a hard error. +has the table, and E101 in `experiments/30-run-tests.sh` is the case that +keeps the default honest. -⚠ **The Makefile now asks the compiler rather than assuming from the -architecture.** Targeting x86 is not the same as supporting the flag, and the -architecture test alone let an unsupported flag reach a compiler that refuses -it. Most callers therefore never need `portable` for that reason at all. +⚠ **A toolchain that enables CET by default still emits `endbr64`**, and that +is the distribution's choice rather than this project's. Measured on a gcc +whose `-Q --help=common` reports `-fcf-protection=full`: the shims carry 202 +each with and without our flag, identical. --- @@ -157,7 +161,7 @@ sh scripts/build.sh --arch aarch64 # cross-build (riscv64, ppc64, # ppc64le and loongarch64 too) sh scripts/build.sh --arch both # x86_64 and aarch64, sequentially sh scripts/build.sh --engine docker -sh scripts/build.sh --portable # -DCLD_STRICT_ENV, and no CET flag +sh scripts/build.sh --portable # -DCLD_STRICT_ENV, strict environment sh scripts/build.sh --floor-image debian:bookworm-slim --floor-glibc 2.36 ``` diff --git a/docs/integrating.md b/docs/integrating.md index c31d0ff..3ae18af 100644 --- a/docs/integrating.md +++ b/docs/integrating.md @@ -55,16 +55,20 @@ CROSS_LIBC_DLOPEN_LIBDIR=lib # default; the directory under the ro not own: an AppImage runtime exports it into every process it starts, before anything here runs. `CROSS_LIBC_DLOPEN_ROOT` wins when both are set. -⭐ **If you want one spelling and no interop, take the `portable` build.** -Every release ships it beside the default, as -`cross-libc-dlopen-portable-.tar` and `.zip`. Those objects read -`CROSS_LIBC_DLOPEN_ROOT` and never look at `APPDIR`; the string is not even in -the binary. To build it yourself: +⭐ **If you want one spelling and no interop, build the strict variant.** The +release ships one build, which reads `CROSS_LIBC_DLOPEN_ROOT` with `APPDIR` +as the fallback. A build that reads `CROSS_LIBC_DLOPEN_ROOT` and never looks +at `APPDIR` (the string is not even in the binary) is a build-time choice: ```bash -sh scripts/build.sh --portable +cd src && make portable ``` +or `sh scripts/build.sh --portable` for the same objects through the +container build. Whoever assembles the bundle knows whether an AppImage +runtime is going to export `APPDIR` into the process, which is why the choice +lives there; E87 and E88 in `experiments/30-run-tests.sh` measure both arms. + ⛔ **Every control has exactly one name.** The `ANYLINUX_*` spellings this project used before it was renamed are no longer read by anything in `src/`. Nothing consumed them: there has never been a published release, so no bundle diff --git a/docs/report/01-summary.md b/docs/report/01-summary.md index 6939a14..11423c1 100644 --- a/docs/report/01-summary.md +++ b/docs/report/01-summary.md @@ -11,7 +11,7 @@ | Completion criterion | Status | |---|---| | Both goals demonstrated by a test that fails before and passes after | **Yes.** Goal 1: E5, E12. Goal 2: E22/E23 for the mechanism, E30/E32 and E37a/E37 for the end-to-end | -| The evidence harness still reports all predictions held | **Yes, 63/63** on x86-64 and **60/60** on aarch64, up from 22/22. The AppImage suite adds 45 on a glvnd glibc host, 40 on musl, 26 on each of two pre-glvnd glibc hosts and 7 on a real-application stage, with every unrunnable case SKIPPED by the capability it lacks | +| The evidence harness still reports all predictions held | **Yes, 64/64** on x86-64 and **60/60** on aarch64, up from 22/22. The AppImage suite adds 45 on a glvnd glibc host, 40 on musl, 26 on each of two pre-glvnd glibc hosts and 7 on a real-application stage, with every unrunnable case SKIPPED by the capability it lacks | | No host file modified, verified by checksum | **Yes.** T4.3, identical sha256 before and after | | Bundled libraries still win, verified via `dladdr` | **Yes.** T4.2, all resolved under `$APPDIR` | | A forward-compatibility story that does not depend on foresight | **Yes.** Host-runtime selection for the unenumerable gap, a generated shim for the enumerable one, and a build-time audit (E26) for the version traps | diff --git a/docs/report/08-test-results.md b/docs/report/08-test-results.md index 19fe143..88e41aa 100644 --- a/docs/report/08-test-results.md +++ b/docs/report/08-test-results.md @@ -24,14 +24,14 @@ wrong. ### Tier 1, the evidence table -`sh scripts/run-evidence.sh` reports **63/63 predictions held on x86-64** and +`sh scripts/run-evidence.sh` reports **64/64 predictions held on x86-64** and **60/60 on aarch64**. The x86-64 total was measured at the change that added -E75c through E75f; the aarch64 runner runs the same table, so its total is the -x86-64 total minus the three skips below, and CI re-runs both on every push. +E101; the aarch64 runner runs the same table, so its total is the +x86-64 total minus the four skips below, and CI re-runs both on every push. `experiments/run.ps1` drives the same three stage scripts for a machine with PowerShell and no POSIX shell. -⚠ **The two totals differ by exactly the three cases aarch64 SKIPS**, each +⚠ **The two totals differ by exactly the four cases aarch64 SKIPS**, each naming the capability it lacks rather than the difference being unexplained: | case | why it skips on aarch64 | @@ -39,10 +39,11 @@ naming the capability it lacks rather than the difference being unexplained: | E22 | that libc exports `pthread_cond_init` at one symbol version. The trap needs an obsolete definition beside the current one | | E23 | skipped WITH E22 deliberately. With no trap present the stripped object already returns 0, so E23 would pass whether or not `version-compat.c` does anything | | E58 | section M's trampoline is hand-written x86-64 machine code. What the real aarch64 trampolines do is measured by E69 through E73 and E76/E76b, natively on the ARM runner | +| E101 | `endbr64` is an x86 instruction, and asking aarch64 gcc for `-fcf-protection=full` is a hard error rather than a warning, so there is no flag arm to compare against | ⭐ **E23's skip is the one worth reading.** It was reporting MATCH on the ARM runner while asserting nothing, and skipping it with E22 is what stopped that. -63 minus 3 is 60, and no case is missing for a reason nobody wrote down. +64 minus 4 is 60, and no case is missing for a reason nobody wrote down. E1 through E13 measure the problem. E14 through E21 are one per fix from the first pass: the ELF self-test, the generated-shim compile and behaviour, and five selector diff --git a/docs/report/09-the-second-boundary.md b/docs/report/09-the-second-boundary.md index 0e6918d..19bddb8 100644 --- a/docs/report/09-the-second-boundary.md +++ b/docs/report/09-the-second-boundary.md @@ -362,7 +362,7 @@ Totals with this section in: **40/40 on the musl host** with five named skips, and ubuntu:16.04 with nineteen named skips, **7/7** on the gtk4 stage, and **53/53** in the container suite on x86-64, and **50/50** on aarch64 with the three skips named in section 8, at the measurement this section records; the -totals for the tree as it ships are **63/63** and **60/60** in section 8. +totals for the tree as it ships are **64/64** and **60/60** in section 8. ### 9.8 What the shim does not do, stated as a number diff --git a/docs/report/10-measured-versus-assumed.md b/docs/report/10-measured-versus-assumed.md index 75d3f70..3553a2a 100644 --- a/docs/report/10-measured-versus-assumed.md +++ b/docs/report/10-measured-versus-assumed.md @@ -1,7 +1,7 @@ ## 10. Measured versus assumed **Measured:** every table and quoted output above, plus `sh scripts/run-evidence.sh` -(63/63 on x86-64, 60/60 on aarch64), `sh scripts/run-appimage.sh` (45/45 glvnd glibc, 40/40 musl with five +(64/64 on x86-64, 60/60 on aarch64), `sh scripts/run-appimage.sh` (45/45 glvnd glibc, 40/40 musl with five named skips, 26/26 on each pre-glvnd glibc host, 7/7 on the gtk4 stage), `tools/gap.py --fetch`, the eight-distro inventory, the AppImage inventory, the corpus test, the real mpv and `iHD` VA-API run in section 9.19, and the five-distro `ld.so.cache` survey in `../ground-truth.md`. diff --git a/docs/todo/PROGRESS.md b/docs/todo/PROGRESS.md index dc02b66..182dc20 100644 --- a/docs/todo/PROGRESS.md +++ b/docs/todo/PROGRESS.md @@ -12,236 +12,87 @@ baseline. ## Where the work is right now -⭐ **[`v0.1.0` is published.](https://github.com/pkgforge-dev/cross-libc-dlopen/releases/tag/v0.1.0)** -22 assets: both architectures, both variants, the loose objects for the default -one, and a `.tar`, `.zip` and `.sha256` for each. Tagged on `21e9236`, built on -the glibc 2.31 floor, body generated from the manifests by -`scripts/release-notes.sh`. Nothing in it was typed at release time. - -⚠ **The old `main` had failing gates AND a failing secret sweep.** Both are -green now, so this is also the first time the default branch has been green. - -**The tracker is empty and there is one branch.** Pull requests #8 and #10 -merged; #1, #2 and #6 were auto-closed by Renovate when their bumps landed in -#8; #3, #4 and #5 were closed as superseded, verified against the pins on -`main`; #9 was closed because its diff no longer applies, with the reason and -the open policy question written into it. Issue #7 is answered and closed. -`main` is the only branch. - -| workflow | latest | +⭐ **[`v0.2.3` is published.](https://github.com/pkgforge-dev/cross-libc-dlopen/releases/tag/v0.2.3)** +66 assets: all six architectures (x86_64, aarch64, riscv64, ppc64, ppc64le, +loongarch64), loose objects plus `.tar`, `.zip` and `.sha256` for each, both +variants. Tagged on `34482c7`, built on the glibc 2.31 floor, body generated +from the manifests by `scripts/release-notes.sh`. The `release` workflow went +green end to end on the tag: run +[34025633281](https://github.com/pkgforge-dev/cross-libc-dlopen/actions/runs/34025633281). + +| workflow | latest on `main` | |---|---| -| `gates` | ✅ on `main` | -| `secret-sweep` | ✅ on `main` | -| `release` | ✅ on `v0.1.0`, published | -| `appimage-suite` | ⛔ red on `main`, run [32959228414](https://github.com/pkgforge-dev/cross-libc-dlopen/actions/runs/32959228414) | +| `gates` | ✅ run [34025411777](https://github.com/pkgforge-dev/cross-libc-dlopen/actions/runs/34025411777) | +| `secret-sweep` | ✅ on `34482c7` | +| `release` | ✅ on `v0.2.3`, published | -⛔ **The nine cases that MISMATCH on `main`**, both architectures, so the next -session starts from a list rather than a re-run: - -| case | what is known | -|---|---| -| E30, E37a | the control arm stopped contrasting. Item 1 | -| E33, E34 | the corpus sweep dies partway. Item 2 | -| E59, E62, E64, E66, E77 | ⚠ **not yet diagnosed.** E64 and E66 exit 139, a segmentation fault, on the aarch64 musl host. Whether they share a cause with E33 and E34 is unknown and worth asking first | - -| suite | command | state | -|---|---|---| -| evidence table, x86-64 | `sh scripts/run-evidence.sh` | exit 0, every prediction held | -| evidence table, aarch64 | the same, on `ubuntu-24.04-arm` | exit 0, three cases SKIP by name | -| AppImage suite | `sh scripts/run-appimage.sh` | ⚠ **completes on both architectures for the first time.** Mismatches remain and they are findings, below | -| build, all four | `sh scripts/build.sh --arch both` and again `--portable` | exit 0 | -| the gates, planted | `sh scripts/verify-gates.sh` | 8 proven, 0 not | -| the documents | `sh scripts/check-drift.sh` | exit 0, six sections | - -⛔ **Totals live in [`../report/README.md`](../report/README.md)**, not here. Do not -copy one into this file: both are on the one-home list and a second copy turns -the gate red, which is exactly how commit `f6d126e` broke the branch. +**This branch changes what a release ships.** One variant, and no CET request +from any build. See the work below. --- ## ⛔ The work order -### 1. The A/B's control arm no longer contrasts. This needs a decision - -⭐ **Start here. It is the only item that changes what the project claims.** - -Upstream adopted this project. The demo AppImage's `lib/foreign-dlopen.so` is -gone, `lib/cross-libc-dlopen.so` is in its place, and it is a build of this -project. So the "as shipped" arm of the A/B is no longer upstream's naive shim. - -E30 and E37a are the controls for that arm, and they are what make the patched -arm a measurement rather than a coincidence. Both now MISMATCH, because both -arms work. `docs/report/09-the-second-boundary.md` 9.17 has the output. - -⚠ **Their log lines say `predicted=OK` and that is about the exit status, not -the verdict.** What they assert is the needle, and the needle is the complaint: -`NO-DEVICES` for E30 and `zero accessible devices` for E37a. A MISMATCH there -means the as-shipped arm found a device. - -⛔ **Do not flip the predictions.** A control that has stopped contrasting has -stopped measuring, and rewriting it to expect success converts two controls -into two cases that pass whatever the shim does. - -⚠ The honest control for "the feature is absent" is an AppDir with **no** -dispatcher in `.preload`, not one carrying somebody else's. Adopting that -changes what the suite claims about upstream, which is why it was left for a -decision rather than taken. - -### 2. The corpus sweep dies partway, and E33/E34 are scored against nothing - -⛔ **New, and it had been invisible.** `tests/corpus.c` loads every library in -the host's directory in ONE process. On two hosts that process dies partway and -produces no verdict line at all, so the total is 0 and both cases are scored -against nothing: - -| host | feature ON | feature OFF | -|---|---|---| -| `alpine:3.22` | `Segmentation fault (core dumped)` | `TOTAL=298 OK=3` | -| `debian:trixie-slim` | `FATAL: HWAddressSanitizer requires a kernel with tagged address ABI.` | 99 OK lines, then the same | - -Both causes went to `/dev/null` until this session. ⭐ This is **T-15's** -premise, which was inferred from reading another project and is now observed -here. Its fresh-process-per-library design covers both. ⚠ The Alpine -segmentation fault is **not yet attributed to a library**, and until it is, -nobody should assume it is the corpus's fault rather than this project's. - -⭐ **E49 and E50 are done.** E49 MATCHes on aarch64, and E50 reports 2 live -hazards on x86-64 and 3 on aarch64, the third being the mutex. E50 reads the -condition out of `abi-host`'s size table instead of carrying a per-architecture -number. `docs/report/09-the-second-boundary.md` 9.18. - -### 3. The demo tag is rolling. The suite verifies against the release API - -The upstream publishes one release and its tag is `demo`, and the assets were -replaced twice inside two minutes. ⛔ There is no immutable release to pin to. -The suite therefore carries no checked-in digest: it reads the digest the -release API publishes at download time and verifies the bytes against it, -refusing on a mismatch. `docs/report/09-the-second-boundary.md` 9.15 has the -policy and the reasoning. - -When the download does not verify, the two candidates are a torn read from a -re-upload in progress and a genuinely wrong download. The suite re-reads the -release once before refusing, so the first is not blamed on the network. - -⚠ All three assets come from `pkgforge-dev/Anylinux-AppImages`, the upstream: -the demo AppImage, `gtk4-demo`, and the `host-drivers` builds. The upstream now -publishes the host-drivers assets too, so nothing depends on a fork. - -### 4. T-12 is answered for one half and unanswerable for the other - -The table is in T-12's entry, both runners. ⭐ Every case that ends on its own -is far under its timeout: the slowest is 11 seconds against a 25-second floor -and the rest are at or below one second. The fear the entry was opened on is -not in the data. - -⚠ **E61 and E62 measure 30 against a configured 30, and that is not a margin -of zero.** A GL binary never exits on its own, so the timeout is how those two -END. ⛔ Do not raise them on that reading. What stays open is that the -instrumentation cannot tell "ran to its timeout on purpose" from "was killed -before finishing", because both look identical. - -### 5. T-10, T-11, T-16 - -T-10's entry now carries which gates have been seen to refuse and where, and -⭐ four of them were not planted: they went red on a runner against a real -defect, which is stronger than a plant because nobody chose the shape of the -failure. ⛔ Four are still unproven and the entry names them: the endings gate, -which `.gitattributes` makes unplantable from the working tree; the two -`generated` steps; and the artefact verifier's floor rule. Those need a runner -and a deliberate push. - -⛔ **One guard remains unproven**, `release.yml`'s ancestor refusal. Item 6 has -why the release going green did not test it. `package-release.sh`'s two were -planted this session and both refuse. - -T-11 and T-16's cheaper half were not started. T-16's is a `glprobe` change, -and it can only be verified by a GL-capable suite run. - -### 6. The release is cut. What it did NOT prove - -⭐ **`v0.1.0` is published**, on `21e9236`, 22 assets, both architectures and -both variants. The `release` workflow went green end to end on the tag: the -evidence table on both runners, the floor-checked build, the packaging, the -generated body, and the publish. - -⚠ **A first release is not proof that the release path is right**, only that it -ran. Two things in it are still unproven: - -- ⛔ **`release.yml`'s ancestor refusal.** It rejects a tag whose commit never - reached the default branch. `v0.1.0`'s commit did, so the check passed - without being tested. Firing it needs a tag on a commit that is NOT an - ancestor of `main`, and if the guard fails, that publishes. It is the one - guard in this family nobody has seen refuse. -- **The floor rule at publish time** fired on nothing, because no artefact - exceeded the floor. `package-release.sh`'s other two refusals were planted - and both work. - -⚠ There is no second release to diff against, so `release-notes.sh`'s changelog -range took its "First release" branch. The `prev..TAG` branch is untested. - ---- - -## What this session did - -Every claim below has its measurement in `docs/report/09-the-second-boundary.md` 9.14 through 9.18. - -**Three deep review passes**, each with a different question. - -*Pass 1, can every guard added here refuse?* The dash ratchet was recorded as -having failed to fire. It had not: the refusal condition was `count > pin`, -nothing ever lowered the pin, and the tree had drifted eight under, so the -planted dash landed inside the slack. The pin is exact now and a fall refuses -too. It also counted the `--` that the prose rule exempts inside a code block, -which made the section recording the fix unwritable. The cited-path check -could not see a path cited in front of a command, and so never noticed that -`conventions/prose.md` named a ratchet script that has never existed. - -*Pass 2, what did this branch stop measuring?* The ARM runner was added saying -qemu "emulates the instructions and not a memory model", and section P went on -running the aarch64 trampolines under qemu **on aarch64 silicon**. It picks its -vehicle from the host now and prints it. The marker was removed and four -documents went on calling it load-bearing. - -*Pass 3, does every claim hold when the command is run?* T-13's -"print a MISMATCH in full" was in one harness and not the other, and it was -found by the failure it describes. The corpus cases were the same shape a -third time, reporting a zero total with the reason in a discarded stderr. -`INDEX.md` listed two entries as open that declare themselves DONE. - -**Three new checks, each planted and seen to refuse.** The dash ratchet in -`verify-gates.sh`; the two orchestrators agreeing on the same upstream; every -`INDEX.md` row against its entry's declared status. - -**The AppImage suite completes**, having never done so before. Getting there -meant re-pinning against a mutable tag, moving `gtk4-demo` to the true -upstream, and teaching the suite to read the dispatcher slot out of the AppDir -rather than spelling it. - -⭐ **And completing it found a real limitation nothing else could have.** On -aarch64 a musl object cannot allocate and initialise its own `pthread_mutex_t` -in a glibc process: 40 bytes allocated, 48 written, no crossing involved. It -needed real ARM silicon, three chained reporting defects fixed before it was -even legible, and one wrong fix before the right one. `docs/report/09-the-second-boundary.md` 9.18. - -**`tests/bindprobe.c` builds on aarch64.** - -**T-12 answered**, and **T-10's entry now says which gates have been seen to -refuse and where**, including four that were never planted because they went -red on a runner against a real defect. +### 1. Ship one variant, and stop asking for CET + +⭐ **This is the session's work, on branch `drop-portable-variant`.** Two +decisions, both the operator's: + +- **The release ships the default build only.** The strict build (reads only + `CROSS_LIBC_DLOPEN_ROOT`, never `APPDIR`) stays a build-time choice: + `cd src && make portable`, measured by E87 and E88. quick-sharun sets + `CROSS_LIBC_DLOPEN_ROOT` itself, so the strict assets had no consumer; the + `APPDIR` fallback stays, because upstream's own AppImage relies on it and + [`src/cld-env.h`](../../src/cld-env.h) has that argument in full. +- **No build asks for `-fcf-protection=full`.** Measured in + [`../report/09-the-second-boundary.md`](../report/09-the-second-boundary.md) + 9.13: the flag adds six `endbr64` and cannot produce the IBT property note, + so it does no protective work here. It stays askable with + `make CET_CFLAGS=-fcf-protection=full`. + +**The case that proves it.** E101 in `experiments/30-run-tests.sh` builds the +shim by the default recipe and again with the flag asked for, and requires the +default to come out strictly lighter: + +- FAILS before, against the Makefile that still asked for the flag: + `predictions matched: 63, mismatched: 1` with + `E101 MISMATCH predicted=OK (exit 1, wanted OK)`; both arms tied. +- PASSES after: `E101 MATCH predicted=OK fewer endbr64 than the flag arm: + default 3472, asked for: 3478`, and the table is green end to end on + x86-64. The suite total moves 63 to 64 and every one-home record moved with + it: [`../report/`](../report/README.md) 01, 08, 09 and 10, the list in + [`gates.yml`](../../.github/workflows/gates.yml), and the same list in + `scripts/verify-gates.sh`. The aarch64 total and the four-skip list are in + report 08, which is that number's home. + +Measured locally besides the suite: `build.sh --arch x86_64` and +`--arch x86_64 --portable` both exit 0 with the right manifest variant, the +default build's `gl-fwd.so` carries 3472 `endbr64`, and both directories +package and generate a body. + +### 2. What is still open + +Nothing from this session. The open list is [`INDEX.md`](INDEX.md) and the +work order lives nowhere else. --- ## ⚠ What a new session should distrust -- **The `.preload` baseline is DERIVED, not shipped.** The AppImage ships this - project's own forwarding shims in its `.preload`, and restoring that list - would make every absence case measure a presence. `41-extract.sh` prints what - it drops on every extraction. If that line disappears, look at it. -- **`ground-truth.md`'s inventory carries a verdict column now.** Two rows are - UNVERIFIED and say why. Do not quietly re-attach them to the new binary. -- **The tracker is evidence, not instruction.** Pull request #9's premise, that - `-fcf-protection=full` breaks aarch64, is true of `main` and already fixed on - this branch; what remains of it is a policy question about the default. -- **A guard that has never been seen to refuse is a guard nobody knows works.** - Three were found decorative or unarmed this session and every one of them - looked fine. +- **The aarch64 total on this branch is expected, not yet measured here.** + This machine has no ARM silicon: it is the x86-64 total minus four named + skips (E22, E23, E58 and now E101). The PR's CI run is the measurement. +- **`verify-gates.sh`'s one-home list claimed to be identical to + `gates.yml`'s and was not** (it still gated the previous suite totals after + the totals had moved on) until this branch aligned them. A comment that + asserts sameness is a claim; diff it. +- **`skip E76` and `skip E76b` at the foot of `experiments/30-run-tests.sh` + name a function that does not exist in that file.** On an x86-64 machine + with neither qemu nor an aarch64 cross compiler those lines would fail with + `skip: command not found` and leave E76 and E76b unscored rather than + SKIPPED by name. CI never reaches that path (it installs both), so it has + never been seen to fire. Named, not fixed: a change there belongs to its + own decision. +- **A guard that has never been seen to refuse is a guard nobody knows + works.** Three were found decorative or unarmed in earlier sessions and + every one of them looked fine. diff --git a/experiments/30-run-tests.sh b/experiments/30-run-tests.sh index 878b399..4893239 100644 --- a/experiments/30-run-tests.sh +++ b/experiments/30-run-tests.sh @@ -31,9 +31,9 @@ printf '%s\n' 'deb http://archive.debian.org/debian bullseye main' \ rm -f /etc/apt/sources.list.d/*.sources 2>/dev/null || true apt-get update -qq -o Acquire::Check-Valid-Until=false -o Acquire::Retries=3 \ >/work/.apt-update.log 2>&1 || true -apt-get install -y -qq -o Acquire::Retries=3 gcc binutils python3 \ +apt-get install -y -qq -o Acquire::Retries=3 gcc binutils python3 make \ >/work/.apt-install.log 2>&1 || true -for _tool in gcc python3 readelf; do +for _tool in gcc python3 readelf make; do command -v "$_tool" >/dev/null 2>&1 || { echo "STAGE 3 CANNOT RUN: $_tool did not install; the apt output follows" >&2 sed 's/^/ update| /' /work/.apt-update.log >&2 @@ -407,6 +407,42 @@ else run E88 OK "appdir : $PWD/app_old" \ env CROSS_LIBC_DLOPEN_ROOT="$PWD/app_old" ./runtime-select-strict --probe + # ---- the default build asks for no CET flag ---------------------------- + # + # ⛔ -fcf-protection=full does no protective work here, measured in + # docs/report/09-the-second-boundary.md 9.13: it adds six endbr64 to the + # shims and cannot produce the IBT property note, because glibc's crti.o + # carries no property and the linker ANDs that absence across the link. + # The default recipe therefore passes no CET flag, and E101 is the case + # that keeps that true: the SAME sources built by the default recipe and + # by the same recipe with the flag asked for must NOT come out identical, + # because a default build that asked for the flag would tie with the flag + # arm and fail here. Measured on bullseye's gcc 10.2: 3472 against 3478. + # ⚠ x86-64 only. endbr64 does not exist on the other architectures, and + # on aarch64 gcc asking for the flag is a hard error rather than a + # warning, so there is no flag arm to compare against. + if [ "$(uname -m)" = x86_64 ]; then + rm -rf srcbuild && mkdir srcbuild + cp /repo/src/Makefile /repo/src/gl-fwd.c /repo/src/gl-fwd-gl.h \ + /repo/src/ld-conf.h /repo/src/cld-env.h srcbuild/ \ + 2>"$BERR" || bfail "E101 sources" + make -C srcbuild gl-fwd.so >/dev/null 2>"$BERR" \ + || bfail "gl-fwd.so (the default recipe)" + n_def=$(objdump -d srcbuild/gl-fwd.so | grep -c endbr64) + mv srcbuild/gl-fwd.so srcbuild/gl-fwd-default.so + make -C srcbuild gl-fwd.so CET_CFLAGS='-fcf-protection=full' \ + >/dev/null 2>"$BERR" || bfail "gl-fwd.so (the flag arm)" + n_flag=$(objdump -d srcbuild/gl-fwd.so | grep -c endbr64) + run E101 OK "fewer endbr64 than the flag arm" \ + env N_DEF="$n_def" N_FLAG="$n_flag" sh -c \ + '[ "$N_DEF" -lt "$N_FLAG" ] && + echo "fewer endbr64 than the flag arm: default $N_DEF, asked for: $N_FLAG"' + else + echo " E101 SKIPPED - endbr64 is an x86 instruction, and asking" + echo " this architecture's gcc for -fcf-protection=full is a" + echo " hard error rather than a warning." + fi + # ---- H. the version-binding trap, and the forwarders that close it ---- # # T3.2 was blamed on glibc-vs-musl ABI differences for a long time. It is diff --git a/scripts/build-in-env.sh b/scripts/build-in-env.sh index 5afcbcb..41e6097 100644 --- a/scripts/build-in-env.sh +++ b/scripts/build-in-env.sh @@ -130,16 +130,12 @@ cp "$REPO"/tools/* "$WORK/tools/" 2>/dev/null || true printf 'building for %s with %s%s\n' "$ARCH" "$($CC --version | head -1)" \ "${CLD_EXTRA_CFLAGS:+ [variant: $CLD_EXTRA_CFLAGS]}" -# ⚠ CET_CFLAGS= on the command line overrides the Makefile's `:=`, which is -# how the portable variant drops -fcf-protection=full. An empty value here is -# not the same as leaving it out: leaving it out lets the Makefile decide. -if [ "${CLD_NO_CET:-0}" = 1 ]; then - ( cd "$WORK/src" && make CC="$CC" EXTRA_CFLAGS="${CLD_EXTRA_CFLAGS:-}" CET_CFLAGS= >/dev/null ) || - die "make failed for $ARCH" -else - ( cd "$WORK/src" && make CC="$CC" EXTRA_CFLAGS="${CLD_EXTRA_CFLAGS:-}" >/dev/null ) || - die "make failed for $ARCH" -fi +# ⛔ No CET_CFLAGS here, and that is deliberate: the Makefile's default is +# empty because the flag does no protective work here (the comment beside its +# assignment has the measurement). A caller who wants it passes CET_CFLAGS to +# make directly, which a command-line value always wins. +( cd "$WORK/src" && make CC="$CC" EXTRA_CFLAGS="${CLD_EXTRA_CFLAGS:-}" >/dev/null ) || + die "make failed for $ARCH" mkdir -p "$OUT" for f in cross-libc-dlopen.so gl-fwd.so egl-fwd.so gles-fwd.so runtime-select; do diff --git a/scripts/build.sh b/scripts/build.sh index a076de2..84585e0 100644 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -18,9 +18,9 @@ # scripts/build.sh --arch both x86_64 and aarch64, sequentially # scripts/build.sh --engine native no container; refuses if the host # libc is newer than --floor-glibc -# scripts/build.sh --portable reads only CROSS_LIBC_DLOPEN_ROOT and -# no CET flag. `make portable` is the -# same build without this script +# scripts/build.sh --portable reads only CROSS_LIBC_DLOPEN_ROOT. +# `make portable` is the same build +# without this script # scripts/build.sh --check detect and report, build nothing # # Everything it produces lands in build// with a manifest beside it. @@ -50,16 +50,10 @@ KEEP_GOING=0 # The build variant. "default" reads APPDIR as well as CROSS_LIBC_DLOPEN_ROOT, # because an AppImage runtime exports APPDIR into every process it starts. # "portable" reads only this project's own name, which is what a consumer who -# wants one spelling asked for; src/cld-env.h has the argument. +# wants one spelling asked for; src/cld-env.h has the argument. The release +# ships the default only; the strict build stays a build-time choice. VARIANT=default EXTRA_CFLAGS= -# ⭐ The portable variant also drops -fcf-protection=full. Measured in -# docs/report/09-the-second-boundary.md 9.13: the flag emits endbr64 and no IBT property note, the -# note cannot be emitted honestly because glibc's crti.o carries none, and -# without it a CET-enforcing loader does not turn IBT on for the object. So the -# instructions do no protective work, and a consumer targeting a kernel or an -# emulation layer that would rather not see them can have a build without. -NO_CET=0 die() { printf 'build: %s\n' "$*" >&2; exit 1; } note() { printf ' %s\n' "$*"; } @@ -79,7 +73,7 @@ while [ $# -gt 0 ]; do --out) OUT=${2:?--out needs a value}; shift 2 ;; --check) CHECK_ONLY=1; shift ;; --keep-going) KEEP_GOING=1; shift ;; - --portable) VARIANT=portable; EXTRA_CFLAGS=-DCLD_STRICT_ENV; NO_CET=1; shift ;; + --portable) VARIANT=portable; EXTRA_CFLAGS=-DCLD_STRICT_ENV; shift ;; -h|--help) usage 0 ;; *) printf 'build: unknown option %s\n' "$1" >&2; usage 2 ;; esac @@ -222,7 +216,6 @@ build_one() { # build_one [ "$a" = "$host_arch" ] || die "native builds cannot target $a from $host_arch; use a container" CLD_OUT="$out" CLD_ARCH="$a" CLD_FLOOR_GLIBC="$FLOOR_GLIBC" \ CLD_VARIANT="$VARIANT" CLD_EXTRA_CFLAGS="$EXTRA_CFLAGS" \ - CLD_NO_CET="$NO_CET" \ sh "$HERE/build-in-env.sh" "$ROOT" return fi @@ -240,7 +233,6 @@ build_one() { # build_one -e CLD_INSTALL_DEPS=1 \ -e CLD_VARIANT="$VARIANT" \ -e CLD_EXTRA_CFLAGS="$EXTRA_CFLAGS" \ - -e CLD_NO_CET="$NO_CET" \ "$FLOOR_IMAGE" sh /repo/scripts/build-in-env.sh /repo } diff --git a/scripts/release-notes.sh b/scripts/release-notes.sh index 4dab2ee..bcc3558 100644 --- a/scripts/release-notes.sh +++ b/scripts/release-notes.sh @@ -147,15 +147,13 @@ printf '### Assets\n\n' printf 'Each artefact ships three ways: loose, in the `.tar`, and in the `.zip`.\n' printf '⛔ No archive contains a directory, so an extract drops the files where\n' printf 'you are standing.\n\n' -printf '⭐ **Two variants.** The default reads `CROSS_LIBC_DLOPEN_ROOT` and also\n' -printf '`APPDIR`, which an AppImage runtime exports on its own, and is built\n' -printf 'with `-fcf-protection=full`. The `portable` assets read\n' -printf '`CROSS_LIBC_DLOPEN_ROOT` and nothing else, and are built without that\n' -printf 'flag. Take those if you want one spelling and no CET instrumentation.\n' -printf '⚠ The flag accounts for six of the shim`s 3478 endbr64; the rest are the\n' -printf 'trampolines`, spelled as literal bytes, and no flag removes them.\n' -printf 'All five objects differ between the two variants, so the variant ships\n' -printf 'as archives rather than loose files.\n\n' +printf '⭐ **One variant.** The objects read `CROSS_LIBC_DLOPEN_ROOT`, with\n' +printf '`APPDIR` as the fallback, because an AppImage runtime exports `APPDIR`\n' +printf 'into every process it starts and `CROSS_LIBC_DLOPEN_ROOT` wins when both\n' +printf 'are set. The build asks for no CET flag: measured, the flag adds six\n' +printf 'endbr64 to the shims and cannot produce the IBT property note, so it does\n' +printf 'no protective work here. A consumer who wants a build reading only this\n' +printf 'project'"'"'s name makes it at build time: `cd src && make portable`.\n\n' printf '| asset | sha256 |\n|---|---|\n' for f in "$DIST"/*; do [ -f "$f" ] || continue diff --git a/scripts/verify-artifacts.sh b/scripts/verify-artifacts.sh index 4159b85..94bd00d 100644 --- a/scripts/verify-artifacts.sh +++ b/scripts/verify-artifacts.sh @@ -86,28 +86,25 @@ for pair in 'gl-fwd.so gl-fwd-gl.h' 'egl-fwd.so gl-fwd-egl.h' 'gles-fwd.so gl-fw say "$so: SONAME $got_son, $got_n entry points" done -# ⭐ FATAL: the endbr64 instrumentation, which is what -fcf-protection=full -# actually delivers. A build where the flag was dropped, by an edit or by -# the Makefile's CET_CFLAGS failing to resolve on a host whose compiler does -# a compiler that does not answer -dumpmachine the expected way, produces a shim with none, and -# nothing else here would notice. x86-64 only: CET is an x86 feature and the -# aarch64 shim correctly has no endbr64 at all. +# ⭐ The endbr64 count, REPORTED rather than asserted. The trampolines spell +# their endbr64 as literal bytes in gl-fwd.c so the floor's assembler cannot +# be too old for them, and the build asks for no CET flag, so the count here +# should be the trampolines' own and nothing more. E101 in +# experiments/30-run-tests.sh is the case that keeps that true: the default +# recipe must produce strictly fewer endbr64 than the same recipe with the +# flag asked for. x86-64 only: CET is an x86 feature and the shims of every +# other architecture correctly have no endbr64 at all. # -# ⚠ REPORTED, NOT FATAL: the .note.gnu.property IBT note, which is absent. -# The reason is measured, and it is not the one this check used to give. -# `-fcf-protection=full` alone emits no note on bullseye (gcc 10.2), bookworm -# (12.2) or trixie (14.2), because glibc's crti.o carries no property on any -# of the three, and the linker ANDs that absence across the link. -# ⛔ `-Wl,-z,ibt,-z,shstk` DOES emit one on all three, and the note it emits is -# FALSE: _init and _fini come from crti.o/crtn.o, ld.so reaches them through -# DT_INIT and DT_FINI, an indirect call, and neither begins with endbr64. -# Forcing the note would assert a property the object does not have, which is -# worse than not having the note. docs/report/09-the-second-boundary.md 9.13 has the full table. -# -# ⚠ The `portable` variant asks for NO CET, so there the expectation inverts: -# endbr64 present would mean --portable did not reach the compile. Both arms -# are asserted, because a check that only knows one of them cannot tell a -# working variant from a broken flag. +# ⚠ REPORTED, NOT ASSERTED: the .note.gnu.property IBT note, which is absent. +# The reason is measured. `-fcf-protection=full` emits no note on bullseye +# (gcc 10.2), bookworm (12.2) or trixie (14.2), because glibc's crti.o carries +# no property on any of the three, and the linker ANDs that absence across the +# link. ⛔ `-Wl,-z,ibt,-z,shstk` DOES emit one on all three, and the note it +# emits is FALSE: _init and _fini come from crti.o/crtn.o, ld.so reaches them +# through DT_INIT and DT_FINI, an indirect call, and neither begins with +# endbr64. Forcing the note would assert a property the object does not have, +# which is worse than not having the note. +# docs/report/09-the-second-boundary.md 9.13 has the full table. if [ -f "$DIR/gl-fwd.so" ] && [ "$ARCH" = x86_64 ]; then nend=$($OBJDUMP -d "$DIR/gl-fwd.so" 2>/dev/null | grep -c endbr64 || true) say "gl-fwd.so: $nend endbr64" @@ -115,16 +112,12 @@ if [ -f "$DIR/gl-fwd.so" ] && [ "$ARCH" = x86_64 ]; then # # An earlier version of this check refused a build with no endbr64, on the # grounds that endbr64 is what -fcf-protection=full actually delivers. - # ⚠ THAT CHECK COULD NEVER HAVE FAILED. Measured: a default x86-64 - # gl-fwd.so has 3478 endbr64 and the same object built with the flag - # removed has 3472. The flag accounts for six of them. The other 3472 are - # the trampolines' own, spelled as literal bytes in gl-fwd.c so the - # floor's assembler cannot be too old for them, and no compiler flag - # removes those. - # - # So a count over zero says nothing about whether the flag arrived, and a - # guard that cannot fail is worse than no guard. The number is printed and - # the manifest records the variant; docs/report/09-the-second-boundary.md 9.13 has both figures. + # ⚠ THAT CHECK COULD NEVER HAVE FAILED. Measured: the trampolines carry + # 3472 of their own, spelled as literal bytes no compiler flag removes, + # and the flag arm added six more for 3478. So a count over zero says + # nothing about whether the flag arrived, and a guard that cannot fail is + # worse than no guard. The number is printed, the manifest records the + # variant, and E101 now asserts the count the default build owes. if command -v readelf >/dev/null 2>&1 && readelf -n "$DIR/gl-fwd.so" 2>/dev/null | grep -qi 'propert'; then say "gl-fwd.so: IBT property note present" diff --git a/scripts/verify-gates.sh b/scripts/verify-gates.sh index df50df8..3c6907a 100644 --- a/scripts/verify-gates.sh +++ b/scripts/verify-gates.sh @@ -93,7 +93,7 @@ g_attrib() { # section. Kept identical to the gates.yml step, including that. g_onehome() { f=0 - for n in 3470 358 53/53 50/50 45/45 40/40 26/26; do + for n in 3470 358 64/64 60/60 45/45 40/40 26/26; do c=$(git grep -lF "$n" -- '*.md' ':(exclude)docs/history/*' | sed 's|^docs/report/.*|docs/report/|' | sort -u | wc -l) [ "$c" -gt 1 ] && f=1 @@ -148,7 +148,7 @@ check "no tool is credited (tree)" g_attrib docs/_gate_probe.md \ check " the same, as a generated-with line" g_attrib docs/_gate_probe.md \ "Generated with [$TOOLNAME Code](https://example.invalid)" check "every headline number has one home" g_onehome docs/_gate_probe.md \ - 'the suite reports 53/53 predictions held' + 'the suite reports 64/64 predictions held' # ⛔ THIS CHECK EXISTS BECAUSE THE RATCHET DID NOT REFUSE. It was written as a # budget with a hardcoded number and a printed suggestion that the next reader diff --git a/src/Makefile b/src/Makefile index 5317157..f6503c0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -33,21 +33,22 @@ SOFLAGS = -shared -fPIC # has never parsed. EXTRA_CFLAGS ?= -# CET is an x86 feature. On aarch64 gcc, -fcf-protection=full is not a warning -# but a hard error, "not supported for this target", so both aarch64 builds -# failed at the first shim before anything else could be measured. +# CET. ⛔ The build does not ask for it, and that is a measurement rather than +# an omission (docs/report/09-the-second-boundary.md 9.13): the flag adds six +# endbr64 to the shims and cannot produce the IBT property note, because +# glibc's crti.o carries no property on any floor image and the linker ANDs +# that absence across the link. Without the note a CET-enforcing loader turns +# indirect-branch tracking off for the whole process, so the instructions the +# flag emits are ones no loader will honour. E101 in +# experiments/30-run-tests.sh is the case that keeps this true. # -# ⚠ Selected by what the COMPILER TARGETS, not by uname: the aarch64 artefacts -# are CROSS-compiled, so the machine running make is x86-64 while the object -# being produced is not. -dumpmachine is the only one of the two that knows. +# It stays askable, for a toolchain whose crti.o does carry the property: # -# ⚠ Asked, not assumed: a toolchain built without CET refuses the flag on x86 -# too, and that reaches the compiler as a hard error. Issue #13. -CC_TARGET := $(shell $(CC) -dumpmachine 2>/dev/null) -CET_OK := $(shell printf 'int main(void){return 0;}' > .cet-probe.c 2>/dev/null && \ - $(CC) -fcf-protection=full -c .cet-probe.c -o .cet-probe.o >/dev/null 2>&1 && \ - echo yes; rm -f .cet-probe.c .cet-probe.o) -CET_CFLAGS := $(if $(filter x86_64% i386% i686% amd64%,$(CC_TARGET)),$(if $(CET_OK),-fcf-protection=full,),) +# make CET_CFLAGS=-fcf-protection=full +# +# ⚠ On aarch64 gcc the flag is not a warning but a hard error, "not supported +# for this target", and a toolchain built without CET refuses it on x86 too. +CET_CFLAGS ?= ROOT = .. FLOOR ?= $(ROOT)/inventories/appdir.json @@ -80,10 +81,11 @@ GLES ?= $(ROOT)/.tmp/gtk4x/AppDir/lib all: cross-libc-dlopen.so gl-fwd.so egl-fwd.so gles-fwd.so runtime-select -# The packager's variant, without the build script. docs/building.md says what -# the two flags do and why the CET one is dropped. +# The packager's variant, without the build script: the strict environment, +# -DCLD_STRICT_ENV. It differs from the default by that flag and nothing +# else; docs/building.md has the argument. portable: - $(MAKE) EXTRA_CFLAGS='-DCLD_STRICT_ENV $(EXTRA_CFLAGS)' CET_CFLAGS= all + $(MAKE) EXTRA_CFLAGS='-DCLD_STRICT_ENV $(EXTRA_CFLAGS)' all # The preload carries the shim: it is already in the global lookup scope, so # its exports satisfy undefined symbols in anything dlopen'd later (E2, E5).