PE: State little-endian for the PowerPC machine types - #805
Conversation
|
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS Every machine type in side = "cle states the endness" if hasattr(pe, "arch_from_machine_type") else "cle master"
resolve = getattr(pe, "arch_from_machine_type", None) or (lambda ident: archinfo.arch_from_id(ident))Both sides ran against archinfo master Before -- on cle master cle masterAfter -- the two PowerPC machine types are little-endian, and every other machine-type row is byte-identical: with this changeThe whole |
|
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS Validation record for head
Caveats: the gate was run at cle scope -- this repository's full suite, its full hook set over every file, and the merge-base lint and type comparison. The angr, pyvex, claripy, archinfo and GUI suites were not run: the change is two call sites in one loader plus a table test, and it removes no API. The Windows, macOS and Pyodide legs run on hosted CI only. Running pylint without the branch on |
|
Corpus decompilation diffs can be found at angr/dec-snapshots@master...angr/cle_805 |
Windows NT on PowerPC was little-endian. Both PE call sites that turn a file header's machine type into an Arch called archinfo.arch_from_id with no endness, so IMAGE_FILE_MACHINE_POWERPC (0x1f0) and IMAGE_FILE_MACHINE_POWERPCFP (0x1f1) came back with whatever ArchPPC32 defaults to. angr/archinfo#380 made that default big-endian, so cle reads a Windows NT PowerPC PE with its bytes the wrong way round today. Route both sites through one helper that passes Endness.LE for those two machine types and changes nothing else. Of the 35 machine types pefile names, plus three numbers it does not, 36 resolve to exactly the same Arch as before; the two that change are 0x1f0 and 0x1f1, from big-endian to little-endian. angr/binaries has no PowerPC PE: of the 1,834 blobs tracked at 3de2c41a, 106 carry a valid PE signature and their machine types are 0x8664 (54), 0x14c (44), 0xaa64 (6), 0x1c4 and 0x5064. The test therefore asserts the machine type mapping directly instead of loading a fixture. Take the endness argument back out and it fails, because archinfo answers big-endian. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ba1ac04 to
03e94c8
Compare
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS
Problem
cle attaches the wrong byte order to a Windows NT PowerPC PE.
cle/backends/pe/pe.pyturnsFILE_HEADER.Machineinto an architecture at two sites -- theself._arch is Nonebranch ofPE.__init__andPE.check_compatibility-- and both callarchinfo.arch_from_idwith noendnessargument. Windows NT on PowerPC was little-endian, soIMAGE_FILE_MACHINE_POWERPC(0x1f0) andIMAGE_FILE_MACHINE_POWERPCFP(0x1f1) have to resolve little-endian. Against archinfo master384c676they resolve big-endian:pefileparses the headers little-endian whatever the machine type, so the parse itself survives. What does not is everything that reads a word through the object'sArch:Clemory.unpack_word, the TLS andgopclntabreaders, and the lifting and CFG built on top.angr/archinfo#380 made this true on 2026-09-01, by changing
ArchPPC32.default_endnessfromIend_LEtoIend_BE. That pull request named this repair and said it "belongs in cle, as a separate change". An earlier version of this description said nothing about a PowerPC PE was observably wrong; that was true before angr/archinfo#380 and is not true now.Root cause
The registration a PowerPC machine type falls through to is
register_arch([r".*p\w*pc.*"], 32, Endness.ANY, ArchPPC32), and for an entry whose endness isANYa call that names none is answered withcls(cls.default_endness). The identifier string carries no byte order, so the endness of a PowerPC PE is decided by an architecture's default rather than by the container format that knows the answer.Fix
Both sites go through one module-level helper in
cle/backends/pe/pe.pythat passesEndness.LEfor those two machine types andEndness.ANY--arch_from_id's own default -- for everything else. Each site keeps the lookup it had:PE.__init__still falls back tohex(machine_type)for a machinepefiledoes not name, andcheck_compatibilitystill indexes the table directly. Of the 35 machine typespefilenames, plus three numbers it does not, 36 resolve to exactly the same architecture as on master; the two that move are 0x1f0 and 0x1f1, from big-endian to little-endian. The full before and after tables are in the output comment. archinfo's PowerPC default is left alone on purpose: angr/archinfo#380 set it deliberately, and the byte order of a PE is the container's to state.Testing
TestPEMachineTypesintests/test_pe.pyasserts that both PowerPC machine types resolve to a 32-bit little-endianPPC32. Take theendnessargument back out of the helper and it fails --AssertionError: assert <Endness.BE: 'Iend_BE'> == <Endness.LE: 'Iend_LE'>. It asserts the mapping instead of loading a PE becauseangr/binarieshas no PowerPC one: of the 1,834 blobs tracked at3de2c41a, 106 carry a validPE\0\0signature, and their machine types are 0x8664, 0x14c, 0xaa64, 0x1c4 and 0x5064.#757 rewrites the same two call sites for an unrelated reason, so the two conflict; whichever merges second needs a rebase.
Validation: #805 (comment)
session: sharpen