Skip to content

Add a PA-RISC shared library that exercises p-code conditional branch fall-through - #229

Open
zardus wants to merge 1 commit into
masterfrom
feature/parisc-phoenix
Open

Add a PA-RISC shared library that exercises p-code conditional branch fall-through#229
zardus wants to merge 1 commit into
masterfrom
feature/parisc-phoenix

Conversation

@zardus

@zardus zardus commented Sep 8, 2026

Copy link
Copy Markdown
Member

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

Problem

angr has one PA-RISC test input, tests/hppa/test-instr_hppa, and nothing can be
asserted about the decompiler on it: CFGFast raises KeyError: <Tmp 251392> from
angr/analyses/s_propagator.py:498 before any function is structured, with
data_references=True, with normalize=True, and with no options at all. A
decompiler fix for PA-RISC therefore has no fixture to regress against.

Root cause

That KeyError is a separate defect, already covered by open pull requests in angr.
It is not something this repository can address; what this repository can do is carry
a second PA-RISC object small enough to reach structuring.

Fix

tests/hppa/ruby-bindex-cruby.so, 6,004 bytes:
usr/lib/hppa-linux-gnu/rubygems-integration/3.3.0/extensions/hppa-linux-gnu/3.3.0/bindex-0.8.1/skiptrace/internal/cruby.so
from ruby-bindex 0.8.1+ds-2+b2 for hppa in Debian Ports,
https://deb.debian.org/debian-ports/pool-hppa/main/r/ruby-bindex/ruby-bindex_0.8.1+ds-2+b2_hppa.deb
(package sha256 1c655476754809e14d198858e47ab2eae646acdc50acd1fc523314fae011f943,
member sha256 3973dc9042b57fe47b082d97441837e29483be7a0785f9f412e69543585d481e).
ELF32 big-endian HPPA shared object, stripped. ruby-bindex is Expat-licensed and
its Debian copyright file names Expat for both the upstream sources and the
packaging.

Testing

Loaded with main_opts={"backend": "elf", "arch": archinfo.ArchPcode("pa-risc:BE:32:default")}
it yields 37 functions through CFGFast, and converting every one of its blocks to
AIL produces 61 conditional jumps -- 58 of which are missing their fall-through target
on angr master, and none of which are on the consumer branch.

The consumer is the angr change that needs this fixture; it links here.

Validation: #229 (comment)

session: sharpen

… fall-through

angr's p-code to AIL converter takes a conditional branch's fall-through address
from the BRANCH operation that follows it. The PA-RISC SLEIGH specification emits
its nullification bookkeeping after most conditional branches instead, so usually
no BRANCH follows and the fall-through is never supplied. 58 of this object's 61
conditional branches are in that shape; the other 3 are followed by a BRANCH and
are converted correctly today.

usr/lib/hppa-linux-gnu/rubygems-integration/3.3.0/extensions/hppa-linux-gnu/3.3.0/bindex-0.8.1/skiptrace/internal/cruby.so
from ruby-bindex 0.8.1+ds-2+b2 for hppa, in Debian Ports:

  https://deb.debian.org/debian-ports/pool-hppa/main/r/ruby-bindex/ruby-bindex_0.8.1+ds-2+b2_hppa.deb

The package is sha256 1c655476754809e14d198858e47ab2eae646acdc50acd1fc523314fae011f943
and the member is sha256 3973dc9042b57fe47b082d97441837e29483be7a0785f9f412e69543585d481e.
ruby-bindex is Expat-licensed; its Debian copyright file names Expat for both the
upstream sources and the packaging.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@zardus

zardus commented Sep 8, 2026

Copy link
Copy Markdown
Member Author

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

Validation record for head 136fdfc580c845709e3398c183e3bb953019f5ba against baseline fc07821c89535534979b02760e7e1bfc35faf690.

Provenance, verified by fetching it

curl with no credentials against
https://deb.debian.org/debian-ports/pool-hppa/main/r/ruby-bindex/ruby-bindex_0.8.1+ds-2+b2_hppa.deb
returns HTTP 200 and an 8,836-byte package whose sha256 is
1c655476754809e14d198858e47ab2eae646acdc50acd1fc523314fae011f943. Its
data.tar.xz member
usr/lib/hppa-linux-gnu/rubygems-integration/3.3.0/extensions/hppa-linux-gnu/3.3.0/bindex-0.8.1/skiptrace/internal/cruby.so
is 6,004 bytes with sha256
3973dc9042b57fe47b082d97441837e29483be7a0785f9f412e69543585d481e, which is the
blob this commit adds, byte for byte. Re-fetched and re-hashed on 2026-09-08.

