Skip to content

build: CI owns the BPF objects — state the contract, make taking them cheap (#117) - #150

Merged
dpsoft merged 1 commit into
mainfrom
fix/ci-owns-bpf-objects
Sep 17, 2026
Merged

dpsoft merged 1 commit into
mainfrom
fix/ci-owns-bpf-objects

Conversation

@dpsoft

@dpsoft dpsoft commented Sep 17, 2026

Copy link
Copy Markdown
Owner

#117 has been approached as "make two machines produce identical bytes". That is fighting a compiler bug with pinning, and every lever has now been measured and eliminated.

Why the bytes cannot be pinned

Clang assigns BTF type IDs to forward declarations by iterating a pointer-keyed std::map in BTFDebug::endModule, so they follow malloc addresses — which depend on the build directory string. Same source, same clang, different directory, different bytes.

Eliminated by measurement, per the issue and one new check here:

lever result
-fdebug-compilation-dir / -ffile-prefix-map no — changes what is stored, not what was allocated; 5 objects differ either way
pin the build path (CI_BUILD_PATH) works, but it is a magic constant mirroring actions/checkout's layout; a repo rename breaks it silently
make bpf2go pass a relative path not possiblecilium/ebpf@v0.21.0/cmd/bpf2go/main.go:192 does filepath.Abs(args[1]) unconditionally
clang / llvm-strip version no — changes size, not this

What #87 actually asked for

Trust: CI had never built from the committed objects, and go generate silently replaced them. Byte-identity was the mechanism chosen to enforce that, not the requirement. So this states the contract rather than chasing the bytes.

Changes

  • CONTRIBUTING.md told contributors to regenerate with make generate for BPF-touching changes — the exact instruction that produces uncommittable bytes. It now documents the contract and the round trip, and names the two things that have cost real time: committing a local regeneration, and hand-reverting the objects, which waives the check per file and is how this hid for four rounds.
  • make adopt-ci-objects needs no RUN=. It finds the failed run for the current branch, and refuses with an explanation rather than a usage line when there is none. Verified: on a branch with no failed run it prints why and what to do instead.
  • The guard and CI verify messages lead with adopting, and explain that same-size-differing-bytes is BTF ordering rather than a code change.
  • generate-container is demoted from source-of-truth to preview — it reproduces CI for the command lines committed today, so a match is encouraging and a mismatch inconclusive.

This does not fix #117

Stated plainly in the commit and left open. The real fixes are upstream (FixupDerivedTypes keyed by something stable — still pointer-keyed on LLVM main) or normalising .BTF type ordering before committing, which would make the object a function of the source. Both are recorded on the issue.

No code changes; 36 packages still pass, both YAML files parse.

https://claude.ai/code/session_01P5889hA6CrX8ysnQkvv6im

#117 has been treated as "make two machines produce identical bytes".
That is fighting a compiler bug with pinning. Clang assigns BTF type IDs
to forward declarations from a pointer-keyed std::map, so they follow
malloc addresses and therefore the build directory string; no flag
removes it (-fdebug-compilation-dir was measured and does not), and
bpf2go absolutizes the source path unconditionally, so the path cannot
be made relative without patching it.

What #87 actually established was trust: CI had never built from the
committed objects and go generate silently replaced them. Byte-identity
was the mechanism chosen to enforce that, not the requirement. So state
the contract instead of chasing the bytes -- the committed objects are
CI's artifacts, and a local regeneration differing is expected.

CONTRIBUTING.md said to regenerate with `make generate` for BPF-touching
changes, which is the instruction that produces uncommittable bytes. It
now describes the round trip, and names the two things that have cost
real time here: committing a local regeneration, and hand-reverting the
changed objects, which waives the check per file and is how this hid for
four rounds.

adopt-ci-objects no longer needs RUN=: it finds the failed run for the
current branch, and refuses with an explanation rather than a usage line
when there is none. The guard and CI messages lead with it, and
generate-container is demoted from source-of-truth to preview -- it
reproduces CI for the command lines committed today, so a match is
encouraging and a mismatch inconclusive.

This does not fix #117. The bug is upstream in BTFDebug::endModule, and
the real local fix would be normalising .BTF ordering before committing.
Both are recorded there.

Claude-Session: https://claude.ai/code/session_01P5889hA6CrX8ysnQkvv6im
@dpsoft
dpsoft merged commit d43e7e7 into main Sep 17, 2026
12 checks passed
@dpsoft
dpsoft deleted the fix/ci-owns-bpf-objects branch September 17, 2026 15:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BPF objects are not byte-reproducible across machines that agree on every visible input

1 participant