Skip to content

Add an AArch64 ARMv8.1-A LSE and ARMv8.3-A RCpc atomics fixture - #217

Open
zardus wants to merge 1 commit into
masterfrom
feature/lse-atomics
Open

Add an AArch64 ARMv8.1-A LSE and ARMv8.3-A RCpc atomics fixture#217
zardus wants to merge 1 commit into
masterfrom
feature/lse-atomics

Conversation

@zardus

@zardus zardus commented Aug 29, 2026

Copy link
Copy Markdown
Member

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

Problem

Nothing in this repository carries an ARMv8.1-A LSE atomic or an ARMv8.3-A LDAPR, so a lifter change for those encodings has no compiled input to assert on. Scanning every tracked AArch64 ELF -- 17 objects, 274,169 instructions decoded with capstone over each SHF_EXECINSTR section -- finds zero of them, tests/aarch64/libc.so.6 included:

tests/aarch64/libc.so.6                    233007 insns, 0 LSE/RCpc
tests/aarch64/ld-linux-aarch64.so.1         22532 insns, 0 LSE/RCpc
tests/aarch64/libastring-ocaml-astring.cmxs 16562 insns, 0 LSE/RCpc
...                                        274169 insns, 0 LSE/RCpc in total

Root cause

These encodings only appear when a compiler is told the target has them. GCC emits them from ordinary C11 and __atomic_* builtins under -march=armv8.1-a -mno-outline-atomics, and every AArch64 object here predates that or was built for baseline ARMv8.0.

Fix

tests/aarch64/lse_atomics is an 8272-byte static ARMv8.4-A ELF built from the C in tests_src/aarch64_lse_atomics/. Its 861 .text instructions include 97 LSE, RCpc and RCpc2 encodings across 57 distinct mnemonics -- the eight LD<OP> forms, SWP, CAS, CASP, LDAPR{,B,H} and LDAPUR/STLUR -- at both W and X widths, one per named function so a test can reach an encoding by symbol. build.sh pins the toolchain through the workspace's own nixpkgs revision and sets SOURCE_DATE_EPOCH, -ffile-prefix-map and -Wl,--build-id=none; three builds from three different directories produced sha256 7f581abb… byte for byte. strings -a -n 1 over the artifact yields 1058 runs, none containing a path, a user name, a host name, a toolchain version or a date.

Where GCC has no C spelling -- it has no atomic min/max builtin, and it lowers a 128-bit compare-exchange to a libatomic call rather than to CASP -- the encoding comes from inline asm inside an ordinary C function, compiled and assembled by the same cross toolchain. The comments in src/lse_asm.c and src/rcpc2.c say which and why.

Testing

readelf reads the committed file back as ET_EXEC AArch64, e_flags 0x0, entry 0x400e60, six section headers and 99 STT_FUNC symbols; cle.Loader loads it and sees all 99. Re-running build.sh reproduces the committed bytes exactly. The consumer is the pyvex regression in angr/pyvex#579, which lifts every word of .text and asserts that only the ARMv8.4-A RCpc2 group is still refused.

Validation: #217 (comment)

session: sharpen

@zardus

zardus commented Aug 29, 2026

Copy link
Copy Markdown
Member Author

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

Validation record for head 490e4868b3e174679e1b8e23694aab39448f892a against baseline 003e82a2bfa641530924055695b36cec8af483ab.

What the shape scan says. Reading the first 20 bytes of all 1,791 tracked files and keeping ELF objects with e_machine == 183 gives 17 AArch64 objects at a87538bc248531d2a1d22434143e8b9752459bac, the baseline this record was keyed to when the scan ran. Disassembling every SHF_EXECINSTR section of each with capstone 5.0.1 decodes 274,169 instructions and finds 0 ARMv8.1-A LSE, ARMv8.3-A RCpc or ARMv8.4-A RCpc2 encodings -- tests/aarch64/libc.so.6 (233,007 instructions) included. At the head it was keyed to then, f1f8eebd637d141b823b8a73e4184e3b74b3fde7, the same scan over 18 objects finds 97, all in the new file. The detector was proved against 11 hand-built encodings first, so the zero is a measurement rather than a broken scan.

