Skip to content

Commit 8672403

Browse files
authored
ci: Android shares this implementation, and CI says so (#26)
* ci: Android shares this implementation, and CI says so mcpp 2026.9.11.3 makes `aarch64-linux-android` and `x86_64-linux-android` real target rows, and the question that raises for this package has a short answer: nothing here changes. This file is written on the Linux kernel's own system-call interface and borrows nothing from any C library. Android's kernel IS Linux, the per-architecture system-call ABI is the same, and `src/sys.h` branches on `__x86_64__` / `__aarch64__` -- the architecture, not the operating system. A claim about what does not change is exactly the kind that rots unmeasured, so one step now asserts it. THE SECOND HALF IS THE INTERESTING ONE. Compiling is the weaker statement; the property this package exists for is that its objects name no C library symbol, and on Android the C library is a DIFFERENT one. A reference that resolved to glibc by habit would appear here as a bionic name, so the same permitted set is applied to the same kind of output for another libc. The architecture of the objects is asserted too rather than assumed from `--target`, because one of the two is not the runner's. TWO NAMES THE NATIVE SET DOES NOT HAVE, and measurement decides them: `__emutls_get_address` and `__aarch64_swp4_acq` are both defined in the NDK's `libclang_rt.builtins-<arch>-android.a` and in NEITHER bionic `libc.so`. They belong to the category the set already admits for memcpy and its three neighbours -- compiler-emitted, computing rather than calling, and incapable of re-entering this implementation. The emulated-TLS one is not mcpp's choice: `build.ninja` for this target carries no `-femulated-tls`, and the NDK's clang emits the reference by itself from a bare `thread_local int x;` at API 21. They are permitted in this step only; widening the native step's set for a target it was not measured on is how a check stops being one. One matrix leg, because the axis does not reach this target: the Android rows carry a capability pin, so mcpp refuses any other toolchain for them and `matrix.toolchain` selects nothing here. NOT EXECUTION, and the step says so. Running an Android artifact needs a device or an emulator. Measured by hand on an API 24 x86_64 image: a program written against openkal alone -- no C library, no `import std` -- printed `openkal: 1-2-3` with exit 0. The device ABI has no execution path from an x86_64 runner at all, since Google's emulator refuses a foreign guest. Verified in both directions by extracting the step and running it locally: it passes on both ABIs, and with a `puts` call added it reports "references a symbol it must not: puts" for each and exits 1. * ci: bump the engine pin with the step that needs it The Android step ran against mcpp 2026.8.27.1 -- this repository's pinned `MCPP_VERSION` -- where `aarch64-linux-android` and `x86_64-linux-android` do not exist as target rows at all, so it failed with exit 2. The rows become real in 2026.9.11.3, which is now tagged. This is the CI pin, which is what this repository is TESTED against, and not a floor: nothing here records a minimum engine for consumers, and moving a floor would make a client stopped at the old one unable to use this package at all. The job stays red until the index carries 2026.9.11.3 -- `xlings install mcpp@$MCPP_VERSION` can only resolve what the published index has, and the release's own ecosystem job is what puts it there. That ordering is the dependency this PR already records, now expressed in the file rather than only in prose.
1 parent 34f7668 commit 8672403

1 file changed

Lines changed: 117 additions & 1 deletion

File tree

.github/workflows/ci.yml

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,13 @@ on:
3030
default: ""
3131
env:
3232
MCPP_SOURCE_REF: ${{ github.event.inputs.mcpp_ref || vars.MCPP_SOURCE_REF }}
33-
MCPP_VERSION: 2026.8.27.1
33+
# BUMPED WITH THE ANDROID STEP, BECAUSE THAT STEP NEEDS THIS ENGINE.
34+
# `aarch64-linux-android` and `x86_64-linux-android` become real target
35+
# rows in 2026.9.11.3; on 2026.8.27.1 they do not exist and the step
36+
# fails with exit 2. This is the CI pin -- what this repository is
37+
# tested against -- and not a floor: nothing here records a minimum
38+
# engine for consumers.
39+
MCPP_VERSION: 2026.9.11.3
3440
XLINGS_VERSION: v2026.8.17.2
3541
XLINGS_NON_INTERACTIVE: '1'
3642

@@ -303,6 +309,116 @@ jobs:
303309
test "$bad" -eq 0
304310
echo "the implementation references no C library symbol"
305311
312+
# ANDROID SHARES THIS IMPLEMENTATION, AND THIS IS WHAT SAYS SO.
313+
#
314+
# This file is written on the Linux kernel's own system-call interface and
315+
# borrows nothing from any C library. Android's kernel IS Linux, the
316+
# per-architecture system-call ABI is the same, and `src/sys.h` branches
317+
# on `__x86_64__` / `__aarch64__` -- the architecture, not the operating
318+
# system. So the claim is that nothing here needs to change, and a claim
319+
# about what does not change is exactly the kind that rots unmeasured.
320+
#
321+
# THE SECOND HALF IS THE INTERESTING ONE. Compiling is the weaker
322+
# statement; the property this package exists for is that its objects
323+
# name no C library symbol, and on Android the C library is a DIFFERENT
324+
# one. A reference that resolved to glibc by habit would show up here as
325+
# a bionic name, so the same permitted set is applied to the same kind of
326+
# output for a different libc.
327+
#
328+
# ONE MATRIX LEG, AND THE AXIS DOES NOT REACH THIS TARGET. The Android
329+
# rows carry a CAPABILITY pin (`android-ndk@...`), which means mcpp
330+
# refuses any other toolchain for them -- so `matrix.toolchain` selects
331+
# nothing here and running both legs would download the NDK twice for one
332+
# signal.
333+
#
334+
# NOT EXECUTION. Running an Android artifact needs a device or an
335+
# emulator: measured by hand on an API 24 x86_64 image, where a program
336+
# written against openkal alone printed `openkal: 1-2-3` with exit 0, and
337+
# the device ABI has no execution path from an x86_64 runner at all
338+
# (Google's emulator refuses a foreign guest). This job asserts the two
339+
# things a runner can.
340+
- name: Android shares this implementation, and its objects still name no C library
341+
if: matrix.toolchain == 'llvm@22.1.8'
342+
run: |
343+
permitted='^(memcpy|memmove|memset|memcmp|__libc_start_main|main|_GLOBAL_OFFSET_TABLE_|kal_[a-z_]+|__init_array_start|__init_array_end|__preinit_array_start|__preinit_array_end|_ZN3okl.*)$'
344+
weak_permitted='^environ$'
345+
346+
# TWO NAMES THE NATIVE SET DOES NOT HAVE, AND THEY ARE NOT A C
347+
# LIBRARY'S.
348+
#
349+
# Measured: both are defined in the NDK's own
350+
# `libclang_rt.builtins-<arch>-android.a` and in NEITHER bionic
351+
# `libc.so` -- so they belong to the same category the set above
352+
# already admits for memcpy and its three neighbours: emitted by the
353+
# compiler, computing rather than calling, and incapable of
354+
# re-entering this implementation.
355+
#
356+
# __emutls_get_address emulated thread-local storage. NOT mcpp's
357+
# choice: `build.ninja` for this target
358+
# carries no `-femulated-tls`, and the NDK's
359+
# clang emits this reference by itself from
360+
# a bare `thread_local int x;` at API 21,
361+
# which is its documented default below the
362+
# level where bionic gained ELF TLS.
363+
# __aarch64_swp4_acq an outline-atomics helper, aarch64 only,
364+
# which is why it appears on one ABI and not
365+
# the other.
366+
#
367+
# Kept in this step rather than widened into the native one: nothing
368+
# about the native build has been measured to need them, and a
369+
# permitted set that grows for a target it was not measured on is how
370+
# a check stops being one.
371+
permitted_compiler_rt='^(__emutls_get_address|__aarch64_[a-z0-9_]+)$'
372+
rm -f "$RUNNER_TEMP/android.bad"
373+
374+
for target in aarch64-linux-android x86_64-linux-android; do
375+
echo "== $target =="
376+
rm -rf target
377+
mcpp build --target "$target" --features standalone
378+
379+
objs=$(find target -path '*/obj/*' -name '*.o' ! -name '*.m.o' ! -name 'conformance*')
380+
test -n "$objs" || {
381+
echo "::error::no objects were found for $target; the check would pass vacuously" >&2
382+
exit 1
383+
}
384+
385+
# The objects are for the guest architecture, and one of the two is
386+
# not this runner's -- so assert the architecture rather than trust
387+
# that `--target` was honoured. `file` names it for both.
388+
case "$target" in
389+
aarch64-*) want='ARM aarch64' ;;
390+
x86_64-*) want='x86-64' ;;
391+
esac
392+
one=$(printf '%s\n' $objs | head -1)
393+
file "$one" | grep -q "$want" || {
394+
echo "::error::$one is not $want: $(file "$one")" >&2
395+
exit 1
396+
}
397+
398+
nm --undefined-only $objs | awk '{ print $1, $2 }' | sort -u |
399+
while read -r kind name; do
400+
[ -n "$name" ] || continue
401+
printf '%s\n' "$name" | grep -qE "$permitted" && continue
402+
printf '%s\n' "$name" | grep -qE "$permitted_compiler_rt" && continue
403+
if printf '%s\n' "$name" | grep -qE "$weak_permitted"; then
404+
case "$kind" in
405+
w|v) continue ;;
406+
*) echo "::error::$name is permitted only as a weak reference, and this one is '$kind'" >&2 ;;
407+
esac
408+
else
409+
echo "::error::$target references a symbol it must not: $name" >&2
410+
fi
411+
echo bad >> "$RUNNER_TEMP/android.bad"
412+
done
413+
done
414+
415+
if [ -s "$RUNNER_TEMP/android.bad" ]; then
416+
rm -f "$RUNNER_TEMP/android.bad"
417+
exit 1
418+
fi
419+
rm -f "$RUNNER_TEMP/android.bad"
420+
echo "both Android ABIs build from this implementation unchanged, and name no C library symbol"
421+
306422
# A checker is only useful if it fails when it should.
307423
- name: The independence check detects a dependence
308424
run: |

0 commit comments

Comments
 (0)