* fix(ghidra): prevent loader relocations from constant folding
Ghidra imports loader relocation destinations with their file bytes. A
destination in a non-writable loaded block can be populated by the
runtime loader instead, for example a copy-relocated musl `stderr` in
RELRO, so the decompiler folded the on-disk zero and showed
`fprintf(NULL, ...)`.
Before initializing the decompiler, mark each loaded, initialized,
non-writable block containing a relocation destination volatile in the
ephemeral database, and add a limitation to procedures that read such a
destination. ELF shared objects and PIEs are included; relocatable
objects and core files are not. Permissions and source FileBytes are
unchanged.
Sessions without database-mutation authority (Windows P0 and existing
caller projects) record the limitation but do not mark blocks volatile,
and the analysis profile versions the two behaviors separately.
A real Linux Ghidra ELF probe runs in the Ghidra bridge CI lane.
Fixes#4177
Squashed from #4198 and rebased onto main. Rebase adaptations: the
read-only gate, the verifier renamed to verify-ghidra-loader-relocation
so it stays out of the manual-verifier glob, docs moved to
docs/testing-lanes.md and docs/providers/ghidra-provider.md.
Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ghidra): await installation inspection in the relocation verifier
inspectGhidraInstallation became async on main after #4198's base, so the
verifier asserted on a pending Promise. Also wait for provider output to
close before reading it, like the sibling Ghidra verifiers, and keep the
workflow reference inside the docs site.
Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ghidra): address relocation review findings
- Version the existing-project analysis profile with the read-only
relocation semantics, since those sessions now add the limitation.
- Inspect the installation through the verifier helper that owns the
file-reader resources.
- Build the fixture with -fPIC: with -fPIE, GCC 12 emits a copy
relocation of rea_external_value into writable .bss, so the probe's
GOT check could not hold.
- Run probe cleanup through withVerifierCleanup so a cleanup failure
does not hide the probe failure, and keep the workspace when owned
Ghidra cleanup is incomplete.
- Link the workflow with a full GitHub URL, per the docs guide.
Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(ghidra): reach the external data through the GOT with a weak extern
Building the whole fixture with -fPIC also turned the reader's direct
load of rea_loader_pointer into a GOT-relaxed lea plus two loads, and
the real-Ghidra lane then reported that the baseline no longer folds
the file constant. Keep -fPIE and declare only rea_external_value weak:
GCC and clang must reach a possibly absent symbol through a GOT entry,
which lands as R_X86_64_GLOB_DAT inside GNU_RELRO (checked with readelf
for GCC 12.2 and clang/lld).
Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ghidra): protect only relocations Ghidra could not apply
Running the real-Ghidra lane locally (Ghidra 12.1.4, JDK 21, GCC 12)
showed that marking every non-writable block that holds a relocation
volatile also marks full-RELRO .got. External calls then decompile as
opaque pointer calls (`(*pcVar2)(param_1)` through
`PTR_rea_external_...` instead of `rea_external(param_1)`), which the
probe's GOT-stability check caught.
Ghidra applies GLOB_DAT and RELATIVE relocations, so those bytes already
hold the loader's value and folding them is correct. The #4177 case is
a copy relocation, which Ghidra imports as UNSUPPORTED with the file's
zero in place. Protect only destinations whose relocation status is not
APPLIED or APPLIED_OTHER.
The fixture now reads a provider `const` object that the linker copies
into .data.rel.ro, the #4177 shape. The probe requires that relocation
to be unapplied and requires applied GOT relocations to stay
non-volatile. Ghidra 12.1.4 already shows this x86-64 read by name
without protection, so the probe records whether the baseline folds and
compares before and after only when it does. The profile value becomes
volatile-unapplied-relocation-blocks-v1.
Co-Authored-By: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Arslan Basharat <arslanbasharat414@gmail.com>
Co-authored-by: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>