The package's usr/share/doc/ruby-bindex/copyright gives License: Expat for
Files: * and for Files: debian/*, and carries the Expat text once. Expat is
the only licence named in the file.

What it is

Read out of the committed blob: ELFCLASS32, ELFDATA2MSB, e_type 3
(ET_DYN), e_machine 15 (EM_PARISC), 6,004 bytes, no .symtab and a
.gnu_debuglink, so stripped. Nothing in it names a build path: no /home/, no
nix store path.

It is the second PA-RISC object in this repository. At the baseline fc07821,
855 of the 1,851 tracked files begin with \x7fELF, and reading e_machine out
of every one of them -- rather than matching filenames -- finds exactly one
EM_PARISC: tests/hppa/test-instr_hppa.

Why the existing PA-RISC object could not be used

Loaded with main_opts={"backend": "elf", "arch": archinfo.ArchPcode("pa-risc:BE:32:default")},
tests/hppa/test-instr_hppa raises KeyError: <Tmp 251392> from
angr/analyses/s_propagator.py:498 inside CFGFast, before any function is
structured. Re-run on 2026-09-08 against the consumer branch's build: the same
KeyError from the same line with data_references=True, with normalize=True,
and with no options at all, so nothing downstream of the CFG can be asserted on
it. That KeyError is a separate defect with open fixes of its own in angr.

What the new object supports

CFGFast(normalize=True) recovers 37 functions covering 114 distinct blocks.
Those blocks lift to 61 p-code CBRANCH operations, and the operation that
immediately follows each one is COPY 46 times, INT_ADD 12 times and BRANCH
3 times. So 58 of the 61 have no BRANCH after them, which is the shape the
consumer change is about; the other 3 do have one, and those 3 already convert
correctly
. Nothing here claims the object has no BRANCH after a conditional
branch -- an earlier version of this commit's message did, and it was wrong.

Converting each of the 114 blocks once produces 61 conditional jumps. On the
consumer's baseline 23b470d9f 58 of them are missing their fall-through target
and 0 are missing their true target; on the consumer's head 1461b1bff none is
missing either. Those 58 are the 58 with no BRANCH after them, checked block by
block rather than by matching the totals: in all 114 blocks the count of
CBRANCHes with no BRANCH next equals the count of conditional jumps with no
false target, so no block differs. Counted on 2026-09-08 from the committed blob,
in the two store environments the consumer's record names.

Repository checks

The workspace gate ran on 2026-09-08 (08:57:52Z to 09:52:56Z) with this commit's
tree in the feature's binaries checkout and its test-inputs suite passed: 1 checkout(s) add no binary or manufactured input outside angr/binaries. That
suite deliberately says nothing about this repository's own commit -- it printed
skipped features/parisc-phoenix/repos/binaries: angr/binaries, where a fixture belongs -- so what it checked is the consumer, which adds no fixture of its own.

That gate ran while this work sat on commit ffd41914e, which was amended to
136fdfc58 afterwards to correct the false claim named above. The amend touched
the message only: both commits carry tree
9cba157c0de6a44d37f387e58a63e644f50f311b on parent fc07821, git diff ffd41914e 136fdfc58 is empty, and the fixture blob is sha256 3973dc9042b5… in
both. So the gate read exactly the bytes this commit adds.

session: sharpen

@zardus

zardus commented Sep 8, 2026

Copy link
Copy Markdown
Member Author

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

What this fixture shows, before and after the angr change that consumes it
(angr/angr#7124). Both arms load tests/hppa/ruby-bindex-cruby.so with
main_opts={"backend": "elf", "arch": archinfo.ArchPcode("pa-risc:BE:32:default")},
run CFGFast(normalize=True), and convert every block of every function to AIL with
ailment.IRSBConverter.convert(proj.factory.block(addr).vex, manager) -- the same
enumeration the consumer's test uses.

Before -- on angr master, 58 of the object's 61 conditional jumps reach the
decompiler naming only one of their two targets:

angr master
{"functions": 37, "blocks": 114, "conditional_jumps": 61,
 "missing_any": 58, "missing_true": 0, "missing_false": 58,
 "block_conversion_errors": 0}

Every one of the 58 is missing the fall-through target; none is missing the true
target. The other 3 are followed by a p-code BRANCH, which back-patches the
fall-through, and they convert correctly today.

After -- with angr/angr#7124, none is missing either target:

with angr/angr#7124
{"functions": 37, "blocks": 114, "conditional_jumps": 61,
 "missing_any": 0, "missing_true": 0, "missing_false": 0,
 "block_conversion_errors": 0}

The function, block and conditional-jump counts are identical in both arms; only the
missing-target counts move.

session: sharpen

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