Inline P-code temporaries as expressions, compute everything from the
pre-instruction state and emit register/memory/pc writes in program order.
Conditional branches guard the following pcodes with the negated condition.
Track the register-space location of operands to handle partial register
overlaps, and compare wide values at 64 bits.
* Implement the new Scope::findSymbolBefore/After by asking the cache
* Look up join records through AddrSpace::findJoin
* Iterate main ops now that INDIRECTs live in their own container
* Follow the new size argument of pushUnnamedLocation
* Refresh test expectations for the new decompiler and sleigh output
Capture lifted operations before analysis and preserve their native dead state before clearing analysis. Export eliminated_ops in pdgj and pdgx, then rerun with unused computations preserved for display under r2ghidra.deadcode.
Document that p-code retirement does not prove instructions can be NOPed. Cover real dead records, toggling, full addresses and timeout workers.
Validation: 59 focused regression tests; AddressSanitizer with a forced analysis restart and preserved-code rerun.
Use the upstream Meson dependency, shared source manifest, and Sleigh compilation helper instead of maintaining local copies. Check pinned subprojects before Make and XPS builds, cache fetched revisions for offline reuse, and save local ghidra-native changes in a named stash before updating.
ccode mode called setNoCastPrinting(true) after applying r2ghidra.casts, so
`e r2ghidra.casts=true` was silently overridden and semantically necessary
truncations disappeared. For
long cast_test(long x) { return (long)(int)x + 0x100000000L; }
the output was `arg1 + 0x100000000`, which evaluates to 0xffffffff at
0xffffffff instead of the 0x1ffffffff the binary produces.
Let r2ghidra.casts decide: ccode keeps only the identifier cleanup and the
hidden implied ZEXT/SEXT extensions and no-convention prototype style. With
ccode=true,casts=true the printer now emits
`(int64_t)(int)arg1 + 0x100000000`; with casts=false it stays cast-free as the
option documents, and ccode=false output is unchanged.
Regression in test/db/extras/r2ghidra_ccode with the compiled fixture.
sanitizeCName() assumed every flag tail was `<lib>_<fn>`, which corrupted
names in actual output:
- ELF imports lost their prefix: sym.imp.pthread_mutex_lock became mutex_lock
(and sym.imp.__libc_init lost a leading underscore).
- Windows API-set DLL names stopped at the first underscore inside the
library name, leaving a dot and naming the wrong symbol:
sub.api_ms_win_crt_stdio_l1_1_0.dll__set_fmode became
ms_win_crt_stdio_l1_1_0.dll__set_fmode.
Only strip a real PE `.dll_` separator (case-insensitive); keep the whole tail
for ELF imports, handle loc.imp. like sym.imp./imp., and use the same rule for
sub. stubs. Normalize the result to a legal C identifier: keep leading
underscores, map dots/@/other illegal characters to _, never return empty
(a leading digit gets an underscore prefixed).
Prefer import metadata when it actually improves the name: registerFunction()
now uses a flag realname that differs from the raw flag name (rsym. flags carry
their own namespaced name as realname). This matches what registerFunctionFlag
and registerFlag already do for ccode output and also fixes those names for
e asm.flags.real=true.
Regressions in test/db/extras/r2ghidra_ccode:
- radare2-testbins/pe/ConsoleApplication1.exe, the reviewer's
sub.api_ms_win_crt_stdio_l1_1_0.dll__set_fmode fixture -> _set_fmode
- a small ELF .so importing pthread_mutex_lock -> pthread_mutex_lock
- hello-arm's sym.imp.__libc_init -> __libc_init
The wrap pointed at a fork revision with the patches baked into src, and the
generated dependency Makefile still applied a private copy of the patches that
this PR deletes, so clean builds failed on the missing 0100-CHAR-windows.patch
and ghidra-native.patch.
Pin both the wrap and subprojects/ghidra-native.mk to radareorg's merged
revision bcfd3fa and apply upstream's own patch stack once:
- acr Makefile: after checkout and the packagefiles overlay, run upstream's
`make patch`, which applies patches/*.patch in order and fails on the first
error (patch.done marks completion).
- meson overlay: at configure time apply the same top-level patches once with
`git apply` (git is guaranteed for wrap-git builds, so Windows needs no GNU
patch), enumerated from `git ls-files patches` filtered to patches/*.patch,
which is the same order as upstream's `ls patches/*.patch | sort -n`.
Dirty trees and trees carrying upstream's patch.done are skipped, and a
checkout at any other revision fails configure, so the stack is never
applied twice.
A `make dist` archive stays the release path: it ships pre-patched and carries
patch.done but neither patches/ nor .git, so the meson step is a no-op.
Verified from a clean tree: `make -C subprojects ghidra-native` exits 0 and
leaves a tree whose diff is byte-identical to upstream's own `make patch`
flow; a fresh meson fetch logged "applied 18 upstream patches" and produced
the same tree; re-running configure skipped patching (hash unchanged) and the
MSVC build completed.
Point the ghidra-native wrap at the fork (feature/r2ghidra-patches, which
ships the patches/ and the patched src/) and drop the private
subprojects/packagefiles/ghidra-native/patches copy plus the diff_files list.
The decompiler now builds from ghidra-native's patched source directly.
Note: the wrap url/revision point at the kkuehl fork until the ghidra-native
PR merges to radareorg/ghidra-native; then re-point at the upstream commit.
When e r2ghidra.ccode is enabled, resolve flags to their realname (the
clean import or demangled name radare2 already derives from the binary)
instead of the sym.imp.<lib>_<fn> or mangled flag name. sanitizeCName stays
as the fallback for names with no realname, such as anonymous functions.
On Windows PE, imports are reached through the IAT: `call qword
[sym.imp.KERNEL32.dll_WriteFile]`. Pcode renders that as an indirect
dereference `(*_WriteFile)(...)`, which is not a direct call expression.
The existing fixupResolvedIndirectCalls only deindirects register-indirect
calls.
Add fixupImportCallDeindirection: for every CALL/CODE ref whose target is
an import flag, insert a deindirect override so the decompiler prints a
direct call. Gated by e r2ghidra.ccode so default output is unchanged.
radare2 6.2 names imports `sym.imp.<lib>_<fn>` (e.g.
`sym.imp.KERNEL32.dll_WriteFile`) rather than the older `sym.imp.<fn>`.
The previous sanitizer only dropped `sym.imp.`, leaving the `.dll`-bearing
library name in the output (`KERNEL32.dll_WriteFile`), which is still not a
valid C identifier. Drop everything through the first `_` so the bare
`WriteFile` remains, and switch `sub.` to `find('_')` too so names with a
leading underscore (`_snprintf`) survive.
When e r2ghidra.ccode=true, additionally set NoCastPrinting, HideImpliedExts
and disable the calling convention on the prototype, so the output reads more
like Ghidra's toggleCCode and parses as clean C with less ZEXT/SEXT noise.
radare2 labels import stubs sub.<lib>_<fn>, anonymous functions fcn.ADDR and
imports sym.imp.fn / imp.fn - none of which are valid C identifiers, so the
decompiled output uses names that are not legal C.
New opt-in eval var, e r2ghidra.ccode=true, sanitizes names in R2Scope:
- sub.<lib>_<fn> -> <fn> (import stubs -> bare symbol, e.g. memcpy)
- sym.imp.<fn> / imp.<fn> -> <fn> (import flags)
- dotted names (fcn.ADDR, sym.lib.fn) -> underscores
Applied in registerFunction, registerFunctionFlag and registerFlag; default
stays false so existing output is unchanged.
Commit 20d6ae1 bumped ghidra-native to 483ae94 but only regenerated a few
patches, leaving four stale ones that no longer apply and break the meson
build:
- 0002-make-sleigharch-public (sleigh_arch.hh reorganized, getDescriptions
present, collectSpecFiles still in protected section)
- 0020-Fix-double-free (xml.hh moved from 'using namespace std' to
per-symbol using declarations)
- 0091-decompiler-xml-packer (setOutputStream moved below setPackedOutput)
- 0092-badvar-segfault (SymbolEntry::getSizedType renamed to
MapEntry::getSizedType and the off computation folded to one line)
Regenerate all four against 483ae94 so the wrap applies cleanly. Also add
the missing trailing newline to 0001-space-after-comma.patch, which newer
git releases otherwise reject as a corrupt patch.