That zero does not carry to the current baseline, and it was re-run rather than assumed. Master has added five AArch64 ELF objects since: tests/aarch64/langdetect_go, langdetect_go_go1.17.13, langdetect_go_go1.18.10, langdetect_go_go1.20.14 and langdetect_go_go1.22.5, all Go builds. Re-scanned the same way at the current baseline 003e82a2bfa641530924055695b36cec8af483ab -- capstone 5.0.1 with skipdata, every SHF_EXECINSTR section of all 22 AArch64 ELF objects, 958,707 instructions -- the count is 1,808, every one of them inside those five Go binaries and none in the 17 objects the original scan covered. The re-scan reproduces the original figures where the two overlap: 0 across those same 17 objects, and exactly 97 in tests/aarch64/lse_atomics. So this fixture is no longer the only object in the repository carrying these encodings -- the five Go binaries carry them incidentally.

Readback of the committed artifact.

ELF64, 2's complement little endian, UNIX - System V, Type EXEC, Machine AArch64
Flags 0x0, entry 0x400e60, 6 section headers (.text .bss .symtab .strtab .shstrtab)
99 STT_FUNC symbols;  8272 bytes
sha256 7f581abbc8f0470333c8f6ace821e64f65e1fb50f705118f112fd31f8309cf70

cle.Loader loads it and reports all 99 function symbols.

Reproducibility. tests_src/aarch64_lse_atomics/build.sh was run three times, each into a differently named directory after rm -rf, under nix shell github:NixOS/nixpkgs/6b5e5b7a6631f065bf6908986990b37d845f847f#pkgsCross.aarch64-multiplatform.buildPackages.gcc -- GCC 15.3.0, binutils 2.46, the same nixpkgs revision this workspace's flake.lock pins. All three produced sha256 7f581abb…; cmp reports the third byte-identical to the committed file.

Leak scan. strings -a -n 1 yields 1058 runs. Case-insensitive counts: /home 0, nix 0, store 0, yans 0, datadriven 0, sharpening 0, gcc 0, commit 0, build-a 0, some-other 0, 2026 0, 2025 0. Two of those greps are real tests rather than formalities: this machine's directory is literally named datadriven-sharpening and its user yans. There is no .comment, no build-id, no note and no debug section.

Encoding coverage of .text, verified with capstone rather than assumed: 861 instructions, 97 of them LSE/RCpc/RCpc2 across 57 distinct mnemonics -- LDADD, LDCLR, LDEOR, LDSET, LDSMAX, LDSMIN, LDUMAX, LDUMIN, SWP, CAS, CASP, LDAPR{,B,H}, LDAPUR{,B,H} and STLUR{,B,H}, at both W and X widths where the instruction has both. LDSMAX/LDSMIN/LDUMAX/LDUMIN, CASP and the LDAPUR forms come from inline asm inside ordinary C functions, because GCC has no atomic min/max builtin, lowers a 128-bit compare-exchange to a libatomic call, and folds an acquire load at an offset into add plus LDAPR; the comments in src/lse_asm.c and src/rcpc2.c say so. Everything else is compiler output from C11 and __atomic_* builtins.

Not run. This repository has no executable test suite and no pre-commit configuration, so neither ran. The consumer's suite is in the pyvex pull request.

Rebased 2026-09-06; this record is re-keyed to the new head. The branch was replayed from 3de2c41a297606c45be7985aa09b3ae615424d61..60f449f76b87db6ed4456097944a99fb50ff25b7 onto binaries master 003e82a2bfa641530924055695b36cec8af483ab, giving 490e4868b3e174679e1b8e23694aab39448f892a: git range-diff marks every commit =, the two git diff <merge-base>...<head> outputs hash to the same object, and every path the branch touches has the same blob at the old and the new head, so master left those paths alone. The figures that read the branch's own files therefore describe the same bytes on a new base. A figure that counts the whole repository is a different matter: master has added files, so each of those is keyed to the revision named beside it. It moved because a pull request that pins this one is checked out by CI at this branch's tip rather than merged with master, so the consumer was building against a binaries tree missing fixtures that were already on master. angr/binaries runs no pull-request workflow, so there is no run at the new head to cite — check-runs and actions/runs report 0 there, as they do for every pull request in this repository.

session: sharpen

tests/aarch64/lse_atomics is a static ARMv8.4-A ELF whose .text carries the
eight LD<OP> forms, SWP, CAS, CASP, LDAPR{,B,H} and LDAPUR/STLUR at both
widths, one encoding per named function. No tracked AArch64 object had any of
them, so a lifter change for these encodings had nothing to assert on.

tests_src/aarch64_lse_atomics/build.sh reproduces the committed bytes from the
C beside it, pinning the cross toolchain to the nixpkgs revision the angr
development workspace uses.
@zardus
zardus force-pushed the feature/lse-atomics branch from 60f449f to 490e486 Compare September 6, 2026 05:42
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.

1 participant