Expose the ObjectPool as anal.gp in r2flutter-A

x27/PP is the only Dart reserved register with a static address (the pool is
in the snapshot). r2flutter -A now seeds anal.gp with the pool base,
anal.gpseed=x27, anal.roregs=x27,gp,zero and an 'f PP' flag, so plain-r2
analysis/ESIL resolves [x27, off] pool loads - matching what -p -r already
set up. Resolution runs on a shallow DartCtx copy so the per-command read
cache (ctx->rmem) is not shared into the analysis DartApp and double-freed.
This commit is contained in:
pancake committed 2026-10-01 14:30:46 +02:00
1 parent a6fc7e4829
commit 5651efc492
5 files changed
+69

No files matched your search

+20
View File
@@ -1685,3 +1685,23 @@ residual are all word-aligned `complete=false` strings `-O` cannot resolve
either), bare indirect calls 1178 -> 1, ~150 misaligned `+0x..7` bogus comments
-> 0, and 64439 slots now carry a kind/name plus xrefs. Verified on iOS (Dart
3.13/3.2.5, cws=8) and Android (w0rdle, Dart 2.18, cws=4): 0 misaligned on both.
## Expose the ObjectPool as r2's global pointer (anal.gp) in r2flutter-A
x27 (PP) is the only Dart reserved register with a *static* address: the
ObjectPool lives in the snapshot, so we know its base. `r2flutter -p -r`
already emitted `e anal.gp=<base>` / `dr x27=<base>` / `e anal.roregs=...`, but
the analysis command never set them, and the `-R` script had a dead
`dr x27=\`e anal.gp\`` that depended on an unset value. `r2flutter -A` (every
depth) now calls `r2flutter_setup_pp_gp`, which seeds `anal.gp` with the pool
base, `anal.gpseed=x27`, `anal.roregs=x27,gp,zero` and an `f PP` flag, so plain
r2 analysis/ESIL resolves `[x27, off]` pool loads consistently.
Gotcha worth remembering: `dart_resolve_pp_info` populates the context read
cache (`ctx->rmem`) and can leave a stale layout pointer. `dart_app_new_from_core`
copies the `DartCtx` by value (`memcpy`), so resolving on the live session
context and then running the analysis double-freed that cache (SIGABRT after
"Loaded N functions"). The setup resolves on a shallow copy with its own
`rmem`/`layout` and frees only what it allocated, leaving the session context
pristine. x22 (NULL_REG) and x26 (THR) are runtime pointers with no static
address, so they are documented/annotated, never seeded as anal.gp.
+3
View File
@@ -394,6 +394,9 @@ static bool r2flutter_run_cmd(RCore *core, DartCtx *dctx, const R2FlutterCmd *cm
if (cmd->analysis_depth >= 2) {
dctx->dump_fields = 1;
}
// Seed anal.gp/gpseed/roregs with the ObjectPool base for every -A depth
// so plain-r2 analysis can resolve [x27, off] pool loads.
r2flutter_setup_pp_gp (core, dctx);
if (cmd->analysis_depth >= 3) {
r2flutter_analysis_run (core, dctx, dctx->quiet);
} else {
+28
View File
@@ -1317,6 +1317,34 @@ static void flutter_scan_functions(RCore *core, FlutterAnalModel *model, Flutter
ht_up_free (seen_fcns);
}
// Expose the reconstructed ObjectPool as r2's global pointer so that plain-r2
// analysis and ESIL resolve `[x27, off]` loads to pool addresses, matching how
// `r2flutter -p -r` already wires it up. x27 (PP) is the only Dart reserved
// register with a static address; x22 (NULL_REG) and x26 (THR) are runtime
// pointers and cannot be seeded this way.
void r2flutter_setup_pp_gp(RCore *core, DartCtx *dctx) {
if (!core || !dctx) {
return;
}
// Resolve on a shallow copy: dart_resolve_pp_info populates the read cache
// (ctx->rmem) and can leave a stale layout pointer in the context. The real
// dctx is copied by value into the analysis DartApp, so polluting it here
// would double-free that cache. The copy shares heap-owned pointers with
// dctx, so we only release what we allocated into it (its own rmem).
DartCtx probe = *dctx;
probe.rmem = NULL;
probe.layout = NULL;
DartPpInfo pp = { 0 };
if (dart_resolve_pp_info (&probe, &pp) && pp.base) {
r_config_set_i (core->config, "anal.gp", pp.base);
r_config_set (core->config, "anal.gpseed", "x27");
r_config_set (core->config, "anal.roregs", "x27,gp,zero");
r_flag_set (core->flags, "PP", pp.base, 1);
}
dart_pp_info_fini (&pp);
dart_read_cache_free (probe.rmem);
}
bool r2flutter_analysis_run(RCore *core, DartCtx *dctx, bool quiet) {
R_RETURN_VAL_IF_FAIL (core && dctx, false);
DartApp *app = dart_app_new_from_core (core, dctx);
+1
View File
@@ -5,5 +5,6 @@
#include "../../include/r2flutter/dart_r2.h"
bool r2flutter_analysis_run(RCore *core, DartCtx *dctx, bool quiet);
void r2flutter_setup_pp_gp(RCore *core, DartCtx *dctx);
#endif
+17
View File
@@ -0,0 +1,17 @@
NAME=r2-pp-gp-register-ios
FILE=bins/ios/Runner.app/Frameworks/App.framework/App
ARGS=-NN -e scr.color=0 -e bin.relocs.apply=true
CMDS=<<EOF2
L ../src/r2/core_flutter.so
L ../src/r2/core_flutter.dylib
r2flutter -A
e anal.gp
e anal.gpseed
f~ PP[0]
EOF2
EXPECT=<<EOF2
0x0022d2d1
x27
0x0022d2d1
EOF2
RUN