Skip to content

v2.0#3

Closed
ebrocas wants to merge 62 commits into
mainfrom
dev
Closed

v2.0#3
ebrocas wants to merge 62 commits into
mainfrom
dev

Conversation

@ebrocas

@ebrocas ebrocas commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

No description provided.

ebrocas and others added 30 commits June 16, 2026 12:18
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
ebrocas added 28 commits June 16, 2026 12:18
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
Signed-off-by: Eloïse Brocas <ebrocas@quarkslab.com>
- map() now returns bool so the CLI reports success/failure correctly
- map() runs the right phase per loop: index_function, then
  index_decompiled, then index_call_graph (all three previously called
  index_function)
- fix log-prefix formatting that applied :0x to a list instead of the addr
- record the binary as a class node and set self.bin.id so functions get a
  valid parent_id (mirrors InterImageCGMapper.record_binary_in_db)
- index_call_graph reads source_calls_loc by callee address (the key used
  at population) instead of the caller address
- source_calls_loc is a defaultdict(list) so per-callee appends no longer
  raise KeyError
- fix GhidraDecompilMapper docstring (was 'IDA Pro')
Add decomp_objects.py with three pydantic models projecting the decomp
mapper's transient structures into a JSON-serialisable form:
- ExportedLocation: serialisable mirror of Location
- ExportedFunction: serialisable mirror of FuncData, embedding Symbol and
  exposing id/name/demangled_name/addr as delegating properties
- ExportedDecompilation: single-binary container keyed by parser-space
  address, with int-key serialiser/validator, write/from_json_export and a
  model_dump_json override, mirroring the FileSystem export style

Wire it in:
- DecompilMapper.to_export() builds an ExportedDecompilation from a run
- the decomp CLI command gains -e/--export, writing <db>.json when set
- export the new classes from pyrrha_mapper.mappers
- tests/test_decomp_objects.py: unit tests for ExportedLocation,
  ExportedFunction and ExportedDecompilation with hand-built fixtures
  (property delegation, is_func validation, from_func_data/from_mapper
  conversions, python vs json dump parity, int-key round-trip including the
  nested source_calls_loc, write/from_json_export round-trip and malformed
  -dump error paths). 100% line coverage of decomp_objects.
- tests/test_cli.py: TestDecompMapper runs the decomp subcommand via a
  subprocess (Ghidra JVM) on every executable of the test firmware,
  asserting the JSON export exists, loads as ExportedDecompilation, keys
  functions by address and carries decompiled source.
The decomp command's match block assigned both IdaDecompilMapper and
GhidraDecompilMapper to one variable, which mypy rejected as incompatible
types. Annotate the variable with the common DecompilMapper base class.
- exe-decomp mapper page: document the new -e/--export option and the
  ExportedDecompilation / ExportedFunction / ExportedLocation structure,
  with a loadable post-processing example (also drop a duplicated help line)
- CHANGELOG: add a curated Unreleased section covering all changes on dev
  since v1.0.1 (single backend value, qbinary/quokka removal, SONAME
  support, decomp rework + JSON export, mapper fixes, repo reorganisation
  and CI), folding in this change set's decomp export feature and fixes
- test_decomp_objects: unit-test job for the export model, mirroring
  test_data_structures on a plain python image with a 100% coverage gate on
  pyrrha_mapper.mappers.decomp_objects
- test_decomp: functional job extending test_fs-cg, running
  tests/test_cli.py::TestDecompMapper across the ida/ghidra backend matrix
  with coverage over decomp_mapper and decomp_objects
func_decompiled called get_pseudocode(func, remove_tags=True), but in
ida_domain 0.5.0 get_pseudocode(func) takes no remove_tags argument and
returns a PseudocodeFunction (not a list of lines), so every decomp run
crashed with TypeError once it reached a function body. Call
get_pseudocode(func).to_text(remove_tags=True) to get the lines, and catch
IdaDomainError (the base of PseudocodeError) instead of RuntimeError, which
did not cover decompilation failures. Drop a dead result dict and a
misleading log line.
The autouse _collect_export_artifacts fixture in conftest reads
export_res.project_path when PYTEST_ARTIFACTS_DIR is set (as it is in CI),
but TestDecompMapper.ExecResults only defined export_path and db_path,
raising AttributeError at teardown. Add project_path, matching
BaseTestFsMapper.ExecResults.
…exing

Imported functions have no decompiled body (their source is set to "" in
index_function) and are never recorded in the DB. Running index_decompiled
and index_call_graph over them produced a 'declaration not found' ERROR and
'missing target id' WARNING per imported function, flooding the logs on any
real binary.

Skip imported functions as source-indexing and call-graph caller targets,
and demote the 'cannot record call' message to DEBUG when the callee is
imported (an expected condition in a single-binary decompilation, not an
error).
The decomp mapper legitimately logs per-function warnings/errors (e.g. a
declaration not located in a particular decompiled body) without failing the
run. Add check_click_result_allow_logs, which asserts exit code 0 and no
exception but does not ban ERROR/WARNING lines (only CRITICAL), and use it in
TestDecompMapper.
pytest 9 deprecates class-scoped fixtures defined as instance methods (they
will be removed in pytest 10): the instance a fixture runs on is not the one
test methods see. Convert the class-scoped fixtures (pyrrha_exec, export_res,
export_dump across the fs, fs-cg and decomp test classes, plus the _host_path
helper they call) to classmethods using cls instead of self. The abstract
export_res stub stays an instance method since it is always overridden and
never run as a fixture.
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