Unify the tag from cid getter for the 3 possible formats

This commit is contained in:
pancake committed 2026-08-11 14:17:36 +02:00
1 parent e9fa905562
commit 897a98d24d
9 files changed
+29 -65

No files matched your search

+2 -14
View File
@@ -24,6 +24,7 @@ rank; each item states the concrete failure it causes.
| `0716a95` | **Structural instruction-image location** for symbol-less Mach-O: `vm_instr` = first executable section, `iso_instr = align_up(vm_instr + ImageSize, 64)` from the Dart Image header, with cross-checks that fall back to `0` rather than emit junk. Verified on stripped Runner (`0x4000`/`0xe700`, full 7671 IT entries) and AuthPass (`0x5a80`/`0xfdc0`). Adds `scripts/strip_macho_symbols.py` + `discovery-stripped-ios`. |
| `d11ebc3` | **P0.1**: `modern_get_fill_spec`'s fallback `rules[]` is keyed by `DartCidKind` and resolved through a per-layout `by_kind[]` table built once in `modern_cid_cache_init`, instead of literal 3.6-era CIDs. Fixes a whole-block off-by-one on 3.2–3.5 layouts (36 CIDs from `SENTINEL` upward got the neighbouring class's ref/scalar layout) and `MonomorphicSmiableCall`/`UnlinkedCall` on 3.9+, where the two swapped places. Abstract classes that never form a cluster (`CallSiteData`, `AbstractType`, `TypedDataBase`) no longer claim a spec. |
| *(uncommitted)* | **P2.5 + part of P0.2**: one shared cluster walker (`ModernWalk`) replaces the five hand-rolled tag→cid→alloc→fill loops. Consumers register readers in a `DartCidKind`-keyed table and the walker skips every class they don't claim, so tag decoding, alloc/fill skipping, `start_ref` bookkeeping and the inline-string decision each exist **once** (were 4–5× each). `modern_walk_fill_recorded` serves the resolver, which reads each cluster from its own recorded offset. Verified behaviour-preserving: output byte-identical on 5 samples × 21 actions. Also lands the **P0.2 read-only-data rule** (`modern_walk_is_rodata`): the six classes Dart moves into the data image when pointer compression is off now correctly have no fill records. Inert until the gate opens. |
| *(uncommitted)* | **P1.1**: one `dart_cid_from_tags` helper now decodes `CID_INT32`, `CID_SHIFT1`, and `OBJECT_HEADER` tags for object inspection, modern and legacy cluster walks, data-image scanners, and InstructionTable discovery. This removes the duplicate decoders and fixes `CID_INT32` being treated as `OBJECT_HEADER` in two paths. |
Corrections to earlier assumptions, worth remembering:
- **The CID tables are not the problem.** Flooring is exactly correct 2.10→3.12,
@@ -133,18 +134,6 @@ app samples need collecting.
## P1 — correctness, lower prevalence or already-guarded
### P1.1 Tag-style CID decode applied inconsistently
`dart_object.c:88-99` honours all three `DartTagStyle` variants, but cluster/CID
decode hardcodes the OBJECT_HEADER form `(tags>>12)&0xFFFFF` in
`dart_pool_clusters.c:226`, `modern_cid_from_tags` (`dart_pool_modern.c`, now the
decoder's single site after P2.5), `dart_pool_data_image.c:82`; and
`dart_pool_it.c:158-160` routes **CID_INT32** into the OBJECT_HEADER formula
(should be `header & 0xffffffff`).
**Failure:** pre-2.14 (CID_INT32) and 2.14–3.3 (CID_SHIFT1) snapshots are
mis-decoded by every cluster walker. Mitigated today only because those eras also
fail the modern gate and fall back to scanners.
**Fix:** one tag-style-aware extractor, called everywhere.
### P1.2 Hardcoded 8-byte target word / arm64 assumptions
- `modern_target_word_size()` (`dart_pool_modern.c:1196-1199`) **ignores `ctx` and
returns 8** — it takes `ctx` only to `(void)` it. All ObjectPool `pp_offset`
@@ -294,8 +283,7 @@ repeatedly. Consider a small multi-window/LRU cache and a short-read-tolerant re
the landed P0.1 fix an end-to-end regression test.
3. **P2.1 + P3.1** — delete the legacy walker; one cluster walk, one data scan.
4. **P1.3** — shared frontend parser (fixes the `-r`/`r2 -n` AGENTS.md violations).
5. **P1.1, P1.2** — tag-style helper (now a one-line change in the modern decoder
thanks to P2.5); arch gating.
5. **P1.2** — arch gating.
6. **P3.2–P3.4**, remaining P1/P2 cleanups, test hardening.
`COMMIT2.md` tracks the narrower follow-ups left over from the discovery work
+7
View File
@@ -1121,6 +1121,13 @@ so the generator keeps `2.18.2` on the `3.9.2`-shaped fallback CID table used by
the sample. This is documented as a Flutter-engine snapshot quirk, not as
standalone Dart SDK `2.18.2` CID data.
CID extraction must go through `dart_cid_from_tags()`. Serialized snapshots use
three encodings: a raw 32-bit CID through Dart 2.13, a CID shifted left by one
through Dart 3.3, and the ObjectHeader bitfield from Dart 3.4 onward. Keeping the
formula in one helper prevents object, cluster, data-image, and InstructionTable
parsers from silently disagreeing. This only centralizes tag decoding; older
cluster fill layouts still need validation before the modern parser gate opens.
## Dart SDK Stable Tag Refresh
Running `scripts/update-dart-version --sync-stable-tags --min-version 2.10.0`
+1 -14
View File
@@ -85,19 +85,6 @@ static int dart_object_heap_tag(DartCtx *ctx) {
return tag;
}
static int dart_object_cid_from_header(DartCtx *ctx, ut64 header) {
DartTagStyle style = ctx && ctx->layout? ctx->layout->tag_style: DART_TAG_STYLE_OBJECT_HEADER;
switch (style) {
case DART_TAG_STYLE_CID_INT32:
return (int) (header & 0xffffffffU);
case DART_TAG_STYLE_CID_SHIFT1:
return (int) ((header >> 1) & 0xffffffffU);
case DART_TAG_STYLE_OBJECT_HEADER:
default:
return (int) ((header >> 12) & 0xfffffU);
}
}
static const char *dart_object_cid_name(DartCtx *ctx, int cid) {
const DartVerLayout *layout = ctx? ctx->layout: NULL;
for (int i = 0; i < DART_CID_KIND_COUNT; i++) {
@@ -265,7 +252,7 @@ static bool dart_object_decode_at(DartCtx *ctx, ut64 addr, DartObjectInfo *out)
}
out->addr = addr;
out->header = r_read_le64 (hdr);
out->cid = dart_object_cid_from_header (ctx, out->header);
out->cid = (int)dart_cid_from_tags (ctx, out->header);
if (out->cid <= 0 || out->cid >= 0xfffff) {
return false;
}
+1 -1
View File
@@ -1509,7 +1509,7 @@ RList *dart_pool_extract_classes(DartCtx *ctx) {
if (!cs_read_u32 (&stream, &tags)) {
break;
}
uint32_t cid = (tags >> 12) & 0xFFFFF;
const uint32_t cid = dart_cid_from_tags (ctx, tags);
bool is_canonical = ((tags >> 1) & 1) != 0;
(void)is_canonical;
if (ctx->verbose > 1) {
+1 -1
View File
@@ -223,7 +223,7 @@ int deserialize_clusters(DartCtx *ctx, ut64 cluster_start, ut64 cluster_end, ut6
}
break;
}
uint32_t cid = (tags >> 12) & 0xFFFFF;
const uint32_t cid = dart_cid_from_tags (ctx, tags);
bool is_canonical = ((tags >> 1) & 1) != 0;
if (ctx->verbose > 1) {
fprintf (stderr, "[r2flutter] Cluster %" PRIu64 ": cid=%u canonical=%d cursor=0x%" PFMT64x "\n", ci, cid, is_canonical, stream.cursor);
+2 -17
View File
@@ -21,21 +21,6 @@ void init_function_layout(DartCtx *ctx, DartFunctionLayout *fl) {
}
}
static ut32 extract_cid_from_header(DartCtx *ctx, ut64 header) {
if (!ctx || !ctx->layout) {
return 0;
}
switch (ctx->layout->tag_style) {
case DART_TAG_STYLE_OBJECT_HEADER:
return (ut32) ((header >> 12) & 0xFFFFF);
case DART_TAG_STYLE_CID_SHIFT1:
return (ut32) (header >> 1);
case DART_TAG_STYLE_CID_INT32:
default:
return (ut32) ((header >> 12) & 0xFFFFF);
}
}
static bool read_object_pointer(DartCtx *ctx, const ut8 *buf, ut32 off, bool use_compressed, ut64 data_base, ut64 data_end, bool restrict_range, ut64 *out_addr) {
(void)ctx;
if (use_compressed) {
@@ -79,7 +64,7 @@ bool read_data_image_field(DartCtx *ctx, ut64 pos, ut64 data_start, ut64 data_en
return false;
}
ut64 header = r_read_le64 (hdr);
if ((int) ((header >> 12) & 0xFFFFF) != kFieldCid) {
if ((int)dart_cid_from_tags (ctx, header) != kFieldCid) {
return false;
}
bool use_compressed = ctx->compressed_word_size == 4;
@@ -174,7 +159,7 @@ bool read_data_image_method(DartCtx *ctx, ut64 pos, ut64 data_start, ut64 data_e
}
const int resolved_function_cid = dart_cid_get (ctx->layout, DART_CID_FUNCTION);
const int cid_function = resolved_function_cid >= 0? resolved_function_cid: kFunctionCid;
if ((int)extract_cid_from_header (ctx, r_read_le64 (buf)) != cid_function) {
if ((int)dart_cid_from_tags (ctx, r_read_le64 (buf)) != cid_function) {
return false;
}
ut64 entry = r_read_le64 (buf + fl->entry_off);
+1 -13
View File
@@ -147,19 +147,7 @@ static bool read_it_data_header_from_string_object(DartCtx *ctx, ut64 obj_addr,
return false;
}
ut64 tags = r_read_le64 (objhdr);
ut32 cid = 0;
switch (ctx->layout->tag_style) {
case DART_TAG_STYLE_OBJECT_HEADER:
cid = (ut32) ((tags >> 12) & 0xFFFFF);
break;
case DART_TAG_STYLE_CID_SHIFT1:
cid = (ut32) (tags >> 1);
break;
case DART_TAG_STYLE_CID_INT32:
default:
cid = (ut32) ((tags >> 12) & 0xFFFFF);
break;
}
const ut32 cid = dart_cid_from_tags (ctx, tags);
const int resolved_string_cid = dart_cid_get (ctx->layout, DART_CID_ONE_BYTE_STRING);
const int string_cid = resolved_string_cid >= 0? resolved_string_cid: kOneByteStringCid;
if ((int)cid != string_cid) {
+1 -5
View File
@@ -1361,10 +1361,6 @@ static bool modern_read_cluster_tags(ClusterStream *s, DartCtx *ctx, ut32 *out)
return cs_read_tagged32 (s, out);
}
static int modern_cid_from_tags(ut32 tags) {
return (int) ((tags >> 12) & 0xFFFFF);
}
// Shared alloc/fill cluster walker.
//
// Every consumer below reads the same stream the same way: for each cluster a
@@ -1492,7 +1488,7 @@ static bool modern_walk_alloc(ModernWalk *w, ClusterStream *s) {
if (!modern_read_cluster_tags (s, w->ctx, &tags)) {
return modern_walk_fail (w, "alloc tag", i, s->cursor, -1);
}
m->cid = modern_cid_from_tags (tags);
m->cid = (int)dart_cid_from_tags (w->ctx, tags);
m->is_canonical = ((tags >> 1) & 1) != 0;
m->is_immutable = (tags & (1 << 6)) != 0;
m->start_ref = next_ref;
+13
View File
@@ -17,6 +17,19 @@ typedef struct {
ut64 end;
} ClusterStream;
static inline ut32 dart_cid_from_tags(const DartCtx *ctx, ut64 tags) {
const DartTagStyle style = ctx && ctx->layout? ctx->layout->tag_style: DART_TAG_STYLE_OBJECT_HEADER;
switch (style) {
case DART_TAG_STYLE_CID_INT32:
return (ut32)tags;
case DART_TAG_STYLE_CID_SHIFT1:
return (ut32) (tags >> 1);
case DART_TAG_STYLE_OBJECT_HEADER:
default:
return (ut32) ((tags >> 12) & 0xfffffU);
}
}
typedef enum {
kIllegalCid = 0,
kClassCid = 5,