Skip to content

Resolve p-code architectures from arch_from_id - #365

Draft
zardus wants to merge 2 commits into
masterfrom
feature/fix-archinfo-arch-coverage
Draft

Resolve p-code architectures from arch_from_id#365
zardus wants to merge 2 commits into
masterfrom
feature/fix-archinfo-arch-coverage

Conversation

@zardus

@zardus zardus commented Aug 9, 2026

Copy link
Copy Markdown
Member

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

arch_from_id only returns architectures registered in arch_id_map, which ArchPcode cannot join: register_arch builds an architecture from an endness, a p-code architecture from a sleigh language. Every loader that reads an architecture out of a header calls it, so PA-RISC, Xtensa and LoongArch images did not load.

When nothing registered claims the identifier, look it up among the sleigh languages instead — a full language id first, then the processor a language describes — honouring the endness and width the caller passed. Anything no language covers still raises ArchNotFound. The regression resolves the machine names the ELF and PE loaders pass.

Parked on #363: resolving now reaches the 8051, whose one-byte stack pointer needs that fix, so this branch carries its commit; I will rebase and mark it ready once it merges.

Validation: #365 (comment)

ArchPcode derived the initial stack pointer with an expression that assumed a
stack pointer at least 16 bits wide, so an architecture with a narrower one --
the 8051's stack pointer is a single byte -- raised "ValueError: negative shift
count" during construction. Derive the value from the stack pointer's own width
instead; it is unchanged for every wider architecture.

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

zardus commented Aug 9, 2026

Copy link
Copy Markdown
Member Author

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

Validation record for head 08c021f2a2549aca27d94cf9414ca28f12a65f7a against baseline da171ca0dd8eec1e16dd9c4f04576b2c958e2aea. Python 3.12.13, pypcode 4.0.1.dev0, pyelftools 0.33, pefile 2024.8.26.

  • Regression: python -m pytest tests/test_pcode.py -k arch_from_id — 7 tests; on the baseline 4 fail with ArchNotFound raised by arch_from_id, all 7 pass on head
  • Focused: python -m pytest tests — 25 passed, 0 skipped, 0 xfailed
  • Lint/type, per changed file against the merge base: pylint archinfo/arch.py 9.70 -> 9.70, archinfo/arch_pcode.py 10.00 -> 10.00, tests/test_pcode.py 10.00 -> 10.00; pyright badness non-increasing on all three
  • Hooks: pre-commit run --all-files — 24 hooks, no failure, tree unchanged
  • Negative configuration: with _has_pypcode false, standing in for archinfo installed without the pcode extra, arch_from_id answers exactly as it does on the baseline — registered architectures resolve, everything else raises ArchNotFound. Pinned by test_arch_from_id_without_pypcode

Resolver comparison, baseline against head, over 500 identifiers x 7 endness/width hint combinations = 3500 resolutions. The identifiers are every all_arches name, every pyelftools ENUM_E_MACHINE name, every pefile IMAGE_FILE_MACHINE_* name, every sleigh language id and processor name, and the literal strings cle's ELF, PE, COFF, TE and Mach-O backends pass.

  • Unchanged answers: 2431
  • ArchNotFound -> resolved: 1069
  • Answers changed, and new exception types: 0 of each

End to end on tests/hppa/test-instr_hppa from angr/binaries at 58841bf0d9e71ca7b215f404ba61e1924c712906, sha256 6cdf3252a73fc432c250024fe36d6e92cbe916cc79496b82ae6528781b7ab390, through unpatched cle 9.3.3.dev0 and angr 9.3.3.dev0:

  • cle.Loader(path)ELF, pa-risc:BE:32:default, 32 bits, Iend_BE, entry 0x10498; raises ArchNotFound on the baseline
  • angr.Project(path) — selects UberEnginePcode, and factory.block(entry) disassembles _start as 18 PA-RISC instructions

For an ELF machine this can name, it now answers before cle's own opinion-file fallback. Comparing the two paths on synthesized ELF headers covering every ENUM_E_MACHINE value at both classes and both byte orders, this resolves 16 machine/class/byte-order combinations. 8 match what the opinion path picks, 3 have no ELF opinion at all — PA-RISC among them, which is why test-instr_hppa did not load before — and 5 differ:

  • Big-endian Xtensa, big-endian AArch64 ilp32 and little-endian NDS32: the opinion path's first match ignores the byte order in the header, and this returns the language the header asks for.
  • LoongArch at both classes: the opinion file selects the float variant with an e_flags mask that cle's opinion reader does not parse (its own FIXME: Mask parsing) and therefore ignores, so it takes whichever variant enumerates first. Neither path reads those flags. This one breaks the tie on the language id and lands on the d variants, which two of that mask's three defined values select.

Every sleigh language pypcode ships constructs on head — 187 of 187, none raising — so any language this can select is one ArchPcode can build.

Caveats:

  • pypcode has no IA-64, DEC Alpha, VAX, SM83 or 65816 definition and no little-endian SPARC, so those loads still fail with ArchNotFound; nothing in archinfo can fix that.
  • EM_SPARCV9, EM_SPARC32PLUS and EM_68K still do not resolve. Sleigh spells those processors sparc and 68000, and matching them would need suffix stripping loose enough to produce wrong answers elsewhere.
  • EM_BPF reaches sleigh's classic BPF, which is 32-bit, only at 32-bit ELF class; no real eBPF object uses that class, and at 64-bit class it still falls through to the opinion path, which picks eBPF.
  • An identifier a registered architecture claims but the caller's width or byte order excludes also reaches this, so arch_from_id("x86", "le", 64) returns x86:LE:64:default rather than raising. That is the same rule that lets EM_AARCH64 at 32-bit class reach AArch64 ilp32 and EM_RISCV at 32-bit class reach RISCV32, neither of which archinfo has a registered class for.
  • arch_from_id("data") and arch_from_id("toy") now reach Ghidra's placeholder processors. No loader produces those identifiers; they only appear if a caller asks for them by name.
  • Retarget sequence once Support narrow p-code stack pointers #363 merges: rebase feature/fix-archinfo-arch-coverage onto origin/master, which drops the duplicated commit, rerun the focused suite and the lint and type comparison on the rebased head, then mark ready.

@angr-bot

angr-bot commented Aug 9, 2026

Copy link
Copy Markdown
Member

Corpus decompilation diffs can be found at angr/dec-snapshots@master...angr/archinfo_365

arch_from_id could only return one of the fourteen hand-written architecture
classes, because arch_id_map is populated by register_arch, which builds an
architecture from an endness while ArchPcode is built from a sleigh language.
Every loader that names an architecture from a file header goes through
arch_from_id, so the 187 languages pypcode ships were unreachable and loading a
PA-RISC, Xtensa, LoongArch or TriCore image failed outright.

Look the identifier up among the sleigh languages when no registered class
claims it, matching a full language id first and then the processor a language
describes, and honouring the endness and width the caller passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.

2 participants