Cross-DEX method calls not executed by VM
- Dominant language
- Python
- Stars
- 4
- Forks
- 3
- Avg merge
- 3d 4h
- Merged PRs (30d)
- 2
Description
## Summary
The Dalvik VM is bound to a single DEX, so a method whose bytecode lives in a different DEX of a multi-dex APK is never executed. When a method in `classes.dex` invokes one defined in `classes2.dex`, the direct/static dispatch lookup misses the single-DEX method map and the call is silently downgraded to an external API miss (stub or no-op). `dextrace run ` therefore only produces correct results when every method actually executed happens to live in the one DEX that was loaded.
## Root cause
```python
# src/dextrace/vm/engine.py (_do_invoke, direct/static path)
callee_code_off = self._sig_to_codeoff.get(callee_sig)
if callee_code_off is None:
self._handle_external_miss(callee_sig, insn, state)
return None
```
`self._sig_to_codeoff`, `self._parser`, `self._disasm`, `self._resolver`, and `self._hierarchy` are all built from a single `dex_bytes`. A `code_off` is a byte offset valid only within its own DEX, so the engine has no way to reach a callee defined in another DEX — the lookup misses and real bytecode is skipped.
## Fix
Make the VM module-aware: build one `DexModule` (parser, resolver, disasm, local method map) per DEX, resolve direct/static calls through a global map, and carry the current module through the execution loop and `CallFrame`.
```python
# global dispatch map: sig -> (module_id, code_off), stable order (classes.dex first)
location = self._sig_to_location.get(callee_sig)
if location is None:
self._handle_external_miss(callee_sig, insn, state)
return None
callee_module_id, callee_code_off = location
# switch active module on invoke; restore caller's module on return
```
Cross-DEX virtual dispatch and inheritance additionally require merging `ClassHierarchy` across all DEXes (vtable values become `(module_id, code_off)`) — a separable follow-up.
## Affected locations
| File | Line(s) | Context |
|------|---------|---------|
| `src/dextrace/vm/engine.py` | 137-162 | Per-DEX state built from single `dex_bytes` |
| `src/dextrace/vm/engine.py` | 708-711 | Direct/static dispatch misses cross-DEX callee |
| `src/dextrace/vm/engine.py` | 692-694 | Virtual dispatch via single-DEX `ClassHierarchy` |
| `src/dextrace/vm/class_hierarchy.py` | 71-90 | Vtables built from one DEX; values are bare `code_off` |
| `src/dextrace/cli/cmd_run.py` | 137-139 | `dextrace run` loads only the entry DEX |
Contributor guide
Assessment
This issue has not been assessed yet.