mirror of
https://github.com/radareorg/r2flutter.git
synced 2026-11-08 07:28:14 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02b0b84fa6 | ||
|
|
0ee5e01498 | ||
|
|
cc338995cf | ||
|
|
8a9e54be29 | ||
|
|
441d614937 | ||
|
|
25a5b3f026 | ||
|
|
3bfdd5601a | ||
|
|
84a25ea38c | ||
|
|
2bf613618f | ||
|
|
a2fd82106e | ||
|
|
bd6f596279 | ||
|
|
7be0c178fb | ||
|
|
3552d7485c | ||
|
|
878a588adf | ||
|
|
d0215dbfc2 | ||
|
|
3cef0ce07a | ||
|
|
7d019ea748 | ||
|
|
f7c6cc5b3d | ||
|
|
24e3502737 | ||
|
|
78719df43e | ||
|
|
3fe3c11bac | ||
|
|
fa854fb648 | ||
|
|
a9efd1d262 | ||
|
|
01db9e6b54 | ||
|
|
abcdbb2b11 | ||
|
|
05ebbd2c2d | ||
|
|
cd90ffe555 | ||
|
|
05c0616b37 | ||
|
|
49f174f3cd | ||
|
|
cfb6448172 | ||
|
|
b7f425ca97 | ||
|
|
db30ebcb55 | ||
|
|
5651efc492 | ||
|
|
a6fc7e4829 | ||
|
|
d0467e4e41 | ||
|
|
f35d5b569d | ||
|
|
2396599251 | ||
|
|
7d283902f3 | ||
|
|
30b8fe76d9 | ||
|
|
67935055e5 | ||
|
|
5c4a51d5fb | ||
|
|
e8bf641f91 | ||
|
|
3e12922ba0 | ||
|
|
42b606ae7f | ||
|
|
507994deda | ||
|
|
ce97e96eda | ||
|
|
2f8343b97b | ||
|
|
5394a6f6e6 | ||
|
|
b2d8a4917a | ||
|
|
edd43d6042 | ||
|
|
2f53ba301d | ||
|
|
116aa82551 | ||
|
|
d80fa5bd2e | ||
|
|
ae66842877 | ||
|
|
e6b9eefae9 | ||
|
|
5d86dff9fa | ||
|
|
14c1847177 | ||
|
|
edd90ba5c7 | ||
|
|
a7317c7705 | ||
|
|
3fe9dff0cc | ||
|
|
2e85b70bda | ||
|
|
b45d487fd3 | ||
|
|
b1994cd98e | ||
|
|
3b9d12efea | ||
|
|
f5d7bb15f1 | ||
|
|
66b294ab7c | ||
|
|
5a3da4b149 | ||
|
|
545a2161ca | ||
|
|
6bad309eb6 | ||
|
|
283242b724 | ||
|
|
81acc1c5df | ||
|
|
5d745cdd6b | ||
|
|
54c7aed1ba | ||
|
|
75ad39a7cb | ||
|
|
1f36c65729 | ||
|
|
2e9bb55763 | ||
|
|
f85bd3beae | ||
|
|
791547c855 | ||
|
|
ef2fd323d7 | ||
|
|
bea45d8348 | ||
|
|
43671cfcb3 | ||
|
|
2db2a74bc7 | ||
|
|
02efb309a2 | ||
|
|
08001fe45b | ||
|
|
5d3ffce739 | ||
|
|
b6ea0b8923 | ||
|
|
540efcf5e1 | ||
|
|
25dc49ef8f | ||
|
|
6a7e01b0c9 | ||
|
|
334c97b88d | ||
|
|
f10d30dd6f | ||
|
|
f138b952c9 | ||
|
|
833f89a5d9 | ||
|
|
897a98d24d | ||
|
|
e9fa905562 | ||
|
|
33bb19bbe0 | ||
|
|
d11ebc35b9 | ||
|
|
5592349d99 | ||
|
|
986e2cb42a | ||
|
|
cd6b655c0c | ||
|
|
239178152b | ||
|
|
d387c0b15b | ||
|
|
19d8b96fc5 | ||
|
|
7e1803998e | ||
|
|
33588f29a7 | ||
|
|
4eede7cb33 | ||
|
|
48736a2a89 | ||
|
|
f680d5551a | ||
|
|
07b0ca1e4e | ||
|
|
7f6449b82a | ||
|
|
3b8516b706 | ||
|
|
6b3730c535 | ||
|
|
fd857f5918 | ||
|
|
2a8d3151e8 | ||
|
|
d7ff3c5b78 | ||
|
|
4886597928 | ||
|
|
8257b88040 | ||
|
|
bfaf13a013 |
No files matched your search
+189
-6
@@ -1,12 +1,14 @@
|
||||
name: ci
|
||||
|
||||
env:
|
||||
R2V: 6.1.6
|
||||
R2V: 6.2.4
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build-linux24:
|
||||
@@ -14,7 +16,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- name: Installing radare2 ${{ env.R2V }}
|
||||
uses: radareorg/github-actions@v2
|
||||
uses: radareorg/github-actions@v3
|
||||
with:
|
||||
version: ${{ env.R2V }}
|
||||
from-git: false
|
||||
@@ -26,6 +28,48 @@ jobs:
|
||||
run: r2r test/db/cmd
|
||||
- name: Radare Extras Testing
|
||||
run: r2r test/db/extras
|
||||
- name: Package r2pm binary
|
||||
run: make bindist
|
||||
- name: Verify r2pm binary
|
||||
run: |
|
||||
unzip -l r2flutter-${{ env.R2V }}-linux-x86-64.zip
|
||||
unzip -l r2flutter-${{ env.R2V }}-linux-x86-64.zip | grep -q 'bin/r2flutter$'
|
||||
unzip -l r2flutter-${{ env.R2V }}-linux-x86-64.zip | grep -q 'plugins/core_flutter.so$'
|
||||
- name: Upload r2pm binary
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: bindist-linux-x86-64
|
||||
path: r2flutter-*.zip
|
||||
if-no-files-found: error
|
||||
- name: Package Debian build
|
||||
run: make -C dist/debian
|
||||
- name: Verify Debian package
|
||||
shell: bash
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
packages=(dist/debian/r2flutter_*_amd64.deb)
|
||||
test "${#packages[@]}" -eq 1
|
||||
package="${packages[0]}"
|
||||
version="$(./configure -qV)"
|
||||
test "$(dpkg-deb --field "${package}" Package)" = r2flutter
|
||||
test "$(dpkg-deb --field "${package}" Version)" = "${version}"
|
||||
test "$(dpkg-deb --field "${package}" Architecture)" = amd64
|
||||
test "$(dpkg-deb --field "${package}" Depends)" = radare2
|
||||
contents="$(dpkg-deb --contents "${package}")"
|
||||
printf '%s\n' "${contents}"
|
||||
grep -E '^-.+ root/root .+ \./usr/local/bin/r2flutter$' <<< "${contents}"
|
||||
plugin=".$(r2 -H R2_LIBR_PLUGINS)/core_flutter.so"
|
||||
grep -Fq "${plugin}" <<< "${contents}"
|
||||
make user-uninstall
|
||||
sudo dpkg --force-depends --install "${package}"
|
||||
test "$(r2flutter -V)" = "r2flutter ${version}"
|
||||
test "$(r2 -q -c 'r2flutter -V' --)" = "r2flutter ${version}"
|
||||
- name: Upload Debian package
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: r2flutter-debian-amd64
|
||||
path: dist/debian/r2flutter_*_amd64.deb
|
||||
if-no-files-found: error
|
||||
|
||||
meson-linux24:
|
||||
name: Meson build on Linux
|
||||
@@ -33,9 +77,9 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install build dependencies
|
||||
run: sudo apt-get update -y && sudo apt-get install -y build-essential meson ninja-build pkg-config
|
||||
run: sudo apt-get update -y && sudo apt-get install -y build-essential meson ninja-build
|
||||
- name: Installing radare2 ${{ env.R2V }}
|
||||
uses: radareorg/github-actions@v2
|
||||
uses: radareorg/github-actions@v3
|
||||
with:
|
||||
version: ${{ env.R2V }}
|
||||
from-git: false
|
||||
@@ -51,7 +95,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- name: Installing radare2 ${{ env.R2V }}
|
||||
uses: radareorg/github-actions@v2
|
||||
uses: radareorg/github-actions@v3
|
||||
with:
|
||||
version: ${{ env.R2V }}
|
||||
from-git: false
|
||||
@@ -63,6 +107,78 @@ jobs:
|
||||
run: r2r test/db/cmd
|
||||
- name: Radare Extras Testing
|
||||
run: r2r test/db/extras
|
||||
- name: Package r2pm binary
|
||||
run: make bindist
|
||||
- name: Verify r2pm binary
|
||||
run: |
|
||||
unzip -l r2flutter-${{ env.R2V }}-darwin-arm-64.zip
|
||||
unzip -l r2flutter-${{ env.R2V }}-darwin-arm-64.zip | grep -q 'bin/r2flutter$'
|
||||
unzip -l r2flutter-${{ env.R2V }}-darwin-arm-64.zip | grep -q 'plugins/core_flutter.dylib$'
|
||||
- name: Upload r2pm binary
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: bindist-darwin-arm-64
|
||||
path: r2flutter-*.zip
|
||||
if-no-files-found: error
|
||||
|
||||
build-linux-arm:
|
||||
runs-on: ubuntu-24.04-arm
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- name: Installing radare2 ${{ env.R2V }}
|
||||
uses: radareorg/github-actions@v3
|
||||
with:
|
||||
version: ${{ env.R2V }}
|
||||
from-git: false
|
||||
- name: Building r2flutter
|
||||
run: make && make user-install
|
||||
- name: Basic Testing
|
||||
run: make test
|
||||
- name: Package r2pm binary
|
||||
run: make bindist
|
||||
- name: Upload r2pm binary
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: bindist-linux-arm-64
|
||||
path: r2flutter-*.zip
|
||||
if-no-files-found: error
|
||||
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-python@v7
|
||||
with:
|
||||
python-version: 3.14.x
|
||||
- name: Prepare MSVC toolchain
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
with:
|
||||
arch: x64
|
||||
- name: Install build tools
|
||||
shell: pwsh
|
||||
run: python -m pip install meson ninja
|
||||
- name: Install radare2 ${{ env.R2V }}
|
||||
uses: radareorg/github-actions@v3
|
||||
with:
|
||||
version: ${{ env.R2V }}
|
||||
from-git: false
|
||||
- name: Build and package r2flutter
|
||||
shell: pwsh
|
||||
run: ./dist/windows/build.ps1
|
||||
- name: Verify Windows package
|
||||
shell: pwsh
|
||||
run: |
|
||||
Expand-Archive -Path r2flutter-$env:R2V-windows-x86-64.zip -DestinationPath check-zip -Force
|
||||
Get-ChildItem -Recurse check-zip | Select-Object -ExpandProperty FullName
|
||||
$root = 'check-zip'
|
||||
if (-not (Test-Path "$root/bin/r2flutter.exe")) { throw 'missing r2flutter.exe' }
|
||||
if (-not (Test-Path "$root/plugins/core_flutter.dll")) { throw 'missing core_flutter.dll' }
|
||||
- name: Upload Windows package
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: bindist-windows-x86-64
|
||||
path: r2flutter-*.zip
|
||||
if-no-files-found: error
|
||||
|
||||
r2-xps:
|
||||
name: Build radare2 with r2flutter XPS
|
||||
@@ -72,7 +188,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install build dependencies
|
||||
run: sudo apt-get update -y && sudo apt-get install -y build-essential pkg-config
|
||||
run: sudo apt-get update -y && sudo apt-get install -y build-essential
|
||||
- name: Build radare2 from source with r2flutter
|
||||
run: |
|
||||
git clone --depth=1 https://github.com/radareorg/radare2.git
|
||||
@@ -87,3 +203,70 @@ jobs:
|
||||
run: |
|
||||
r2 -q -c 'Lc~r2flutter' -- | grep r2flutter
|
||||
r2 -q -c 'r2flutter -V' -- | grep 'r2flutter '
|
||||
|
||||
check-release:
|
||||
if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') }}
|
||||
needs:
|
||||
- build-linux24
|
||||
- meson-linux24
|
||||
- build-macos
|
||||
- build-linux-arm
|
||||
- build-windows
|
||||
- r2-xps
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
is_release: ${{ steps.tag.outputs.is_release }}
|
||||
steps:
|
||||
- name: Check release tag
|
||||
id: tag
|
||||
shell: bash
|
||||
run: |
|
||||
if [[ "${GITHUB_REF_NAME}" =~ ^[0-9] ]]; then
|
||||
echo "is_release=yes" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "is_release=no" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
release:
|
||||
if: ${{ needs.check-release.outputs.is_release == 'yes' }}
|
||||
needs: check-release
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Prepare release notes
|
||||
shell: bash
|
||||
run: |
|
||||
tag="${GITHUB_REF_NAME}"
|
||||
previous_tag="$(git tag --merged "${tag}^" --sort=-version:refname | awk '/^[0-9]/{print; exit}')"
|
||||
{
|
||||
echo "# ${tag}"
|
||||
echo
|
||||
if [ -n "${previous_tag}" ]; then
|
||||
echo "## Changes since ${previous_tag}"
|
||||
echo
|
||||
git log --pretty=format:'- %s (%h)' "${previous_tag}..${tag}"
|
||||
else
|
||||
echo "## Changes"
|
||||
echo
|
||||
git log --pretty=format:'- %s (%h)' "${tag}"
|
||||
fi
|
||||
echo
|
||||
} > RELEASE_NOTES.md
|
||||
- name: Download release artifacts
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
path: release-artifacts
|
||||
- name: Create GitHub release
|
||||
uses: softprops/action-gh-release@v3
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref_name }}
|
||||
name: ${{ github.ref_name }}
|
||||
body_path: RELEASE_NOTES.md
|
||||
generate_release_notes: false
|
||||
files: |
|
||||
release-artifacts/r2flutter-debian-amd64/r2flutter_*_amd64.deb
|
||||
release-artifacts/bindist-*/r2flutter-*.zip
|
||||
@@ -6,3 +6,4 @@ bin
|
||||
*.so
|
||||
*.dylib
|
||||
*.swp
|
||||
/r2flutter-*.zip
|
||||
@@ -4,8 +4,9 @@ Scope
|
||||
- This document is for agents working in this repo to implement and iterate on the Dart AOT snapshot/ObjectPool parser and CLI.
|
||||
|
||||
Build
|
||||
- Requires radare2 dev headers (`r_core`) and `pkg-config`.
|
||||
- Build with `make` (uses pkg-config to resolve r2 includes/libs).
|
||||
- Requires radare2 (`r2` binary in PATH) to resolve include/link flags via `r2 -H`.
|
||||
- Do NOT use `pkg-config` — the build system uses `r2 -H R2_CFLAGS` and `r2 -H R2_LDFLAGS` instead.
|
||||
- Build with `make` (uses `r2 -H` to resolve r2 includes/libs).
|
||||
- Do not build with gcc oneliners, always use `make`
|
||||
- Run `make fmt` to format/indent the source code
|
||||
- To update the version change it in configure.acr and then run autogen.sh
|
||||
|
||||
@@ -13,7 +13,7 @@ Repo Layout (relevant parts)
|
||||
- dart_app.[ch]: Minimal app model; wires callback for extracted functions; emits addNames.r2/r2_dart_struct.h.
|
||||
- dart_pool_parse.c: Snapshot discovery (via r2), offsets loading (offsets.json), and clustered snapshot/ObjectPool decoder (constant pool → names).
|
||||
- dart_dumper.[ch]: Emits radare2 flags/comments, PP (x27) helper flags, and a basic struct header.
|
||||
- Makefile: Pure-C build; links against libr2 via pkg-config; no Dart VM linkage.
|
||||
- Makefile: Pure-C build; links against libr2 via `r2 -H`; no Dart VM linkage.
|
||||
- third_party/dartvm/: Vendored VM sources (headers only) to document snapshot/cluster formats used by the decoder (no linking).
|
||||
- arm64-v8a/: Example directory with libapp.so/libflutter.so for testing.
|
||||
- scripts/
|
||||
@@ -21,7 +21,7 @@ Repo Layout (relevant parts)
|
||||
- blutter/: Standalone C++ reference implementation (with VM linkage) used as conceptual reference. r2flutter must not depend on it.
|
||||
|
||||
Quick Start
|
||||
- Requirements: radare2 development headers (pkg-config), make, a libapp.so and libflutter.so pair.
|
||||
- Requirements: radare2 (`r2` in PATH for `r2 -H` flag resolution), make, a libapp.so and libflutter.so pair.
|
||||
- Generate offsets for your libapp:
|
||||
- `./scripts/update-dart-version --hash <snapshot-hash> <dart-version>`
|
||||
- This produces `r2flutter/offsets.json` keyed by snapshot hash.
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
CC ?= gcc
|
||||
R2_CFLAGS ?= $(shell r2 -H R2_CFLAGS)
|
||||
R2_LDFLAGS ?= $(shell r2 -H R2_LDFLAGS)
|
||||
R2_LIBS ?= $(shell r2 -H R2_LIBS)
|
||||
CFLAGS ?= -Wall -Wextra -O2 -fPIC
|
||||
CFLAGS += -Iinclude -Iinclude/r2flutter
|
||||
CFLAGS += $(R2_CFLAGS)
|
||||
LDFLAGS += $(R2_LDFLAGS)
|
||||
LDLIBS += $(R2_LIBS)
|
||||
DEPFLAGS = -MMD -MP
|
||||
R2R_JOBS ?= 1
|
||||
R2R_TIMEOUT ?= 30
|
||||
R2R_TIMEOUT ?= 60
|
||||
R2_LIBR_PLUGINS=$(shell r2 -H R2_LIBR_PLUGINS 2> /dev/null)
|
||||
R2_LIBEXT=$(shell r2 -H R2_LIBEXT 2> /dev/null)
|
||||
-include config.mk
|
||||
@@ -32,7 +34,7 @@ DEP_FILES = $(LIB_OBJ:.o=.d) $(MAIN_OBJ:.o=.d) $(ANALYSIS_OBJ:.o=.d)
|
||||
# Artifacts
|
||||
BIN_FILE = $(BIN_DIR)/r2flutter
|
||||
STATIC_LIB = $(BUILD_DIR)/libr2flutter.a
|
||||
INC_VER = $(SRC_DIR)/../include/r2flutter/r2flutter_version.h
|
||||
INC_VER = include/r2flutter/r2flutter_version.h
|
||||
|
||||
all: $(BIN_FILE) $(INC_VER)
|
||||
|
||||
@@ -47,7 +49,7 @@ $(STATIC_LIB): $(LIB_OBJ)
|
||||
ar rcs $@ $^
|
||||
|
||||
$(BIN_FILE): $(STATIC_LIB) $(MAIN_OBJ) $(ANALYSIS_OBJ) | $(shell mkdir -p $(BIN_DIR))
|
||||
$(CC) $(CFLAGS) -o $@ $(MAIN_OBJ) $(ANALYSIS_OBJ) -L$(BUILD_DIR) -lr2flutter $(LDFLAGS) -Wl,-rpath,$(PREFIX)/lib -g
|
||||
$(CC) $(CFLAGS) -o $@ $(MAIN_OBJ) $(ANALYSIS_OBJ) -L$(BUILD_DIR) -lr2flutter $(LDFLAGS) $(LDLIBS) -Wl,-rpath,$(PREFIX)/lib -g
|
||||
|
||||
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
|
||||
@mkdir -p $(dir $@)
|
||||
@@ -71,6 +73,30 @@ user-install user-uninstall:
|
||||
$(MAKE) -C src/r2
|
||||
$(MAKE) -C src/r2 $@
|
||||
|
||||
# Binary package for r2pm -bi: bin/ and plugins/ for this r2 version and platform
|
||||
# r2 <= 6.2.2 lacks r2pm -H R2PM_TRIPLET, fallback to the host asm.* defaults
|
||||
PKG_TRIPLET := $(or $(shell r2pm -H R2PM_TRIPLET 2> /dev/null),$(shell r2 -N -qc '?e `e asm.os`-`e asm.arch`-`e asm.bits`' -- 2> /dev/null))
|
||||
BINDIST_ZIP = r2flutter-$(shell r2 -qv)-$(PKG_TRIPLET).zip
|
||||
BINDIST_DIR = $(BUILD_DIR)/bindist
|
||||
BINDIST_FILES = $(BINDIST_DIR)/bin/r2flutter $(BINDIST_DIR)/plugins/core_flutter.$(R2_LIBEXT)
|
||||
STRIP ?= strip
|
||||
|
||||
bindist: $(BIN_FILE)
|
||||
$(MAKE) $(STATIC_LIB)
|
||||
$(MAKE) -C src/r2
|
||||
rm -rf $(BINDIST_DIR) $(BINDIST_ZIP)
|
||||
mkdir -p $(BINDIST_DIR)/bin $(BINDIST_DIR)/plugins
|
||||
cp -f $(BIN_FILE) $(BINDIST_DIR)/bin/
|
||||
cp -f src/r2/core_flutter.$(R2_LIBEXT) $(BINDIST_DIR)/plugins/
|
||||
cp -f dist/README.md $(BINDIST_DIR)/
|
||||
ifeq ($(R2_LIBEXT),dylib)
|
||||
$(STRIP) -x $(BINDIST_FILES)
|
||||
codesign -f -s - $(BINDIST_FILES)
|
||||
else
|
||||
$(STRIP) --strip-unneeded $(BINDIST_FILES)
|
||||
endif
|
||||
cd $(BINDIST_DIR) && zip -r $(CURDIR)/$(BINDIST_ZIP) README.md bin plugins
|
||||
|
||||
install: uninstall
|
||||
mkdir -p "$(DESTDIR)$(PREFIX)/bin"
|
||||
cp -f bin/r2flutter "$(DESTDIR)$(PREFIX)/bin/r2flutter"
|
||||
@@ -103,6 +129,6 @@ test: $(BIN_FILE)
|
||||
@echo "Running custom testsuite"
|
||||
@python3 scripts/run_tests.py
|
||||
|
||||
.PHONY: all clean test test-r2r r2 plugin user-install user-uninstall fmt indent format
|
||||
.PHONY: all clean test test-r2r r2 plugin user-install user-uninstall fmt indent format bindist
|
||||
|
||||
-include $(DEP_FILES)
|
||||
@@ -1,151 +1,123 @@
|
||||
# r2flutter - Dart/Flutter support for Radare2
|
||||
# r2flutter
|
||||
|
||||
<p>
|
||||
<img border="0" align="left" width="125px" height="125px" src="doc/images/r2flutter500.png" />
|
||||
<p align="center">
|
||||
<img width="160" src="doc/images/r2flutter500.png" alt="r2flutter logo" />
|
||||
</p>
|
||||
|
||||
[](https://github.com/trufae/r2flutter/actions/workflows/ci.yml)
|
||||
|
||||
<br />
|
||||
The default parser uses radare2 for binary loading but does not rely on existing
|
||||
r2 analysis (`afl`/`aflj`); Dart-aware code analysis is opt-in with `-AAA`. The
|
||||
Dart parser is self-contained C code (no Dart VM headers or libs) and reads AOT
|
||||
snapshots, data images, clusters, ObjectPool records, and instruction tables
|
||||
directly.
|
||||
</p>
|
||||
**r2flutter brings Dart and Flutter AOT awareness to [radare2](https://rada.re/).**
|
||||
It reads the snapshots embedded in release builds and turns their metadata into
|
||||
useful names, addresses, strings, classes, and references for reverse
|
||||
engineering. It is available both as the `bin/r2flutter` command-line tool and
|
||||
as an `r2flutter` command inside radare2.
|
||||
|
||||
## Features
|
||||
Give it an extracted Android `libapp.so`, an Android directory containing one,
|
||||
an iOS `.app` bundle, or a direct AOT binary. AArch64 is the primary analysis
|
||||
target. The parser has in-tree layouts for Dart 2.10 through 3.12; see the
|
||||
[support matrix](doc/support.md) for platform and version details.
|
||||
|
||||
- Support iOS and Android Flutter apps
|
||||
- Accepts direct binaries, Android directories with `libapp.so`, and iOS `.app`
|
||||
bundles with `Frameworks/App.framework/App`
|
||||
- Derives names/addresses from Dart snapshots, data images, clusters,
|
||||
ObjectPool records, and instruction tables
|
||||
- Recovers snapshot headers, functions, classes, fields, string refs, xrefs,
|
||||
reconstructed ObjectPool PP data, and best-effort SBOM/component reports
|
||||
- Uses radare2 for binary loading while keeping the Dart parser self-contained
|
||||
- Prints text, JSON, or radare2 command output for supported dump actions
|
||||
## What it can do
|
||||
|
||||
## Supported Dart/Flutter Versions
|
||||
- Find Dart AOT snapshots in Flutter apps and supported standalone Dart
|
||||
containers.
|
||||
- Recover snapshot headers, functions, classes, fields, type names, strings,
|
||||
ObjectPool values, instruction-table entries, and metadata references.
|
||||
- Apply recovered names, flags, comments, classes, and code references to a
|
||||
radare2 session.
|
||||
- Emit readable text, JSON for automation, or radare2 commands for importing
|
||||
into another session.
|
||||
- Use Flutter's `--save-obfuscation-map` output to restore recovered names.
|
||||
|
||||
r2flutter supports Dart AOT snapshots from **Dart 2.10.0 to 3.10.7** (Flutter 1.22.x to 3.38.x).
|
||||
r2flutter is a metadata and analysis aid, not a Dart source-code decompiler.
|
||||
Recovery depends on what survived in the AOT snapshot, so some names and
|
||||
references are necessarily best effort.
|
||||
|
||||
The detailed support matrix, including tag-style changes and Android/iOS-specific notes, lives in [doc/support.md](doc/support.md).
|
||||
## Build and install
|
||||
|
||||
Unknown snapshot hashes default to the v3.9.2 profile with ObjectHeader tag encoding.
|
||||
You need `radare2` 6.2.2 or newer (or a git build of 6.2.1), including its
|
||||
development headers, and `r2` must be on `PATH`. The build obtains the
|
||||
required flags from `r2 -H`. Older radare2 versions lack the `ic+` attribute
|
||||
support used to register class metadata and are rejected at compile time.
|
||||
|
||||
## Building
|
||||
|
||||
This project can be used from the system shell or directly integrated inside radare2:
|
||||
|
||||
```bash
|
||||
```sh
|
||||
git clone https://github.com/trufae/r2flutter.git
|
||||
cd r2flutter
|
||||
make
|
||||
```
|
||||
|
||||
To get the plugin inside your r2 shell do that:
|
||||
The command-line tool is now ready at `bin/r2flutter`.
|
||||
|
||||
```bash
|
||||
To install the radare2 plugin for your user account:
|
||||
|
||||
```sh
|
||||
make user-install
|
||||
```
|
||||
|
||||
## Usage
|
||||
For a system-wide command-line tool and plugin, build the plugin first and
|
||||
then use the usual install prefix:
|
||||
|
||||
```bash
|
||||
Usage: bin/r2flutter [options] <libapp_path_or_dir>
|
||||
Modifiers:
|
||||
-h Show help
|
||||
-j Output in JSON format
|
||||
-n Heuristic fallback for unknown functions; may assign wrong names
|
||||
-q Compact output; suppress non-essential detail
|
||||
-r Output r2 commands for the selected action
|
||||
-v Verbose (stderr debug info)
|
||||
-vv More verbose (dump headers)
|
||||
-V Show version
|
||||
Actions:
|
||||
-A Analyze Dart snapshot and apply flags/comments
|
||||
-AA Analyze with field extraction enabled
|
||||
-AAA Run Dart-aware code analysis and recover code refs
|
||||
-c Print extracted class information
|
||||
-f Print all extracted functions (addr name)
|
||||
-H Print Dart AOT snapshot header info
|
||||
-HH Print extended snapshot header and cluster layout
|
||||
-HHH Decode selected cluster payloads for diagnostics
|
||||
-i Print instruction table entries to stdout
|
||||
-O <addr|pp+off> Decode Dart tagged/object pointer or ObjectPool PP slot
|
||||
-p Print reconstructed ObjectPool PP value
|
||||
-R Print radare2 script for snapshot analysis
|
||||
-S Print best-effort recovered SBOM/components
|
||||
-T Print string-based type names
|
||||
-x Print metadata/data-image xrefs; with -z, include string refs/ax in -r
|
||||
-z Print reliable ObjectPool-referenced strings (-q prints values only)
|
||||
-zz Print all fuzzy/carved extracted strings (-xzz includes refs/ax in -r)
|
||||
Options:
|
||||
-D <hash|version> Override Dart snapshot profile for analysis
|
||||
-l <N> Limit output to N items
|
||||
-m <file> Load Flutter obfuscation map JSON
|
||||
```sh
|
||||
make r2
|
||||
sudo make install
|
||||
```
|
||||
|
||||
`bin/r2flutter` runs one action per invocation. Directory inputs resolve Android
|
||||
first (`libapp.so`), then iOS (`Frameworks/App.framework/App`).
|
||||
## First look at an app
|
||||
|
||||
`-f` and the `-A` analysis flow skip loader-provided ELF/Mach-O stub symbols; radare2 already gets those from `RBin`, so `r2flutter` stays focused on Dart-derived metadata.
|
||||
Start with the snapshot header. It confirms that r2flutter found a Dart AOT
|
||||
image and shows the detected Dart profile:
|
||||
|
||||
`-m` consumes the JSON array emitted by Flutter/Dart `--save-obfuscation-map`. r2flutter inverts that mapping and applies it to recovered functions, instruction-table names, classes, fields, and method owners.
|
||||
|
||||
`-n` is intentionally opt-in. It consumes `package:` and `dart:` strings from the data image as a sequential fallback for otherwise unnamed functions, so it can produce plausible but incorrect names when the string order does not match the instruction table.
|
||||
|
||||
`-D` forces the Dart profile used for layout/CID decoding without patching the
|
||||
input binary. Pass either a known 32-byte snapshot hash or a supported Dart
|
||||
version such as `3.8.1`.
|
||||
|
||||
`-S` emits a best-effort recovered component report, not a complete dependency
|
||||
inventory. JSON output uses `format: "r2flutter-recovered-sbom"` and
|
||||
`complete: false`; package versions are usually `null` unless explicit packaged
|
||||
metadata such as `pubspec.lock`, `.dart_tool/package_config.json`, or framework
|
||||
`Info.plist` survived in the input bundle.
|
||||
|
||||
Dump actions honor the global format modifiers:
|
||||
|
||||
```bash
|
||||
bin/r2flutter -i test/bins/ios/Runner.app
|
||||
bin/r2flutter -j -l 16 -i test/bins/ios/Runner.app
|
||||
bin/r2flutter -r -l 16 -i test/bins/ios/Runner.app
|
||||
bin/r2flutter -jS test/bins/ios/Runner.app
|
||||
bin/r2flutter -HHH -l 36 test/bins/android/mafia/libapp.so
|
||||
bin/r2flutter -O pp+0x68 test/bins/android/mafia/libapp.so
|
||||
bin/r2flutter -qrz test/bins/ios/Runner.app
|
||||
bin/r2flutter -rz test/bins/ios/Runner.app
|
||||
bin/r2flutter -rzz test/bins/ios/Runner.app
|
||||
```sh
|
||||
bin/r2flutter -H path/to/libapp.so
|
||||
```
|
||||
|
||||
## radare2 Plugin
|
||||
Then try a small, practical extraction:
|
||||
|
||||
`make user-install` installs the core plugin command as `r2flutter`. Inside r2,
|
||||
the same modifiers apply to dump actions:
|
||||
```sh
|
||||
# Recovered Dart function entrypoints and names
|
||||
bin/r2flutter -f path/to/libapp.so
|
||||
|
||||
```text
|
||||
r2flutter -A analyze and apply flags/comments
|
||||
r2flutter -AA analyze with field extraction enabled
|
||||
r2flutter -AAA run Dart-aware code analysis and recover refs/comments
|
||||
r2flutter -C apply Dart classes, fields, methods and types
|
||||
r2flutter -D 3.8.1 override Dart snapshot profile for analysis
|
||||
r2flutter -jS print recovered components/SBOM as JSON
|
||||
r2flutter -r -p map a synthetic ObjectPool and set anal.gp/x27
|
||||
# A compact JSON header, useful for scripts
|
||||
bin/r2flutter -jH path/to/libapp.so
|
||||
|
||||
# The first 20 instruction-table entries
|
||||
bin/r2flutter -l 20 -i path/to/libapp.so
|
||||
```
|
||||
|
||||
Plugin config keys:
|
||||
Directory and bundle inputs work too. For example, pass an Android directory
|
||||
containing `libapp.so`, or an iOS `Runner.app` bundle directly.
|
||||
|
||||
- `r2flutter.mapfile`: Flutter obfuscation map JSON path
|
||||
- `r2flutter.namepool`: enable heuristic name-pool fallback
|
||||
- `r2flutter.profile`: override Dart profile by snapshot hash or Dart version
|
||||
After `make user-install`, open the same binary in radare2 and apply the Dart
|
||||
metadata to the analysis session:
|
||||
|
||||
## Dependencies
|
||||
```sh
|
||||
r2 -q path/to/libapp.so
|
||||
> r2flutter -AAA
|
||||
> afl~method
|
||||
```
|
||||
|
||||
- radare2 (with development headers)
|
||||
- pkg-config
|
||||
`-AAA` is the Dart-aware analysis pass. For a concise list of every action and
|
||||
output modifier, run `bin/r2flutter -h` or `r2flutter -h` inside radare2.
|
||||
|
||||
## Other Projects
|
||||
## Learn more
|
||||
|
||||
- https://github.com/dart-lang/sdk
|
||||
- https://github.com/worawit/blutter
|
||||
- https://github.com/zboralski/unflutter
|
||||
- https://github.com/Impact-I/reFlutter
|
||||
- [Support matrix](doc/support.md) — accepted containers, platform scope, and
|
||||
Dart layout coverage.
|
||||
- [AuthPass walkthrough](doc/tuto-authpass.md) — a complete iOS AOT analysis
|
||||
example using recovered method names in radare2.
|
||||
- [ObjectPool internals](doc/objpool.md) and [instruction-table entries](doc/its.md)
|
||||
— the two central structures behind function and constant recovery.
|
||||
- [Strings](doc/strings.md) and [cross references](doc/xrefs.md) — what can be
|
||||
recovered and how reliable each result is.
|
||||
- [Flutter obfuscation maps](doc/obfuscate.md) — restore names from a map
|
||||
produced during a Flutter build.
|
||||
|
||||
## Related projects
|
||||
|
||||
- [Dart SDK](https://github.com/dart-lang/sdk)
|
||||
- [blutter](https://github.com/worawit/blutter)
|
||||
- [unflutter](https://github.com/zboralski/unflutter)
|
||||
- [reFlutter](https://github.com/Impact-I/reFlutter)
|
||||
- [dae](https://github.com/ejfkdev/dae)
|
||||
|
||||
Released under the [MIT license](LICENSE.txt).
|
||||
@@ -0,0 +1,307 @@
|
||||
# r2flutter — Analysis & Correction Roadmap
|
||||
|
||||
Review of the library (`src/lib`), the r2 core plugin (`src/r2`), and the CLI
|
||||
(`src/tool`) for inconsistent, confusing, duplicated, or wrong assumptions when
|
||||
analyzing Flutter/Dart AOT snapshots on iOS and Android. Findings are verified
|
||||
against the source, the sample binaries in `test/bins`, real Dart SDK sources in
|
||||
`third_party/dart-sdk`, and a locally built Dart 3.12.2 AOT snapshot.
|
||||
|
||||
Maintainer priorities: **1) robust Dart-VM version identification** ·
|
||||
**2) performance** · **3) iOS/Android correctness parity**.
|
||||
|
||||
Items are ranked **P0** (do next) → **P3** (nice to have). Severity is implied by
|
||||
rank; each item states the concrete failure it causes.
|
||||
|
||||
---
|
||||
|
||||
## Status — what has been fixed (do not redo)
|
||||
|
||||
| Commit | What landed |
|
||||
|--------|-------------|
|
||||
| `d387c0b` | Unknown-hash **fingerprinting** replaces the blind hardcoded "v3.9.2 defaults" (`dart_newest_profile()` auto-tracks the profile table). `-H` reports **`version_source`** (`exact-hash`/`override`/`fingerprint`/`unknown`) and labels guessed layouts `~<ver>+ (fingerprint)`. Report and decode paths now share the same layout (previously decode used a degenerate zeroed struct). Test: `header-fingerprint`. |
|
||||
| `2391781` | Explicit **3.7/3.8/3.10/3.11/3.12 CID + layout profiles** (parsed from each release's real `class_id.h`). Also **verified**: the floored tables were already byte-for-byte correct for *every* release 2.10.0–3.12.1, so no CID value changed — the rows are explicit coverage + a regression anchor. |
|
||||
| `cd6b655` | Discovery hardening: removed the **bogus instruction-blob classification** (only data snapshots carry magic `0xf5f5dcdc`; a stray hit could poison `iso_instr`); symbol-derived addresses are no longer clobbered by scan guesses; `vaddr==0` duplicates can't erase a good address; magic hits deduped; Mach-O segments skipped. |
|
||||
| `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`). The `discovery-stripped-ios` test clones the mapped thin slice with `o:`, clears symbol/export metadata with `wv4`, and reloads it with `ooi`. |
|
||||
| `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. |
|
||||
| `33bb19b` | **P2.5**: 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. |
|
||||
| `e9fa905` | **Part of P0.2**: the read-only-data rule (`modern_walk_is_rodata`) — the six classes Dart moves into the data image when pointer compression is off (exactly `Serializer::ReadOnlyObjectType`) now correctly emit no fill records. Inert until the gate opens; see P0.2 for what still blocks it. |
|
||||
| `897a98d` | **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. |
|
||||
| `833f89a` | **P2.1**: deleted the legacy cluster walker — `deserialize_clusters`, `decode_class_cluster`, `decode_function_cluster`, `resolve_names`, the `DartClass`/`DartPoolFunction` types and the `DartCtx.classes`/`.functions` fields that only it wrote (244 lines). It had been running on every `-f`/`-A` alongside the modern scanner while contributing nothing: verified before removal by stubbing it out (50/50 outputs identical), and after by diffing 95 sample×action outputs against the previous commit. `decode_string_cluster`/`skip_generic_cluster`/`free_dart_string` stay — the legacy **class** parser in `dart_pool_classes.c` still uses them for CID_SHIFT1 samples. |
|
||||
| `f10d30d` | **P2.2**: the fake `kXxxCid` enum is gone. Only 3 of its 28 values (`Class`, `PatchClass`, `Function`) matched any real Dart release, so the legacy class parser decoded Class/Function clusters and silently skipped String/Array/Type/Mint/Double — the ones it needed for names. Its `switch` is now an `if` chain over `LegacyClusterCids`, resolved per layout via `dart_cid_get`. The two sites that fell back to a *fabricated* id when resolution failed (`dart_pool_data_image.c`, `dart_pool_it.c`) now fail closed, and a third raw `kFieldCid` comparison was resolved the same way. `builtin_type_name` keeps working on `Type.type_class_id` — a class id packed in the type flags, not a ref despite the field name — but against real ids. The `ti->kind` discriminator, which was never a snapshot id, became `DartTypeKind`. |
|
||||
| `f10d30d` | **Perf, found via P2.2**: `cid_table_for_layout` resolved the CID table by comparing version *strings* against every row on **every** `dart_cid_get` call, and that call sits per-object on some paths. A one-entry memo keyed on the layout pointer makes it a pointer compare. Synthetic `-x` 4862 → **37 ms** (131×), mafia `-f` 1008 → 550 ms, mafia `-c` 756 → 312 ms. Output unchanged on all 95 comparisons. This was already the dominant cost *before* P2.2 — more evidence for the P3.1 note that the double cluster walk was the wrong suspect. |
|
||||
| `251bec7` | **Initial `--dump-xrefs`**: real-binary `-x` tests (`xrefs-android`, `xrefs-ios`) land, so the ObjectPool-use scanner is no longer covered only by the synthetic blob. |
|
||||
| `25dc49e` | **P3.5 + two `-x` correctness bugs**: `collect_pool_uses_from_entry` read JSON keys `pdj` does not emit. `offset` (real key: `addr`) always returned 0, so *every* pool use fell back to `entry->address` — the reported address was the function start, never the referencing instruction. `opstr` (real keys: `disasm`/`opcode`) was always NULL, making two of the three parser calls duplicates. The fixed 96-op window also ran past short functions (median Dart function is 28 instructions) into their neighbours; it is now bounded by the next entry's address. After the fix 100% of pool uses lie inside the function they are attributed to (before, 100% sat at a function start, which is why the attribution check could not fail). mafia `-x` 7783 → 4738 ms. Also hardens the `dart_cid_get` memo to key on a copy of the version text, not the (freeable) layout pointer. |
|
||||
| `334c97b` | **P2.3**: `cid_table_for_layout`'s fallback for a layout with no usable version now goes through `dart_newest_profile()` instead of the literal `"3.9.2"`, so it agrees with the baseline the version fingerprinter picks for an unknown hash. Verified a no-op today (the two tables have zero differing CIDs); it stops being one the moment a release shifts the numbering. |
|
||||
| `540efcf` | **P0.2 largely landed — the modern gate is now open.** `dart_modern_is_supported_snapshot` accepts `cws == 8` **and** `CID_SHIFT1`, not just `cws == 4` / OBJECT_HEADER. The fill desync that blocked this (the roadmap's cluster-12 failure on `hello.aot`) is resolved by `modern_skip_fill_instance` (version-dependent instance fill shape) and `modern_load_rodata_strings`, which reads the six RO-data string classes from the data image where uncompressed builds put them. **Result:** iOS Runner (3.4.3) recovers **6389 method names** where it emitted `func.<addr>` placeholders before; `hello.aot` (3.11.5) recovers 1130/1325. Tests `dump-funcs-ios-runner`/`-authpass`/`dump-it-ios-runner` updated to assert real names. **Still gated:** class/field extraction (`modern_can_extract_classes` keeps `cws == 4`), so `-c` on cws=8 still shows the weak `size=0` fallback — see the trimmed P0.2. |
|
||||
| `b6ea0b8` | **Synthetic fixture reconciled** with the varint-tag switch. `6a7e01b` correctly made the legacy class parser read cluster tags as varints (Dart writes them with `Read<uint32_t>`, a varint), which broke `bins/synthetic/field_snapshot.bin` — it stores raw LE u32 tags. Rather than re-encode the blob, the parser branches on `synthetic_legacy_parse`: raw u32 for the fixture, varint for real snapshots. All tests green again. The fixture still has no generator (see P4). |
|
||||
| `02efb30` | **String comments in `-AAA` disasm**: `PP+N` ObjectPool loads that resolve to a string now emit a `; dart: string "…"` comment at the referencing instruction. New test `analysis-string-comments`. |
|
||||
| `2db2a74` | **`-AAA` PP-string comments work on iOS.** A new direct resolver (`dart_modern_collect_direct_object_pool_string_refs`) resolves ObjectPool slots to string literals straight from the cluster graph, so `flutter_analysis.c` can annotate loads without a disasm round-trip. New test `analysis-string-comments-ios` asserts an `https` string comment on Runner. (Does **not** touch `-x`, which still round-trips through `pdj` — see the P3.1 note.) |
|
||||
| `bea45d8` | **Public API rename sweep**: `dart_modern_is_supported_snapshot`→`modern_supported`, `dart_ctx_effective_version/_hash`→`dart_ctx_version/_hash`, `dart_pool_enumerate`→`dart_pool_scan`, `dart_pool_dump_pp`→`dump_pool`, `dart_pool_extract_object_pool_strings`→`extract_pool_strings`, `dart_pool_dump_entrypoint`→`dump_entry`. No behaviour change. |
|
||||
| `43671cf` / `ef2fd32` / `791547c` | Test-only (authpass tutorial uses pure r2 commands; extras/analysis output) and README. No source behaviour change. |
|
||||
| *(uncommitted)* | **P0.2 second half — class/field extraction on `cws == 8`.** Two changes: `modern_extract_classes` now calls `modern_load_rodata_strings` after the cluster walk (the string classes live in the RO-data image on uncompressed builds, so their names were unresolved — every class came out `class_NNNN size=…`), and `modern_can_extract_classes` drops the `cws == 4` gate. The fill readers already decoded varints and scaled by `compressed_word_size`, so no reader change was needed. **Result** (OBJECT_HEADER cws=8): `hello.aot` 480/501 classes with real names+sizes+fields+methods (was 0), Runner 2233/2254 (was 0). cws=4 byte-identical (mafia unchanged). Graceful fallback preserved where the modern walk can't run: 2.18.2 (`android/first`) still desyncs in fill and AuthPass is `CID_SHIFT1` (class gate still requires OBJECT_HEADER) — both byte-identical to before. Goldens `classes-ios`/`types-ios`/`xrefs-ios` regenerated to the real output. |
|
||||
|
||||
Corrections to earlier assumptions, worth remembering:
|
||||
- **The CID tables are not the problem.** Flooring is exactly correct 2.10→3.12,
|
||||
verified against SDK sources. Only a *future* CID shift needs action, and the
|
||||
generator adds a profile automatically when one appears.
|
||||
- **The `kDart*` snapshot symbols survive `strip`** — they are external/global and
|
||||
load-bearing (the engine resolves them at runtime). Symbol-less iOS is an edge
|
||||
case (obfuscators/repackagers), not the norm.
|
||||
- **The VM snapshot's canonical string cluster carries no canonical-set table**,
|
||||
unlike the isolate's. Measured on `android/mafia`: skipping one there consumes
|
||||
the next cluster's bytes and desynchronises the whole walk. This contradicts
|
||||
the SDK, where `StringDeserializationCluster::ReadAlloc` always calls
|
||||
`BuildCanonicalSetFromLayout` and both snapshots are read with
|
||||
`is_non_root_unit=false`. Unexplained; `modern_vm_strings_alloc` encodes the
|
||||
measured behaviour. Worth revisiting — it may mean the VM cluster range is
|
||||
being located slightly wrong and the strings we recover are a happy accident.
|
||||
|
||||
---
|
||||
|
||||
## The finding that reshaped priorities — now largely acted on
|
||||
|
||||
Historically **only 1 of 5 sample binaries** reached the "modern" (good) cluster
|
||||
decoder, because `dart_modern_is_supported_snapshot()` gated on
|
||||
`tag_style == OBJECT_HEADER && cws == 4`. `540efcf` opened that gate:
|
||||
|
||||
| Sample | Dart | tag_style | cws | modern **name** path | modern **class/field** path |
|
||||
|--------|------|-----------|-----|-----------------|-----------------|
|
||||
| `android/mafia` | 3.9.2 | OBJECT_HEADER | 4 | **yes** | **yes** |
|
||||
| `android/first` | 2.18.2 | OBJECT_HEADER | 8 | **yes** | no — fill desync (see below) |
|
||||
| `ios/Runner.app` | 3.4.3 | OBJECT_HEADER | 8 | **yes** | **yes** (2233/2254) |
|
||||
| `ios/AuthPass.app` | 3.2.5 | CID_SHIFT1 | 8 | **yes** | no — class gate is OBJECT_HEADER-only |
|
||||
| `macos/hello.aot` | 3.11.5 | OBJECT_HEADER | 8 | **yes** | **yes** (480/501) |
|
||||
|
||||
Name recovery runs on all five; class/field extraction now runs on every
|
||||
**OBJECT_HEADER** sample regardless of pointer width. Two hold-outs remain, both
|
||||
falling back gracefully (byte-identical to the old string-scan output):
|
||||
- **`android/first` (2.18.2)**: the modern fill walk desyncs at cluster 0
|
||||
(`cid=92`) — a genuinely version-dependent fill shape in a pre-3.0 release, the
|
||||
same class of problem P0.1 addressed for CIDs but for fill *layout*. This is the
|
||||
only remaining fill-desync case in the corpus.
|
||||
- **`ios/AuthPass.app` (3.2.5, CID_SHIFT1)**: `modern_can_extract_classes` still
|
||||
requires OBJECT_HEADER. The name path handles CID_SHIFT1; extending class
|
||||
extraction to it is untried.
|
||||
|
||||
---
|
||||
|
||||
## P0 — correctness bugs with no test coverage
|
||||
|
||||
### P0.2 (done for OBJECT_HEADER; two edge cases left)
|
||||
Class/field extraction on `cws == 8` **landed** (see status table): the gate
|
||||
dropped and `modern_load_rodata_strings` now runs on the class path. `hello.aot`
|
||||
and Runner recover real classes, fields and methods. Two follow-ups remain, both
|
||||
lower-value than the main win and both currently falling back cleanly:
|
||||
|
||||
1. **2.18.2 fill desync** (`android/first`). The modern walk fails at cluster 0
|
||||
(`cid=92`, an `Array`/string cluster in that era). Some pre-3.0 fill shape
|
||||
differs from `rules[]`. To fix: bisect the first divergent cluster by diffing
|
||||
per-class `ReadFill` between an old SDK and current (`third_party` has both),
|
||||
using byte-per-object as the oracle, then make the affected rule version-aware
|
||||
— same method the roadmap sketched for the (now-resolved) cluster-12 case.
|
||||
Guard rails: `-c` must stay byte-identical on the working samples.
|
||||
2. **CID_SHIFT1 class extraction** (`ios/AuthPass.app`, 3.2.5).
|
||||
`modern_can_extract_classes` requires OBJECT_HEADER; the name path already
|
||||
handles CID_SHIFT1, so the cluster walk works — the untested part is whether
|
||||
the class *fill* reader is correct under the shifted tag encoding. Try opening
|
||||
it and check AuthPass `-c` against a known class list.
|
||||
|
||||
### P0.3 Test corpus does not cover the decoders we ship
|
||||
Progress: iOS **name** recovery on cws=8 is now asserted
|
||||
(`dump-funcs-ios-runner`/`-authpass`, `dump-it-ios-runner`), and `-x` has
|
||||
real-binary coverage (`xrefs-android`/`-ios`). Remaining gaps:
|
||||
- a **Dart 3.4/3.5-era compressed-pointer** app (P0.1 is fixed but still has no
|
||||
end-to-end regression test; it was verified only by resolving `rules[]` against
|
||||
each layout in isolation);
|
||||
- cws=8 class/field extraction is now covered on both paths: iOS (`classes-ios`,
|
||||
`types-ios` on Runner) and macOS/standalone (`dump-classes-macos` on
|
||||
`hello.aot`, added alongside this note). The macOS golden is a real regression
|
||||
anchor — reverting the `cws==8` gate fails all six of its assertions;
|
||||
- more **iOS** binaries generally, ideally real obfuscated/stripped release
|
||||
`App`s (also broadens the `0716a95` structural path, per `COMMIT2.md`);
|
||||
- a **32-bit ARM** app if one can be found (validates P1.2).
|
||||
The remaining gaps (3.4/3.5-era compressed, obfuscated iOS, 32-bit) all need
|
||||
apps **collected externally** — the installed Dart SDK is 3.12.2, so a locally
|
||||
built AOT would exercise the modern decoder but *not* the old CID layout P0.1
|
||||
corrected, which is the one that still lacks an end-to-end test.
|
||||
|
||||
---
|
||||
|
||||
## P1 — correctness, lower prevalence or already-guarded
|
||||
|
||||
### 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`
|
||||
math (`:1201-1208`) and the synthetic-PP builder (`:2243-2262`) are wrong on
|
||||
32-bit ARM, so `PP+N` lookups resolve to the wrong entry.
|
||||
- Entry-point stride hardcodes `*4` (`dart_pool_clusters.c:164`,
|
||||
`dart_pool_classes.c:170`, `dart_pool_it.c:349,399`).
|
||||
- `-AAA` (`src/r2/flutter_analysis.c:104-129`) is entirely arm64 (PP=`x27`,
|
||||
`x15/x29`, AAPCS64 clobber) with **no arch guard**; on arm32/x64 it emits wrong
|
||||
PP annotations instead of bailing. Neither frontend checks `asm.arch`/`bits`.
|
||||
**Fix:** gate on arch, derive the PP register, or refuse with a clear message.
|
||||
|
||||
### P1.3 Plugin/CLI frontend drift (AGENTS.md violations)
|
||||
Two hand-maintained parsers: CLI getopt (`main.c:118`) vs plugin
|
||||
(`core_flutter.c:192+`).
|
||||
- **`-r` is missing in the plugin** (it only has `-*`), directly violating
|
||||
AGENTS.md ("-q, -j and -r flags are modifiers"). `r2flutter -f -r` errors.
|
||||
- Plugin **silently no-ops under `r2 -n`** (`dart_app_new_from_core` returns NULL
|
||||
via `R_UNWRAP4(core,bin,cur,file)`), so `-f/-A/-R` do nothing while `-H/-z/-i`
|
||||
work — and `r2 -n` is the workflow AGENTS.md recommends.
|
||||
- CLI help advertises `[jr*]`/`-c*` but `*` is not in the getopt string.
|
||||
- Plugin help omits `-q` and `-f <N>`; `-C` is plugin-only; `-E` is CLI-only;
|
||||
`-f <N>` grammar differs (CLI treats the number as the path).
|
||||
**Fix:** one shared parser/dispatch in the library so the modifier contract holds
|
||||
once. Cheap, visible, and unblocks consistent `-q/-j/-r` everywhere.
|
||||
|
||||
### P1.4 iOS `LC_NOTE` (`__dart_app_snap`) container is CLI-only
|
||||
`main.c:84-97` extracts the embedded inner Mach-O; the plugin has no equivalent,
|
||||
so `r2flutter -H` inside r2 analyses the outer VM shell and fails, and
|
||||
`dctx.container_*` stays empty. **Fix:** move detection/extraction into the
|
||||
library so both frontends share it.
|
||||
|
||||
### P1.5 Single-data-snapshot aliasing
|
||||
`pick_vm_iso_by_size` (`dart_pool_discovery.c:25-49`) with one snapshot assigns
|
||||
the same address to both `vm_data` and `iso_data` (visible on the synthetic bin:
|
||||
both `0x4`), so one is parsed as the other. The `sbom-synthetic` golden currently
|
||||
*encodes* this, so fixing it means deciding the right semantics (probably: lone
|
||||
snapshot is the isolate, leave `vm_data = 0`) and updating that test.
|
||||
|
||||
---
|
||||
|
||||
## P2 — duplication, dead code, structural debt
|
||||
|
||||
### P2.4 CID/layout data still lives in three places
|
||||
`offsets.json` (source of truth, generated from SDK), `dart_cid.c` `cid_tables[]`
|
||||
(generated ✔), and `dart_offsets.h` `dart_hash_entries[]` — which only carries
|
||||
`hash → compressed_word_size`, a value already derivable from the profile and then
|
||||
overridden by the feature flags anyway (cws is decided three times, flags win).
|
||||
**Fix:** drop the hash→cws table or derive it.
|
||||
|
||||
### P2.6 `data_image_base` recomputed in ≥3 places with a magic-16 fallback
|
||||
`dart_object.c:304-305`, `dart_pool_classes.c:1646-1655`, plus IT callers each do
|
||||
`snapshot + align_up(total_len, layout->max_alignment ?: 16)`. Extract one helper
|
||||
and confirm alignment is genuinely derived (it can be raised to 64) rather than
|
||||
silently defaulted.
|
||||
|
||||
### P2.7 `-x`/`-z` special-case in both frontends (partly overtaken)
|
||||
`core_flutter.c` (`case 'x'`) and `main.c` set `string_refs = true` for *any*
|
||||
`-x`. The dispatch is now live (`case 'x' → dart_pool_dump_xrefs`), so the
|
||||
"dead branch" framing no longer holds — but the unconditional `string_refs`
|
||||
plus the `if (cmd->action != 'z')` guard still reads as accidental coupling
|
||||
between `-x` and `-z`. Re-audit against current line numbers before acting;
|
||||
lower priority than when first written.
|
||||
|
||||
---
|
||||
|
||||
## P3 — performance
|
||||
|
||||
Read caching (`read_mem`, `dart_pool_snapshot.c:10-44`) already turns per-byte
|
||||
varint reads into a 1 MB sliding window. Remaining hotspots, largest first:
|
||||
|
||||
### P3.1 Redundant full passes over the same regions
|
||||
A single `-f`/`-A` walks the cluster stream twice and the data image 2–3 times.
|
||||
|
||||
**This was claimed to be the biggest perf win; measured, it is not.** Removing
|
||||
the whole legacy cluster walk changes runtime by ~1%, inside noise (mafia `-f`
|
||||
993 → 1005 ms; AuthPass `-f` 616 → 618 ms) — it bails out early rather than
|
||||
re-walking the stream.
|
||||
|
||||
The real cost was elsewhere, and profiling found it: `dart_cid_get` re-resolved
|
||||
the CID table by version-string comparison on every call (see the status table).
|
||||
Memoising it took mafia `-f` from 1008 → 550 ms and the synthetic `-x` from 4862
|
||||
→ 37 ms — far more than P2.1 could ever have given. **Profile before spending
|
||||
time here.**
|
||||
|
||||
`-x` was the next slowest; `25dc49e` fixed its correctness bugs and the window
|
||||
bound cut mafia `-x` 7783 → 4738 ms. The `pdj` JSON round-trip is now gone too
|
||||
(*uncommitted*): `collect_pool_uses_from_entry` decodes each instruction with
|
||||
`r_core_anal_op` and feeds `op->mnemonic` to the existing operand parsers — the
|
||||
same disassembly text, minus the JSON build/parse, matching the `-AAA` path in
|
||||
`flutter_analysis.c`. Output byte-identical on all 5 samples; mafia `-x`
|
||||
6052 → 3842 ms (~1.6×). AuthPass gains less (12776 → 11398 ms) — it spends most
|
||||
of its `-x` time in name resolution, not disasm. A full structured-operand
|
||||
rewrite (reading `RAnalValue` instead of `op->mnemonic` text) could skip capstone
|
||||
text rendering too, but arm64 operand modelling is unreliable enough that it
|
||||
would need per-encoding output diffing — not worth the risk for the remainder.
|
||||
|
||||
**The 4096-entry scan cap is justified, not a bug — measured.** Removing it
|
||||
(scanning all 37258 IT entries) takes mafia `-x` from ~3.8 s to **55.8 s**; the
|
||||
per-instruction disasm dominates and there is no cheap way around it. The real
|
||||
wart is that `-l` conflates two limits: the output-line cap (`append_xref_info`)
|
||||
*and* the pool-use entry-scan cap (`collect_pool_uses`, default 4096 when no
|
||||
`-l`). Passing `-l 100` to trim output also silently narrows the scan to 100
|
||||
entries. Untangling them (a separate scan-depth control) belongs with the
|
||||
frontend-parser work (P1.3), not here.
|
||||
|
||||
### P3.2 Brute-force InstructionTable header search
|
||||
`dart_pool_it.c:233-330` probes `{16,0,8,12} × delta -64..64 step 4`, then scans
|
||||
the whole data image every 16 bytes (`:209-231`), then a third linear scan
|
||||
`+0x40..+0x40000 step 8` (`:289-320`); `decode_pool_and_emit` additionally retries
|
||||
`dart_it_emit_varint` across `delta -64..64`. O(image) per lookup because the
|
||||
header location isn't derived. **Fix:** compute it from the layout — the Image
|
||||
header format is now understood (see `COMMIT2.md`: word0 `ImageSize`, word1
|
||||
`InstructionsSectionOffset`, 64-byte header), which should make the IT header
|
||||
directly addressable.
|
||||
|
||||
### P3.3 Per-value snapshot-header re-parse
|
||||
`dart_object.c:296-312` calls `dart_snapshot_header_read(ctx, ctx->iso_data, …)`
|
||||
for *every* scalar value decode just to recompute `data_base`. Cache on the ctx.
|
||||
|
||||
### P3.4 Read-cache tail cliff + forward-only thrash
|
||||
The 1 MB refill (`:37`) needs the whole range readable, so the last <1 MB of each
|
||||
section degrades to one syscall per byte; the window is forward-only, so name
|
||||
resolution alternating between the cluster stream and the string heap refills
|
||||
repeatedly. Consider a small multi-window/LRU cache and a short-read-tolerant refill.
|
||||
|
||||
---
|
||||
|
||||
## P4 — test quality (beyond the corpus gaps in P0.3)
|
||||
|
||||
- **The synthetic fixture has no generator and is fragile.** `bins/synthetic/field_snapshot.bin`
|
||||
is a hand-authored blob. It has already forced two special cases as the parser
|
||||
was corrected: P2.2 had to renumber its cluster tags to real CIDs, and `b6ea0b8`
|
||||
had to branch the tag reader on `synthetic_legacy_parse` because it stores raw
|
||||
LE u32 tags while real snapshots use varints. Every future layout/encoding fix
|
||||
risks a third. **Write a generator** so the fixture tracks the parser instead of
|
||||
pinning obsolete conventions; the `synthetic_legacy_parse` branch could then go.
|
||||
- **Obfuscation-map tests are tautological**: `test/db/cmd/obf-map`,
|
||||
`test/custom/obf-funcs-ios.json`, `obf-it-android.json` load a map whose keys
|
||||
match nothing in the sample, so output is identical to the no-`-m` run. They
|
||||
prove loading doesn't crash, never that renaming works. Add a fixture whose
|
||||
keys actually occur.
|
||||
- `analyze-aaa-android.json` only greps `"Flutter analysis:"` — passes even if
|
||||
nothing is recovered. Assert a recovered count/name.
|
||||
- Header tests pin entire JSON blobs incl. full CID tables: deterministic but
|
||||
brittle (one new field broke ~10 tests during `d387c0b`). Consider
|
||||
field-subset assertions like the `test/custom` specs already use.
|
||||
- Missing behaviours: plugin `-r`, plugin under `r2 -n`, gapped-layout structural
|
||||
discovery (macOS, see `COMMIT2.md`), stripped **ELF**.
|
||||
- Directory-form input only recognises Android `libapp.so` / iOS
|
||||
`App.framework/App` (`main.c:62-74`); `-H <dir-with-hello.aot>` errors "does
|
||||
not exist". Broaden the resolver or document it.
|
||||
|
||||
---
|
||||
|
||||
## Suggested execution order
|
||||
|
||||
1. **P1.3** — shared frontend parser (fixes the `-r`/`r2 -n` AGENTS.md
|
||||
violations). While there, untangle `-l` for `-x`: give the pool-use scan its
|
||||
own depth control instead of reusing the output-line limit (see P3.1).
|
||||
2. **P0.2 edge cases** — the 2.18.2 fill desync and CID_SHIFT1 class extraction
|
||||
(both above). Smaller than the main win; do only if those eras matter.
|
||||
3. **P0.3** — a 3.4/3.5-era compressed sample for the still-untested P0.1 fix
|
||||
(needs an externally-collected app; the local SDK is 3.12.2).
|
||||
4. **P1.2** — arch gating.
|
||||
5. **Write a synthetic-fixture generator** (see P4) so parser fixes stop needing
|
||||
blob special-cases.
|
||||
6. **P3.2** — now a *lower* suspect than it looked; it never showed up in the
|
||||
`-x` profile. Remaining P2 cleanups and test hardening.
|
||||
|
||||
Done since last revision: P0.2 name+class paths (`540efcf`, `f85bd3b`), the
|
||||
macOS class regression test (`75ad39a`), and the `-x` `r_anal_op` rewrite
|
||||
(uncommitted).
|
||||
|
||||
`COMMIT2.md` tracks the narrower follow-ups left over from the discovery work
|
||||
(gapped layouts, more iOS fixtures, stripped ELF, P1.5 semantics).
|
||||
@@ -127,12 +127,12 @@ done
|
||||
: ${INSTALL_PROGRAM_STRIP:=${INSTALL} -m 755 -s}
|
||||
: ${INSTALL_MAN:=${INSTALL} -m 444}
|
||||
: ${INSTALL_LIB:=${INSTALL} -m 755 -c}
|
||||
PKGNAME='r2flutter' ; VERSION='0.2.0' ; VERSION_MAJOR=0; VERSION_MINOR=2; VERSION_PATCH=0; VERSION_NUMBER=00200; CONTACT_MAIL="[email protected]" ; CONTACT_NAME="pancake" ; CONTACT="pancake <[email protected]>" ;
|
||||
PKGNAME='r2flutter' ; VERSION='0.6.0' ; VERSION_MAJOR=0; VERSION_MINOR=6; VERSION_PATCH=0; VERSION_NUMBER=00600; CONTACT_MAIL="[email protected]" ; CONTACT_NAME="pancake" ; CONTACT="pancake <[email protected]>" ;
|
||||
}
|
||||
|
||||
show_usage() {
|
||||
cat <<EOF2
|
||||
'configure' configures r2flutter-0.2.0 to adapt to many kinds of systems.
|
||||
'configure' configures r2flutter-0.6.0 to adapt to many kinds of systems.
|
||||
|
||||
Usage: ./configure [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -214,10 +214,10 @@ ocho() {
|
||||
|
||||
show_version() {
|
||||
if [ "$QUIET" = 1 ]; then
|
||||
echo "0.2.0"
|
||||
echo "0.6.0"
|
||||
exit 0
|
||||
fi
|
||||
echo "r2flutter-0.2.0 configuration script done with acr v2.2.8.
|
||||
echo "r2flutter-0.6.0 configuration script done with acr v2.2.8.
|
||||
The 'Free Software Foundation' message is only for autodetection.
|
||||
Originally written by pancake <nopcode.org>."
|
||||
exit 0
|
||||
@@ -246,11 +246,10 @@ case $flag in
|
||||
show_version ; ;;
|
||||
-r|--r|--report)
|
||||
echo "PKGNAME: r2flutter"
|
||||
echo "VERSION: 0.2.0"
|
||||
echo "VERSION: 0.6.0"
|
||||
echo "AUTHOR: pancake"
|
||||
echo "EMAIL: [email protected]"
|
||||
echo "LANGS: c"
|
||||
echo "PKGCONFIG: r_core"
|
||||
echo "FLAGS: --with-precompiled-agent"
|
||||
exit 0
|
||||
;;
|
||||
@@ -321,7 +320,7 @@ parse_options "$1"
|
||||
shift
|
||||
done
|
||||
|
||||
ENVWORDS="MANDIR DESCRIPTION INFODIR LIBDIR INCLUDEDIR LOCALSTATEDIR ETCDIR SYSCONFDIR DATADIR DOCDIR LIBEXECDIR SBINDIR BINDIR EPREFIX PREFIX SPREFIX TARGET HOST BUILD INSTALL INSTALL_LIB INSTALL_MAN INSTALL_PROGRAM INSTALL_PROGRAM_STRIP INSTALL_DIR INSTALL_SCRIPT INSTALL_DATA HOST_OS HOST_CPU BUILD_OS BUILD_CPU TARGET_OS TARGET_CPU VERSION VERSION_MAJOR VERSION_MINOR VERSION_PATCH VERSION_NUMBER PKGCFG_LIBDIR PKGCFG_INCDIR PKGNAME VPATH CONTACT CONTACT_NAME CONTACT_MAIL PKGCONFIG CC CFLAGS CPPFLAGS LDFLAGS HAVE_LANG_C R2_VERSION PRECOMPILED_AGENT R2_CFLAGS R2_LDFLAGS HAVE_PKGCFG_R_CORE"
|
||||
ENVWORDS="MANDIR DESCRIPTION INFODIR LIBDIR INCLUDEDIR LOCALSTATEDIR ETCDIR SYSCONFDIR DATADIR DOCDIR LIBEXECDIR SBINDIR BINDIR EPREFIX PREFIX SPREFIX TARGET HOST BUILD INSTALL INSTALL_LIB INSTALL_MAN INSTALL_PROGRAM INSTALL_PROGRAM_STRIP INSTALL_DIR INSTALL_SCRIPT INSTALL_DATA HOST_OS HOST_CPU BUILD_OS BUILD_CPU TARGET_OS TARGET_CPU VERSION VERSION_MAJOR VERSION_MINOR VERSION_PATCH VERSION_NUMBER PKGCFG_LIBDIR PKGCFG_INCDIR PKGNAME VPATH CONTACT CONTACT_NAME CONTACT_MAIL CC CFLAGS CPPFLAGS LDFLAGS HAVE_LANG_C R2_VERSION R2_CFLAGS R2_LDFLAGS R2_LIBS PRECOMPILED_AGENT"
|
||||
|
||||
create_environ
|
||||
|
||||
@@ -336,37 +335,6 @@ ocho "checking for working directories... ${WODIS}"
|
||||
ocho "using prefix '${PREFIX}'"
|
||||
ACR_RMFILES=" test.c a.out a.exe"
|
||||
|
||||
ochof "checking for pkg-config... "
|
||||
ENVWORDS="${ENVWORDS} HAVE_PKGCONFIG"
|
||||
if [ -n "$PKGCONFIG" ]; then
|
||||
if [ -x "$PKGCONFIG" ]; then
|
||||
_HAVE_PKGCONFIG="${PKGCONFIG}"
|
||||
else
|
||||
ocho "PKGCONFIG env is not executable"
|
||||
PKGCONFIG=""
|
||||
fi
|
||||
else
|
||||
IFS=:
|
||||
for A in ${PATH} ; do
|
||||
if [ -x "${A}/pkg-config" ]; then
|
||||
PKGCONFIG="${A}/pkg-config"
|
||||
elif [ -x "${A}/pkgconf" ]; then
|
||||
PKGCONFIG="${A}/pkgconf"
|
||||
else
|
||||
continue
|
||||
fi
|
||||
break
|
||||
done
|
||||
unset IFS
|
||||
fi
|
||||
if [ -n "${PKGCONFIG}" ]; then
|
||||
ocho ${PKGCONFIG}
|
||||
HAVE_PKGCONFIG=1
|
||||
else
|
||||
HAVE_PKGCONFIG=0
|
||||
echo "Cannot find pkg-config or pkgconf in PATH" >&2
|
||||
exit 1
|
||||
ocho no; fi
|
||||
COMPILER=CC
|
||||
ochof "checking for c compiler... "
|
||||
HAVE_LANG_C=1
|
||||
@@ -390,21 +358,33 @@ else
|
||||
echo "not found"
|
||||
echo 'required'; exit 1
|
||||
fi
|
||||
if [ -z "${PKGCONFIG}" ]; then pkg-config --version >/dev/null 2>&1 ; if [ 0 = 0 ]; then PKGCONFIG=pkg-config ; else PKGCONFIG=pkgconf ; fi; fi
|
||||
type ${PKGCONFIG} > /dev/null 2>&1 || echo "ERROR: Cannot find valid PKGCONFIG, pkg-config or pkgconf in PATH"
|
||||
echo 'Using PKGCONFIG: '${PKGCONFIG}
|
||||
printf 'checking pkg-config flags for r_core... '
|
||||
tmp=`${PKGCONFIG} --cflags r_core 2>/dev/null`
|
||||
if [ $? != 0 ]; then echo no ; HAVE_PKGCFG_R_CORE=0;
|
||||
R2_CFLAGS='';R2_LDFLAGS='';
|
||||
echo 'This package is required'
|
||||
exit 1
|
||||
else
|
||||
R2_CFLAGS=$tmp;
|
||||
tmp=`${PKGCONFIG} --libs r_core 2>/dev/null`
|
||||
printf "checking exec r2 -H R2_CFLAGS... "
|
||||
R2_CFLAGS="`r2 -H R2_CFLAGS 2>/dev/null`"
|
||||
if [ $? = 0 ]; then
|
||||
echo yes; HAVE_PKGCFG_R_CORE=1;
|
||||
R2_LDFLAGS=$tmp; fi; fi
|
||||
echo "ok"
|
||||
else
|
||||
R2_CFLAGS=""
|
||||
echo "not found"
|
||||
echo 'required'; exit 1
|
||||
fi
|
||||
printf "checking exec r2 -H R2_LDFLAGS... "
|
||||
R2_LDFLAGS="`r2 -H R2_LDFLAGS 2>/dev/null`"
|
||||
if [ $? = 0 ]; then
|
||||
echo "ok"
|
||||
else
|
||||
R2_LDFLAGS=""
|
||||
echo "not found"
|
||||
echo 'required'; exit 1
|
||||
fi
|
||||
printf "checking exec r2 -H R2_LIBS... "
|
||||
R2_LIBS="`r2 -H R2_LIBS 2>/dev/null`"
|
||||
if [ $? = 0 ]; then
|
||||
echo "ok"
|
||||
else
|
||||
R2_LIBS=""
|
||||
echo "not found"
|
||||
echo 'required'; exit 1
|
||||
fi
|
||||
SEDFLAGS=" -e '"
|
||||
COUNT=0
|
||||
for A in ${ENVWORDS} ; do
|
||||
|
||||
+4
-5
@@ -1,17 +1,16 @@
|
||||
PKGNAME r2flutter
|
||||
VERSION 0.2.0
|
||||
VERSION 0.6.0
|
||||
CONTACT pancake ; [email protected]
|
||||
|
||||
USE_PKGCONFIG!
|
||||
|
||||
LANG_C!
|
||||
|
||||
EXEC! R2_VERSION r2 -v ;
|
||||
EXEC! R2_CFLAGS r2 -H R2_CFLAGS ;
|
||||
EXEC! R2_LDFLAGS r2 -H R2_LDFLAGS ;
|
||||
EXEC! R2_LIBS r2 -H R2_LIBS ;
|
||||
|
||||
ARG_WITH PRECOMPILED_AGENT precompiled-agent do not require node/npm ;
|
||||
|
||||
PKGCFG! R2_CFLAGS R2_LDFLAGS r_core
|
||||
|
||||
REPORT R2_VERSION ;
|
||||
|
||||
SUBDIRS ./config.mk ;
|
||||
Vendored
+36
@@ -0,0 +1,36 @@
|
||||
# r2flutter binary release
|
||||
|
||||
This package contains:
|
||||
|
||||
* `bin/r2flutter` — the standalone command line tool
|
||||
* `plugins/core_flutter.{so,dylib,dll}` — the radare2 core plugin
|
||||
|
||||
## Installing
|
||||
|
||||
Copy the tool anywhere in your `$PATH` and drop the plugin into the radare2
|
||||
plugin directory reported by r2:
|
||||
|
||||
```sh
|
||||
r2 -H R2_USER_PLUGINS # per user, no root needed
|
||||
r2 -H R2_LIBR_PLUGINS # system wide
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```sh
|
||||
mkdir -p "$(r2 -H R2_USER_PLUGINS)"
|
||||
cp plugins/core_flutter.* "$(r2 -H R2_USER_PLUGINS)"
|
||||
cp bin/r2flutter /usr/local/bin
|
||||
```
|
||||
|
||||
On Windows the plugin directory is usually `%HOMEPATH%\.local\share\radare2\plugins`.
|
||||
|
||||
## Checking the installation
|
||||
|
||||
```sh
|
||||
r2 -qc 'Lc~r2flutter' --
|
||||
r2flutter -V
|
||||
```
|
||||
|
||||
The plugin must match the radare2 version it was built against, otherwise r2
|
||||
refuses to load it.
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
PACKAGE=r2flutter
|
||||
VERSION?=$(shell ../../configure -qV)
|
||||
DEPENDS=radare2
|
||||
SECTION=user/shell
|
||||
PRIORITY=optional
|
||||
MAINTAINER=pancake <[email protected]>
|
||||
# arch
|
||||
UNAMEM=$(shell uname -m)
|
||||
ifeq ($(UNAMEM),x86_64)
|
||||
ARCH?=amd64
|
||||
else
|
||||
ARCH?=arm64
|
||||
endif
|
||||
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
include ./CONFIG
|
||||
|
||||
PWD=$(shell pwd)
|
||||
|
||||
all: deb
|
||||
|
||||
deb:
|
||||
$(MAKE) root
|
||||
$(MAKE) build
|
||||
|
||||
root:
|
||||
rm -rf root
|
||||
$(MAKE) -C ../.. install DESTDIR=$(PWD)/root
|
||||
|
||||
build:
|
||||
mkdir -p root/DEBIAN
|
||||
echo "Package: $(PACKAGE)" > root/DEBIAN/control
|
||||
echo "Version: $(VERSION)" >> root/DEBIAN/control
|
||||
echo "Section: $(SECTION)" >> root/DEBIAN/control
|
||||
echo "Priority: $(PRIORITY)" >> root/DEBIAN/control
|
||||
echo "Architecture: $(ARCH)" >> root/DEBIAN/control
|
||||
echo "Depends: $(DEPENDS)" >> root/DEBIAN/control
|
||||
echo "Maintainer: $(MAINTAINER)" >> root/DEBIAN/control
|
||||
echo "Description: r2flutter plugin for radare2" >> root/DEBIAN/control
|
||||
dpkg-deb --root-owner-group --build root $(PACKAGE)_$(VERSION)_$(ARCH).deb
|
||||
|
||||
clean:
|
||||
rm -rf root *.deb
|
||||
|
||||
.PHONY: all deb root build clean
|
||||
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
meson setup build-windows --buildtype=release
|
||||
meson compile -C build-windows
|
||||
|
||||
$r2v = if ($env:R2V) { $env:R2V } else { (r2 -qv).Trim() }
|
||||
$root = 'build-windows/bindist'
|
||||
$archive = "r2flutter-$r2v-windows-x86-64.zip"
|
||||
Remove-Item -Recurse -Force $root -ErrorAction SilentlyContinue
|
||||
Remove-Item -Force $archive -ErrorAction SilentlyContinue
|
||||
New-Item -ItemType Directory -Force $root/bin,$root/plugins | Out-Null
|
||||
Copy-Item build-windows/r2flutter.exe $root/bin/
|
||||
Copy-Item build-windows/core_flutter.dll $root/plugins/
|
||||
Copy-Item dist/README.md $root/
|
||||
Compress-Archive -Path $root/* -DestinationPath $archive -Force
|
||||
@@ -0,0 +1,39 @@
|
||||
# DAE Gap Implementation TODO
|
||||
|
||||
This roadmap tracks the high-confidence Dart AOT format and restoration gaps
|
||||
identified by comparing `third_party/dae` with r2flutter. IDs are stable so an
|
||||
item can be selected by name in follow-up work.
|
||||
|
||||
Items are ordered by dependency: stream framing and codecs first, versioned
|
||||
cluster layouts next, then metadata/value restoration and profile detection.
|
||||
|
||||
- [x] **R2F-DAE-001 — Parse version-specific snapshot headers**
|
||||
- Read the profile-defined header field count and semantics.
|
||||
- Preserve canonical-cluster and field-table fields where present.
|
||||
- Commit: `Parse Version-Specific Snapshot Headers`
|
||||
- [x] **R2F-DAE-002 — Add versioned snapshot reference decoding**
|
||||
- Use unsigned LEB128 references through Dart 2.17.
|
||||
- Use the `ReadRefId` +128 encoding from Dart 2.18 onward.
|
||||
- Commit: `Add Versioned Snapshot Reference Decoding`
|
||||
- [x] **R2F-DAE-003 — Handle versioned ObjectPool entry bits**
|
||||
- Model low-seven-bit, swapped low-seven-bit, and modern bitfield eras.
|
||||
- Commit: `Handle Versioned ObjectPool Entry Bits`
|
||||
- [x] **R2F-DAE-004 — Parse versioned Code cluster layouts**
|
||||
- Profile Code reference counts, text-offset deltas, and leading refs.
|
||||
- Commit: `Parse Versioned Code Cluster Layouts`
|
||||
- [x] **R2F-DAE-005 — Support Dart 3.13 single snapshots**
|
||||
- Add hashes/CIDs and merged VM/isolate snapshot handling.
|
||||
- Add the alternate symbols and 3.13 Class, Code, and Closure layouts.
|
||||
- Test complete and truncated combined snapshots, including partial-fill failure reporting.
|
||||
- Commit: `Support Dart 3.13 Single Snapshots`
|
||||
- [x] **R2F-DAE-006 — Retain serialized class layout metadata**
|
||||
- Keep class ID, library/super references, next-field offset, and bitmap.
|
||||
- Test ordinary, top-level, and truncated Class fills.
|
||||
- Commit: `Retain Serialized Class Layout Metadata`
|
||||
- [x] **R2F-DAE-007 — Retain snapshot object value graph**
|
||||
- Preserve Instance, Array, Map, and Set values by object reference.
|
||||
- Expose bounded recursive values through the object inspector.
|
||||
- Commit: `Retain Snapshot Object Value Graph`
|
||||
- [x] **R2F-DAE-008 — Probe snapshot layouts for unknown hashes**
|
||||
- Validate candidate layouts structurally instead of selecting the newest.
|
||||
- Commit: `Probe Snapshot Layouts for Unknown Hashes`
|
||||
+11
@@ -49,6 +49,15 @@ bin/r2flutter -r -l 16 -i test/bins/ios/Runner.app
|
||||
|
||||
`-l` is useful because real apps can have thousands of entries.
|
||||
|
||||
Entrypoint and instruction-image marking:
|
||||
|
||||
```bash
|
||||
bin/r2flutter -E test/bins/ios/Runner.app
|
||||
bin/r2flutter -rE test/bins/ios/Runner.app
|
||||
```
|
||||
|
||||
`-E` reports the first decoded code entry from the InstructionTable. This is different from the loader symbol for `_kDartVmSnapshotInstructions`, which points at the VM instruction image data, not a normal program entrypoint. With `-r`, `-E` also emits `Cd 4 ...` metadata commands for the VM and isolate instruction images so radare2 renders them as dword arrays instead of treating the snapshot symbol as code.
|
||||
|
||||
## Fields Exposed By r2flutter
|
||||
|
||||
Plain output shows:
|
||||
@@ -65,9 +74,11 @@ JSON also includes:
|
||||
- `canonical_stack_map_entries_offset`
|
||||
- per-entry `pc_offset`
|
||||
- per-entry `stack_map_offset`
|
||||
- `-E` JSON includes `entrypoint`, `index`, `code_index`, and instruction-image sizes when known
|
||||
|
||||
## Notes
|
||||
|
||||
- Output is written to `stdout`, not `stderr`.
|
||||
- `-j` and `-r` now affect `-i` the same way they affect the other dump commands.
|
||||
- `-j`, `-q`, and `-r` also affect `-E`; use `-qE` when only the address is needed.
|
||||
- The current parser finds the table by scanning the mapped data image for a valid `InstructionsTable::Data` header when the snapshot's `it_off` field does not point exactly at the payload start.
|
||||
+957
-17
File diff suppressed because it is too large.
Load diff
+18
-9
@@ -17,8 +17,8 @@ instruction tables, and strings directly.
|
||||
| Mach-O details | Thin Mach-O64 and fat Mach-O/fat64; arm64 slice is preferred. LC_NOTE owner `__dart_app_snap` is detected and the embedded Dart Mach-O payload is extracted to a temporary file. |
|
||||
| Architecture | ARM64/AArch64 is the real target. Instruction-table entrypoints are based on ARM64 code offsets, PP tracking uses `x27`, and the radare2 analysis pass understands AArch64 registers/op metadata. |
|
||||
| Snapshot kind | Clustered Dart AOT snapshots with magic `0xdcdcf5f5`, 32-byte snapshot hashes, feature strings, and unsigned varint header fields. |
|
||||
| Dart layouts | In-tree profiles cover Dart `2.10.0` through `3.12.1`, roughly Flutter `1.22.x` through `3.41.x`. |
|
||||
| Unknown hashes | Falls back to hardcoded default offsets (8/24/16/32 for entry points, 56 for owner, 24/8 for names) + v3.9.2-shaped ObjectHeader defaults while reporting the Dart version as `unknown`. |
|
||||
| Dart layouts | In-tree profiles cover Dart `2.10.0` through `3.14.0`. Profiles are keyed at layout-change boundaries and resolved by floor: e.g. `3.7`/`3.8` share the `3.6.0` CID layout and `3.10`–`3.12` share the `3.9.0` layout. CID tables through 3.12.1 were verified byte-for-byte against each release's `runtime/vm/class_id.h`; 3.13–3.14 add the combined-snapshot layout. |
|
||||
| Unknown hashes | Git/dev builds carry an unlisted snapshot hash. r2flutter first probes compatible split- or single-snapshot profiles by parsing the complete isolate cluster stream, checking reference/object counts and fill boundaries, and scoring Function/Class/Library records. A match is reported as `~<range-start>+ (structural-probe)` with `version_source=structural-probe`. If no profile validates, the existing newest-compatible split profile remains as a conservative `~<version>+ (fingerprint)` fallback. Exact hashes and explicit `-D` overrides bypass probing. |
|
||||
| Obfuscation maps | Flutter/Dart `--save-obfuscation-map` JSON arrays are supported with `-m <file>` or `e r2flutter.mapfile=<file>` in the plugin. |
|
||||
|
||||
## Layout Profiles
|
||||
@@ -52,11 +52,13 @@ The CID column is compacted as:
|
||||
| 3.6.2 | 3.27.x | `OBJECT_HEADER` | 4 | 5 | 175 | `5/7/18/93/94/95/90/61/23` | CID table update. |
|
||||
| 3.7.0 | 3.29.x | `OBJECT_HEADER` | 4 | 5 | 175 | `5/7/18/93/94/95/90/61/23` | Same modern profile family. |
|
||||
| 3.8.1 | 3.32.x | `OBJECT_HEADER` | 4 | 5 | 175 | `5/7/18/93/94/95/90/61/23` | Same modern profile family. |
|
||||
| 3.9.2 | 3.35.x | `OBJECT_HEADER` | 4 | 5 | 175 | `5/7/18/93/94/95/90/61/23` | Unknown-hash fallback baseline. |
|
||||
| 3.10.7 | 3.38.x | `OBJECT_HEADER` | 4 | 5 | 175 | `5/7/18/93/94/95/90/61/23` | Newest in-tree profile. |
|
||||
| 3.9.2 | 3.35.x | `OBJECT_HEADER` | 4 | 5 | 175 | `5/7/18/93/94/95/90/61/23` | Modern split profile family. |
|
||||
| 3.10.7 | 3.38.x | `OBJECT_HEADER` | 4 | 5 | 175 | `5/7/18/93/94/95/90/61/23` | Same modern profile family. |
|
||||
| 3.11.5 | 3.39.x | `OBJECT_HEADER` | 4 | 5 | 175 | `5/7/18/93/94/95/90/61/23` | Same modern profile family. |
|
||||
| 3.12.0 | 3.41.x | `OBJECT_HEADER` | 4 | 5 | 175 | `5/7/18/93/94/95/90/61/23` | Same modern profile family. |
|
||||
| 3.12.1 | 3.41.x | `OBJECT_HEADER` | 4 | 5 | 175 | `5/7/18/93/94/95/90/61/23` | Same modern profile family. |
|
||||
| 3.12.1 | 3.41.x | `OBJECT_HEADER` | 4 | 5 | 175 | `5/7/18/93/94/95/90/61/23` | Newest split compatibility profile. |
|
||||
| 3.13.0 | — | `OBJECT_HEADER` | 4 | 5 | 176 | `5/7/18/93/94/95/90/61/23` | Combined snapshot; alternate Class/Code/Closure framing. |
|
||||
| 3.14.0 | — | `OBJECT_HEADER` | 4 | 5 | 176 | `5/7/18/93/94/95/90/61/23` | Same combined-snapshot profile family. |
|
||||
|
||||
All profiles use heap object tag `1`, default alignment `16`, and instruction
|
||||
table capacity `20000` before feature-flag or `offsets.json` adjustments.
|
||||
@@ -89,6 +91,8 @@ These hashes are mapped directly by `src/lib/dart_version.c`.
|
||||
| 3.11.5 | `78da37fed6bf1489361a312568249f3f` |
|
||||
| 3.12.0 | `41be3daaabd524b8aa7423bc24584957` |
|
||||
| 3.12.1 | `ace654289f5abc240509fc941453ebc5` |
|
||||
| 3.13.0 | `0451907c2eaa8467e848c0067bfe8ed4` |
|
||||
| 3.14.0 | `7a1ea3f6f5cf1089a7f6e55d7f20dbfd` |
|
||||
|
||||
`include/r2flutter/dart_offsets.h` contains the hash→compressed_word_size lookup
|
||||
table and the default offset arrays, compiled statically into r2flutter. No
|
||||
@@ -362,7 +366,7 @@ name, then applies it to:
|
||||
| `-HH` | Dump the snapshot header plus VM/isolate cluster allocation/fill layout. |
|
||||
| `-HHH` | Add selected cluster payload diagnostics, currently ObjectPool entry decoding/status plus conservative ref resolution. |
|
||||
| `-i` | Dump instruction-table entries. |
|
||||
| `-O <addr\|pp+off>` | Decode a Dart Smi/tagged heap pointer/object address, or decode a reconstructed ObjectPool PP slot such as `pp+0x68`. Honors `-j`, `-q`, and `-r`. |
|
||||
| `-O <addr\|pp+off\|ref:N>` | Decode a Dart Smi/tagged heap pointer/object address, a reconstructed ObjectPool PP slot such as `pp+0x68`, or a serialized snapshot reference. Snapshot values are recursively bounded and preserve arrays, maps, sets, and instance fields. Honors `-j`, `-q`, and `-r`. |
|
||||
| `-p` | Print the reconstructed static ObjectPool PP address pair; with `-r`, map the synthetic pool image and set `anal.gp`/`x27` from the synthetic vaddr. |
|
||||
| `-R` | Dump a radare2 script for applying method flags/comments and PP helpers. |
|
||||
| `-S` | Dump best-effort recovered components/SBOM with source, confidence, evidence, and nullable versions. |
|
||||
@@ -386,6 +390,11 @@ Config keys:
|
||||
|
||||
- `r2flutter.mapfile`: Flutter obfuscation map JSON path
|
||||
- `r2flutter.namepool`: enable heuristic name-pool fallback
|
||||
- `r2flutter.profile`: override Dart snapshot profile by 32-byte hash or Dart version
|
||||
- `r2flutter.str.limit`: max number of strings printed by `-z`/`-zz` (default `0`, unlimited); without filters it also stops the `-zz` carving early
|
||||
- `r2flutter.str.minlen`: skip printing strings shorter than N (default `0`)
|
||||
- `r2flutter.str.maxlen`: skip printing strings longer than N (default `0`, unlimited)
|
||||
- `r2flutter.str.filter`: only print strings matching this extended regex (default empty)
|
||||
|
||||
Plugin modifiers:
|
||||
|
||||
@@ -411,7 +420,7 @@ Plugin actions:
|
||||
| `r2flutter -HH` | Print snapshot header info plus VM/isolate cluster allocation/fill layout. |
|
||||
| `r2flutter -HHH` | Print cluster layout plus selected payload diagnostics, currently ObjectPool entries/status and ref resolution. |
|
||||
| `r2flutter -i` | Print instruction-table entries. |
|
||||
| `r2flutter -O addr` | Decode a Dart Smi/tagged heap pointer/object address, or decode a reconstructed ObjectPool PP slot such as `pp+0x68`. Honors `-j`, `-q`, and `-r`. |
|
||||
| `r2flutter -O spec` | Decode a Dart Smi/tagged heap pointer/object address, a reconstructed ObjectPool PP slot such as `pp+0x68`, or a serialized snapshot reference such as `ref:209553`. Honors `-j`, `-q`, and `-r`. |
|
||||
| `r2flutter -p` | Print the reconstructed static ObjectPool PP address pair. |
|
||||
| `r2flutter -R` | Print full radare2 script output. |
|
||||
| `r2flutter -S` | Dump best-effort recovered components/SBOM from the current binary context. |
|
||||
@@ -452,8 +461,8 @@ The `-R` path emits:
|
||||
- `PP=x27`; per-function PP offset helper scanning is intentionally skipped by
|
||||
default in script output because it is too slow on large apps
|
||||
- `-r -p` is the focused PP setup path: it opens a malloc map at the synthetic
|
||||
PP base, writes the reconstructed ObjectPool image, and sets `e anal.gp` plus
|
||||
`dr x27`
|
||||
PP base, writes the reconstructed ObjectPool image, sets `e anal.gp`, clears
|
||||
`anal.roregs` before assigning `x27`, then restores `anal.roregs=x27,gp,zero`
|
||||
|
||||
## Practical Limits
|
||||
|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
# AuthPass.app Dart AOT TLS tutorial
|
||||
|
||||
This tutorial uses the iOS fixture:
|
||||
|
||||
```text
|
||||
test/bins/ios/AuthPass.app
|
||||
```
|
||||
|
||||
Run commands from the repository root. The Dart AOT image is not the top-level
|
||||
`Runner` executable; it is:
|
||||
|
||||
```sh
|
||||
IOS_BUNDLE=test/bins/ios/AuthPass.app
|
||||
IOS_APP="$IOS_BUNDLE/Frameworks/App.framework/App"
|
||||
```
|
||||
|
||||
Rebuild the tools and plugin before following the r2 steps:
|
||||
|
||||
```sh
|
||||
make
|
||||
make r2
|
||||
make user-install
|
||||
```
|
||||
|
||||
## Challenge 1: recover Dart AOT method names and map the TLS handshake path
|
||||
|
||||
Start by verifying that r2flutter recognizes the snapshot:
|
||||
|
||||
```sh
|
||||
bin/r2flutter -j -H "$IOS_BUNDLE"
|
||||
```
|
||||
|
||||
Expected important values:
|
||||
|
||||
```text
|
||||
"dart_version":"3.2.5"
|
||||
"hash":"f71c76320d35b65f1164dbaa6d95fe09"
|
||||
"it_len":37092
|
||||
"tag_style":"CID_SHIFT1"
|
||||
```
|
||||
|
||||
Dump recovered functions and resolve the TLS methods by semantic name. Do not
|
||||
hardcode the private suffix; AuthPass uses `14069316`, but that value changes
|
||||
between snapshots.
|
||||
|
||||
```sh
|
||||
FUNCS=$(mktemp)
|
||||
bin/r2flutter -q -f "$IOS_BUNDLE" > "$FUNCS"
|
||||
|
||||
BAD_CERT_ADDR=$(awk '/method\._RawSecureSocket_.*\._onBadCertificateWrapper_/ { print $1; exit }' "$FUNCS")
|
||||
REGISTER_ADDR=$(awk '/method\._SecureFilterImpl_.*\.registerBadCertificateCallback$/ { print $1; exit }' "$FUNCS")
|
||||
REGISTER_IMPL_ADDR=$(awk '/method\._SecureFilterImpl_.*\._registerBadCertificateCallback_/ { print $1; exit }' "$FUNCS")
|
||||
HANDSHAKE_ADDR=$(awk '/method\._RawSecureSocket_.*\._secureHandshake_/ { print $1; exit }' "$FUNCS")
|
||||
MARK_TRUSTED_ADDR=$(awk '/method\._SecureFilterImpl_.*\._markAsTrusted_/ { print $1; exit }' "$FUNCS")
|
||||
|
||||
printf 'wrapper=%s register=%s register_impl=%s handshake=%s mark=%s\n' \
|
||||
"$BAD_CERT_ADDR" "$REGISTER_ADDR" "$REGISTER_IMPL_ADDR" "$HANDSHAKE_ADDR" "$MARK_TRUSTED_ADDR"
|
||||
rm -f "$FUNCS"
|
||||
```
|
||||
|
||||
Expected output:
|
||||
|
||||
```text
|
||||
wrapper=0x3199d4 register=0x3194ec register_impl=0x31954c handshake=0x31749c mark=0x31925c
|
||||
```
|
||||
|
||||
Apply r2flutter flags in r2 and confirm that the same method flags exist:
|
||||
|
||||
```sh
|
||||
r2 -q -e scr.color=0 -e bin.relocs.apply=true \
|
||||
-c 'r2flutter -A' \
|
||||
-c 'f~onBadCertificateWrapper' \
|
||||
-c 'f~registerBadCertificateCallback' \
|
||||
-c 'f~secureHandshake' \
|
||||
-c q "$IOS_APP"
|
||||
```
|
||||
|
||||
Expected method flags:
|
||||
|
||||
```text
|
||||
0x003199d4 0 method._RawSecureSocket_14069316._onBadCertificateWrapper_14069316
|
||||
0x003194ec 0 method._SecureFilterImpl_14069316.registerBadCertificateCallback
|
||||
0x0031954c 0 method._SecureFilterImpl_14069316._registerBadCertificateCallback_14069316
|
||||
0x0031749c 0 method._RawSecureSocket_14069316._secureHandshake_14069316
|
||||
```
|
||||
|
||||
Now disassemble the handshake:
|
||||
|
||||
```sh
|
||||
r2 -q -e scr.color=0 -e bin.relocs.apply=true \
|
||||
-c 'r2flutter -A' \
|
||||
-c "s ${HANDSHAKE_ADDR:-0x31749c}" \
|
||||
-c 'pd 104' \
|
||||
-c q "$IOS_APP"
|
||||
```
|
||||
|
||||
Useful lines:
|
||||
|
||||
```asm
|
||||
0x003174e4 bl method._SecureFilterImpl_14069316.handshake
|
||||
0x00317514 bl method._RawSecureSocket_14069316._secureHandshake_14069316
|
||||
0x0031752c add x1, x22, 0x30
|
||||
0x0031753c bl method._RawSecureSocket_14069316._readSocket_14069316
|
||||
0x00317548 bl method._RawSecureSocket_14069316._writeSocket_14069316
|
||||
0x00317554 bl method._RawSecureSocket_14069316._scheduleFilter_14069316
|
||||
0x00317588 bl method._RawSecureSocket_14069316._reportError_14069316
|
||||
0x003175ac add x0, x22, 0x20
|
||||
```
|
||||
|
||||
The boolean constants are visible in this cluster:
|
||||
|
||||
- `x22 + 0x20` is Dart `true`.
|
||||
- `x22 + 0x30` is Dart `false`.
|
||||
|
||||
## Challenge 2: patch `_RawSecureSocket._onBadCertificateWrapper`
|
||||
|
||||
Disassemble the wrapper:
|
||||
|
||||
```sh
|
||||
r2 -q -e scr.color=0 -e bin.relocs.apply=true \
|
||||
-c 'r2flutter -A' \
|
||||
-c "s ${BAD_CERT_ADDR:-0x3199d4}" \
|
||||
-c 'pd 24' \
|
||||
-c q "$IOS_APP"
|
||||
```
|
||||
|
||||
Important instructions:
|
||||
|
||||
```asm
|
||||
0x003199d4 stp x29, x30, [x15, -0x10]!
|
||||
0x003199ec ldr x0, [x29, 0x18]
|
||||
0x003199f0 ldur x1, [x0, 0x5f]
|
||||
0x003199f4 cmp x1, x22
|
||||
0x003199f8 b.eq 0x319a28
|
||||
0x00319a00 stp x16, x1, [x15]
|
||||
0x00319a0c ldur x2, [x0, 0x37]
|
||||
0x00319a10 blr x2
|
||||
0x00319a1c ret
|
||||
```
|
||||
|
||||
AuthPass differs from the Android samples: on the normal path the wrapper
|
||||
returns the app-provided bad-certificate callback result unchanged. The bypass
|
||||
target is still the wrapper entry. Returning Dart `true` before the callback
|
||||
logic makes the certificate decision succeed.
|
||||
|
||||
For this fixture, `rabin2 -S "$IOS_APP"` reports equal `__text` physical and
|
||||
virtual addresses:
|
||||
|
||||
```sh
|
||||
rabin2 -S "$IOS_APP" | rg '__TEXT.__text'
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
```text
|
||||
0 0x00005a80 0x6f8bd0 0x00005a80 0x6f8bd0 -r-x 0x0 REGULAR 0.__TEXT.__text
|
||||
```
|
||||
|
||||
That means the recovered method virtual address is also the raw file offset for
|
||||
this fixture. If this is not true for another Mach-O, translate with:
|
||||
|
||||
```text
|
||||
section_paddr + (method_vaddr - section_vaddr)
|
||||
```
|
||||
|
||||
Patch a copy first:
|
||||
|
||||
```sh
|
||||
cp "$IOS_APP" "$IOS_APP.bak"
|
||||
|
||||
r2 -q -w -n -e scr.color=0 \
|
||||
-c "s ${BAD_CERT_ADDR:-0x3199d4}" \
|
||||
-c 'wx c0820091c0035fd6' \
|
||||
-c q "$IOS_APP"
|
||||
```
|
||||
|
||||
Patch bytes:
|
||||
|
||||
```text
|
||||
c0820091 c0035fd6
|
||||
```
|
||||
|
||||
Decoded AArch64:
|
||||
|
||||
```asm
|
||||
add x0, x22, 0x20
|
||||
ret
|
||||
```
|
||||
|
||||
Verify:
|
||||
|
||||
```sh
|
||||
r2 -q -n -a arm -b 64 -e scr.color=0 \
|
||||
-c "s ${BAD_CERT_ADDR:-0x3199d4}" \
|
||||
-c 'p8 8' \
|
||||
-c 'pd 2' \
|
||||
-c q "$IOS_APP"
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
```text
|
||||
c0820091c0035fd6
|
||||
add x0, x22, 0x20
|
||||
ret
|
||||
```
|
||||
|
||||
Restore the fixture after testing:
|
||||
|
||||
```sh
|
||||
cp "$IOS_APP.bak" "$IOS_APP"
|
||||
```
|
||||
|
||||
You can also use the generic qjs helper on a copy:
|
||||
|
||||
```sh
|
||||
TMP_APP=$(mktemp /tmp/r2flutter-authpass-app.XXXXXX)
|
||||
cp "$IOS_APP" "$TMP_APP"
|
||||
r2 -q -w -e bin.relocs.apply=true \
|
||||
-i scripts/dart_cert_bypass.qjs \
|
||||
"$TMP_APP"
|
||||
r2 -q -n -a arm -b 64 -e scr.color=0 \
|
||||
-c 's 0x3199d4' \
|
||||
-c 'p8 8' \
|
||||
-c 'pd 2' \
|
||||
-c q "$TMP_APP"
|
||||
rm -f "$TMP_APP"
|
||||
```
|
||||
|
||||
The script resolves `method._RawSecureSocket_*._onBadCertificateWrapper_*`
|
||||
through r2flutter flags, translates the method address to a file offset using
|
||||
section metadata, and writes the same two AArch64 instructions. It expects the
|
||||
actual Dart image to be opened in r2: Android `libapp.so` or iOS
|
||||
`Frameworks/App.framework/App`.
|
||||
|
||||
## Challenge 3: trace callback registration and direct xrefs
|
||||
|
||||
Disassemble callback registration:
|
||||
|
||||
```sh
|
||||
r2 -q -e scr.color=0 -e bin.relocs.apply=true \
|
||||
-c 'r2flutter -A' \
|
||||
-c "s ${REGISTER_ADDR:-0x3194ec}" \
|
||||
-c 'pd 44' \
|
||||
-c q "$IOS_APP"
|
||||
```
|
||||
|
||||
Useful lines:
|
||||
|
||||
```asm
|
||||
0x0031950c stur x0, [x1, 0xf]
|
||||
0x00319530 bl method._SecureFilterImpl_14069316._registerBadCertificateCallback_14069316
|
||||
0x00319534 mov x0, x22
|
||||
0x00319540 ret
|
||||
```
|
||||
|
||||
Run lightweight reference analysis and query the resolved TLS addresses:
|
||||
|
||||
```sh
|
||||
r2 -q -e scr.color=0 -e bin.relocs.apply=true \
|
||||
-c 'r2flutter -A' \
|
||||
-c 'aar' \
|
||||
-c "axt @ ${BAD_CERT_ADDR:-0x3199d4}" \
|
||||
-c "axt @ ${REGISTER_ADDR:-0x3194ec}" \
|
||||
-c "axt @ ${REGISTER_IMPL_ADDR:-0x31954c}" \
|
||||
-c "axt @ ${HANDSHAKE_ADDR:-0x31749c}" \
|
||||
-c q "$IOS_APP"
|
||||
```
|
||||
|
||||
Observed direct xrefs:
|
||||
|
||||
```text
|
||||
0x3199bc -> 0x3199d4 _RawSecureSocket._anon_closure -> _onBadCertificateWrapper
|
||||
0x316cd8 -> 0x3194ec _RawSecureSocket constructor -> registerBadCertificateCallback
|
||||
0x319530 -> 0x31954c registerBadCertificateCallback -> _registerBadCertificateCallback
|
||||
0x317028 -> 0x31749c _RawSecureSocket constructor -> _secureHandshake
|
||||
0x317514 -> 0x31749c _secureHandshake -> _secureHandshake
|
||||
0x3178b4 -> 0x31749c _tryFilter -> _secureHandshake
|
||||
0x317e04 -> 0x31749c _closeHandler -> _secureHandshake
|
||||
```
|
||||
|
||||
The call into the app-provided certificate callback inside the wrapper is
|
||||
indirect (`blr x2`), so it is not expected to produce a stable direct `axt`
|
||||
target. The stable evidence is the callback registration, the wrapper body, the
|
||||
TLS method cluster, and the direct references to the wrapper.
|
||||
|
||||
## Regression tests
|
||||
|
||||
The tutorial workflow is covered by:
|
||||
|
||||
```sh
|
||||
r2r -u test/db/cmd/authpass-r2-tutorial
|
||||
```
|
||||
|
||||
The tests validate:
|
||||
|
||||
- r2 plugin flag recovery for the AuthPass TLS methods.
|
||||
- wrapper and handshake disassembly landmarks.
|
||||
- direct xref/callsite sources without running full analysis in the test.
|
||||
- the generic qjs bypass script on a temporary copy.
|
||||
|
||||
Patching the embedded iOS framework invalidates the bundle code signature. A
|
||||
real deployment requires re-signing with the appropriate identity/provisioning
|
||||
workflow.
|
||||
@@ -18,6 +18,7 @@ typedef struct DartFunction {
|
||||
ut64 addr;
|
||||
ut64 size;
|
||||
int quality;
|
||||
char *signature; // recovered Dart signature "RET(T,T,...)", or NULL
|
||||
} DartFunction;
|
||||
|
||||
void dart_function_fini(DartFunction *fn);
|
||||
@@ -43,8 +44,8 @@ DartApp *dart_app_new(const char *path);
|
||||
DartApp *dart_app_new_from_core(RCore *core, DartCtx *dctx);
|
||||
void dart_app_free(DartApp *app);
|
||||
void dart_app_load_info(DartApp *app);
|
||||
bool dart_app_find_macho_embedded_dart(const char *path, DartAppEmbeddedPayload *out);
|
||||
char *dart_app_extract_embedded_payload(const char *path, const DartAppEmbeddedPayload *payload);
|
||||
bool dart_app_find_macho_dart(const char *path, DartAppEmbeddedPayload *out);
|
||||
char *dart_app_extract_payload(const char *path, const DartAppEmbeddedPayload *payload);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ typedef enum {
|
||||
DART_CID_PC_DESCRIPTORS,
|
||||
DART_CID_CODE_SOURCE_MAP,
|
||||
DART_CID_COMPRESSED_STACK_MAPS,
|
||||
DART_CID_LOCAL_VAR_DESCRIPTORS,
|
||||
DART_CID_EXCEPTION_HANDLERS,
|
||||
DART_CID_CONTEXT,
|
||||
DART_CID_CONTEXT_SCOPE,
|
||||
@@ -43,8 +44,10 @@ typedef enum {
|
||||
DART_CID_MEGAMORPHIC_CACHE,
|
||||
DART_CID_SUBTYPE_TEST_CACHE,
|
||||
DART_CID_LOADING_UNIT,
|
||||
DART_CID_API_ERROR,
|
||||
DART_CID_LANGUAGE_ERROR,
|
||||
DART_CID_UNHANDLED_EXCEPTION,
|
||||
DART_CID_UNWIND_ERROR,
|
||||
DART_CID_INSTANCE,
|
||||
DART_CID_LIBRARY_PREFIX,
|
||||
DART_CID_TYPE_ARGUMENTS,
|
||||
@@ -91,8 +94,8 @@ typedef enum {
|
||||
const char *dart_cid_kind_name(DartCidKind kind);
|
||||
int dart_cid_get(const DartVerLayout *layout, DartCidKind kind);
|
||||
bool dart_cid_is(const DartVerLayout *layout, int cid, DartCidKind kind);
|
||||
int dart_cid_typed_data_internal_base(const DartVerLayout *layout);
|
||||
int dart_cid_typed_data_internal_limit(const DartVerLayout *layout);
|
||||
int dart_cid_typed_data_base(const DartVerLayout *layout);
|
||||
int dart_cid_typed_data_limit(const DartVerLayout *layout);
|
||||
int dart_cid_typed_data_stride(const DartVerLayout *layout);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -7,9 +7,15 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
char *dart_dumper_dump4radare2(DartApp *app);
|
||||
char *dart_dumper_dump_r2(DartApp *app);
|
||||
char *dart_dumper_dump_funcs(DartApp *app, int fmt);
|
||||
void dart_dumper_apply_to_core(DartApp *app);
|
||||
void dart_dumper_apply_to_core(DartApp *app, bool apply_signatures);
|
||||
void dart_dumper_apply_signatures(DartApp *app);
|
||||
void dart_dumper_name_functions(DartApp *app);
|
||||
// Disable anal.trycatch while r2flutter runs many `af` commands, when no try.*
|
||||
// flag exists for it to find. Returns the previous value to pass to the restore.
|
||||
bool dart_core_trycatch_suspend(RCore *core);
|
||||
void dart_core_trycatch_restore(RCore *core, bool prev);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ typedef struct {
|
||||
} DartHashEntry;
|
||||
|
||||
static const DartHashEntry dart_hash_entries[] = {
|
||||
{ "0451907c2eaa8467e848c0067bfe8ed4", 4 }, // Dart 3.13.0
|
||||
{ "0c7f1aa25fc597a7b3b9a19ed390afa1", 8 }, // Dart 2.13.0
|
||||
{ "0eb277b50d6ce4069261c69150ce52c9", 4 }, // Dart 2.17.0
|
||||
{ "1441d6b13b8623fa7fbf61433abebd31", 4 }, // Dart 2.17.6
|
||||
@@ -27,6 +28,7 @@ static const DartHashEntry dart_hash_entries[] = {
|
||||
{ "41be3daaabd524b8aa7423bc24584957", 4 }, // Dart 3.12.0
|
||||
{ "501ef5cbd64ca70b6b42672346af6a8a", 4 }, // Dart 2.19.0
|
||||
{ "5191d02bb1d36a9f8609aa7495894e76", 4 }, // Dart 3.1.0
|
||||
{ "5b97292b25f0a715613b7a28e0734f77", 8 }, // Dart 2.12.0
|
||||
{ "6093345773c65683f60c20c8e62cbe4a", 8 }, // Dart 2.16.0
|
||||
{ "617d044d9faa87a98c29f8aad5d2f220", 4 }, // Dart 3.0.3
|
||||
{ "659a72e41e3276e882709901c27de33d", 8 }, // Dart 2.14.0
|
||||
@@ -35,6 +37,7 @@ static const DartHashEntry dart_hash_entries[] = {
|
||||
{ "77b867742ae1ace67fbbb9a9a0d0334e", 4 }, // Dart 3.3.0
|
||||
{ "77bd0a83c49bfc555a44a0740f15003a", 4 }, // Dart 2.18.0
|
||||
{ "78da37fed6bf1489361a312568249f3f", 4 }, // Dart 3.11.5
|
||||
{ "7a1ea3f6f5cf1089a7f6e55d7f20dbfd", 4 }, // Dart 3.14.0
|
||||
{ "7a5b240780941844bae88eca5dbaa7b8", 8 }, // Dart 2.13.0
|
||||
{ "7b96ef6dda75bdd1d70be8c2f7cb814e", 8 }, // Dart 2.15.0
|
||||
{ "7dbbeeb8ef7b91338640dca3927636de", 4 }, // Dart 3.1.3
|
||||
@@ -75,14 +78,17 @@ static const DartHashEntry dart_hash_entries[] = {
|
||||
{ "fe53c4fdba846005b70514aec3840296", 8 }, // Dart 3.4.0
|
||||
};
|
||||
|
||||
#define DART_HASH_ENTRIES_COUNT 61
|
||||
#define DART_HASH_ENTRIES_COUNT 64
|
||||
|
||||
// Default offset arrays (identical across all known Dart versions)
|
||||
#define DART_DEFAULT_EP_OFFSETS { 8, 24, 16, 32 }
|
||||
#define DART_DEFAULT_EP_OFFSETS \
|
||||
{ 8, 24, 16, 32 }
|
||||
#define DART_DEFAULT_EP_OFFSETS_COUNT 4
|
||||
#define DART_DEFAULT_OWNER_OFFSETS { 56 }
|
||||
#define DART_DEFAULT_OWNER_OFFSETS \
|
||||
{ 56 }
|
||||
#define DART_DEFAULT_OWNER_OFFSETS_COUNT 1
|
||||
#define DART_DEFAULT_NAME_OFFSETS { 24, 8 }
|
||||
#define DART_DEFAULT_NAME_OFFSETS \
|
||||
{ 24, 8 }
|
||||
#define DART_DEFAULT_NAME_OFFSETS_COUNT 2
|
||||
|
||||
#endif // R2C_DART_OFFSETS_H
|
||||
@@ -82,12 +82,17 @@ typedef struct DartClassInfo {
|
||||
char *library_name;
|
||||
ut64 library_ref;
|
||||
ut64 super_class_ref;
|
||||
ut64 super_type_ref;
|
||||
ut64 interfaces_ref;
|
||||
char *super_class_name;
|
||||
ut32 class_id;
|
||||
ut32 instance_size;
|
||||
ut32 next_field_offset;
|
||||
ut32 type_argument_offset;
|
||||
ut32 num_type_parameters;
|
||||
ut32 flags;
|
||||
ut64 field_bitmap;
|
||||
bool field_bitmap_present;
|
||||
RList *enums;
|
||||
RList *fields;
|
||||
RList *interfaces;
|
||||
@@ -149,6 +154,7 @@ typedef struct DartInstructionTableEntry {
|
||||
ut32 stack_map_offset;
|
||||
bool has_code;
|
||||
char *name;
|
||||
char *signature;
|
||||
} DartInstructionTableEntry;
|
||||
|
||||
void dart_instruction_table_entry_fini(DartInstructionTableEntry *ie);
|
||||
@@ -161,13 +167,13 @@ typedef void(*DartPoolFunctionCallback)(const char *name, ut64 addr, ut64 size,
|
||||
// ============================================================================
|
||||
|
||||
bool dart_ctx_set_profile_override(DartCtx *ctx, const char *spec);
|
||||
const char *dart_ctx_effective_version(DartCtx *ctx);
|
||||
const char *dart_ctx_effective_hash(DartCtx *ctx);
|
||||
int dart_pool_enumerate(DartCtx *ctx, const char *libapp_path, DartPoolFunctionCallback on_fn, void *user, ut64 *out_base, ut64 *out_heap_base);
|
||||
const char *dart_ctx_version(DartCtx *ctx);
|
||||
const char *dart_ctx_hash(DartCtx *ctx);
|
||||
int dart_pool_scan(DartCtx *ctx, DartPoolFunctionCallback on_fn, void *user);
|
||||
char *dart_pool_dump_header(DartCtx *ctx, int fmt);
|
||||
char *dart_pool_dump_header_ext(DartCtx *ctx, int fmt);
|
||||
char *dart_pool_dump_header_deep(DartCtx *ctx, int fmt);
|
||||
char *dart_pool_dump_pp(DartCtx *ctx, int fmt);
|
||||
char *dart_pool_dump_pool(DartCtx *ctx, int fmt);
|
||||
char *dart_pool_dump_object(DartCtx *ctx, const char *spec, int fmt);
|
||||
|
||||
// ============================================================================
|
||||
@@ -192,11 +198,12 @@ void dart_string_ref_free(DartStringRef *sr);
|
||||
// ============================================================================
|
||||
|
||||
RList *dart_pool_extract_strings(DartCtx *ctx);
|
||||
RList *dart_pool_extract_object_pool_strings(DartCtx *ctx);
|
||||
RList *dart_pool_extract_pool_strings(DartCtx *ctx);
|
||||
char *dart_pool_dump_strings(DartCtx *ctx, int fmt);
|
||||
char *dart_pool_dump_strings_fuzzy(DartCtx *ctx, int fmt);
|
||||
void dart_string_list_free(RList *list);
|
||||
char *dart_pool_dump_xrefs(DartCtx *ctx, int fmt);
|
||||
int dart_pool_apply_xrefs(DartCtx *ctx);
|
||||
char *dart_pool_dump_sbom(DartCtx *ctx, const char *input_path, int fmt);
|
||||
|
||||
// ============================================================================
|
||||
@@ -204,6 +211,7 @@ char *dart_pool_dump_sbom(DartCtx *ctx, const char *input_path, int fmt);
|
||||
// ============================================================================
|
||||
|
||||
RVecDartInstructionTableEntry *dart_pool_extract_instruction_table(DartCtx *ctx);
|
||||
char *dart_pool_dump_entry(DartCtx *ctx, int fmt);
|
||||
char *dart_pool_dump_it(DartCtx *ctx, int fmt);
|
||||
void dart_instruction_table_entry_free(DartInstructionTableEntry *ie);
|
||||
void dart_instruction_table_list_free(RVecDartInstructionTableEntry *list);
|
||||
|
||||
@@ -47,16 +47,21 @@ typedef struct {
|
||||
char dart_version_override[32];
|
||||
const DartVerLayout *layout;
|
||||
int compressed_word_size;
|
||||
int version_source; // DartVersionSource: how the layout/version was chosen
|
||||
bool layout_probed;
|
||||
HtUP *name_by_ep;
|
||||
char **name_by_code_index;
|
||||
ut64 name_by_code_index_count;
|
||||
// Recovered Dart signature per code index, format "RET(T,T,...)" where the
|
||||
// first param of an instance method is the receiver typed as the owner
|
||||
// class. Parallel to name_by_code_index; NULL where unresolved.
|
||||
char **signature_by_code_index;
|
||||
ut64 signature_by_code_index_count;
|
||||
ut8 *owner_kind_by_code_index;
|
||||
ut64 owner_kind_by_code_index_count;
|
||||
RList *name_pool;
|
||||
int name_pool_idx;
|
||||
RList *strings;
|
||||
RList *classes;
|
||||
RList *functions;
|
||||
void **refs;
|
||||
ut64 refs_count;
|
||||
ut64 num_base_objects;
|
||||
@@ -76,9 +81,23 @@ typedef struct {
|
||||
int dump_fields;
|
||||
int dump_strings;
|
||||
bool dump_string_refs;
|
||||
ut64 str_limit; // max strings to print (0 = unlimited)
|
||||
ut64 str_scan_limit; // max strings to carve in the fuzzy scan (0 = unlimited)
|
||||
int str_minlen; // skip printing strings shorter than this
|
||||
int str_maxlen; // skip printing strings longer than this (0 = unlimited)
|
||||
const char *str_filter; // only print strings matching this regex
|
||||
const char *obf_map_path;
|
||||
HtPP *obf_by_obfuscated;
|
||||
bool obf_map_tried;
|
||||
// Windowed read cache for read_mem, to avoid one r_io_read_at syscall per
|
||||
// byte while decoding cluster streams (which read one byte at a time).
|
||||
// Scoped to a single command invocation and released by dart_obf_fini.
|
||||
// Heap-allocated so that DartCtx copies share the windows together with
|
||||
// their addresses.
|
||||
struct dart_read_cache_t *rmem;
|
||||
} DartCtx;
|
||||
|
||||
// Release a read_mem window cache (NULL is accepted).
|
||||
void dart_read_cache_free(struct dart_read_cache_t *cache);
|
||||
|
||||
#endif
|
||||
@@ -19,6 +19,47 @@ typedef enum {
|
||||
DART_TAG_STYLE_OBJECT_HEADER = 2 // v3.4.3+ and known 2.18.2 exception
|
||||
} DartTagStyle;
|
||||
|
||||
// Ordered fields following the snapshot feature string. The field count alone
|
||||
// is insufficient because the meaning of the five-field layouts changed.
|
||||
typedef enum {
|
||||
DART_HEADER_STYLE_MODERN = 0, // nb, no, nc, itlen, itdata (2.18+)
|
||||
DART_HEADER_STYLE_210, // nb, no, nc, field_table_len
|
||||
DART_HEADER_STYLE_212, // nb, no, canonical_nc, nc, field_table_len
|
||||
DART_HEADER_STYLE_214, // nb, no, nc, field_table_len, itlen
|
||||
DART_HEADER_STYLE_216 // nb, no, nc, field_table_len, itlen, itdata
|
||||
} DartHeaderStyle;
|
||||
|
||||
// Object references use the ordinary unsigned stream integer through Dart
|
||||
// 2.17. Dart 2.18 introduced a distinct big-endian RefId encoding whose
|
||||
// signed terminal byte is biased by 128.
|
||||
typedef enum {
|
||||
DART_REF_ENCODING_REF_ID128 = 0,
|
||||
DART_REF_ENCODING_UNSIGNED
|
||||
} DartRefEncoding;
|
||||
|
||||
// ObjectPool entry type/patch/behavior bit packing changed independently of
|
||||
// the surrounding cluster layout.
|
||||
typedef enum {
|
||||
DART_POOL_ENCODING_MODERN = 0, // type[0..3], patch[4], behavior[5..7]
|
||||
DART_POOL_ENCODING_LOW7, // type[0..6], patch[7], tagged=0, immediate=1
|
||||
DART_POOL_ENCODING_LOW7_SWAPPED // type[0..6], patch[7], immediate=0, tagged=1
|
||||
} DartPoolEncoding;
|
||||
|
||||
typedef enum {
|
||||
DART_CODE_ALLOC_STATE_DEFERRED = 0, // count, state words, deferred count/state words
|
||||
DART_CODE_ALLOC_DEFERRED_ONLY // count, deferred count
|
||||
} DartCodeAlloc;
|
||||
|
||||
// How the Dart version/layout for a snapshot was determined. Reported so users
|
||||
// can distinguish exact, structurally validated, and fallback profiles.
|
||||
typedef enum {
|
||||
DART_VERSION_SOURCE_EXACT = 0, // snapshot hash matched a known SDK release
|
||||
DART_VERSION_SOURCE_OVERRIDE = 1, // user forced a profile via -D
|
||||
DART_VERSION_SOURCE_PROBE = 2, // unknown hash, layout selected by structural validation
|
||||
DART_VERSION_SOURCE_FINGERPRINT = 3, // unknown hash, conservative fallback profile
|
||||
DART_VERSION_SOURCE_UNKNOWN = 4 // no snapshot available
|
||||
} DartVersionSource;
|
||||
|
||||
// Version layout information for Dart snapshots
|
||||
// CID values are stored in dart_cid.c's cid_tables[] and accessed via dart_cid_get()
|
||||
typedef struct {
|
||||
@@ -30,6 +71,17 @@ typedef struct {
|
||||
ut64 it_cap;
|
||||
DartTagStyle tag_style;
|
||||
int header_fields;
|
||||
DartHeaderStyle header_style;
|
||||
DartRefEncoding ref_encoding;
|
||||
DartPoolEncoding pool_encoding;
|
||||
DartCodeAlloc code_alloc;
|
||||
int code_refs;
|
||||
int code_leading_refs;
|
||||
bool code_has_text_offset;
|
||||
bool single_snapshot;
|
||||
bool class_alloc_fixed;
|
||||
bool closure_variable;
|
||||
bool class_top_level_cid20;
|
||||
} DartVerLayout;
|
||||
|
||||
// Lookup Dart version from a snapshot hash (MD5)
|
||||
@@ -40,12 +92,26 @@ const char *dart_version_from_hash(const char *hash);
|
||||
// Returns NULL if the version string is not a dotted numeric version.
|
||||
const DartVerLayout *dart_profile_from_version(const char *version);
|
||||
|
||||
// Pick a layout structure based on hash, with fallback to defaults
|
||||
// Returns a newly allocated DartVerLayout (caller must free)
|
||||
// If hash is unknown, uses v3.9.2 defaults
|
||||
DartVerLayout *dart_pick_layout_by_hash(const char *hash);
|
||||
// Enumerate the built-in range-start profiles for structural probing.
|
||||
int dart_profile_count(void);
|
||||
const DartVerLayout *dart_profile_at(int index);
|
||||
|
||||
// Free a DartVerLayout allocated by dart_pick_layout_by_hash
|
||||
// Pick a layout structure based on hash, with fallback to a compatible baseline
|
||||
// Returns a newly allocated DartVerLayout (caller must free)
|
||||
// If hash is unknown, fingerprints against the newest split-snapshot profile.
|
||||
DartVerLayout *dart_layout_from_hash(const char *hash);
|
||||
|
||||
// Newest known layout profile.
|
||||
const DartVerLayout *dart_newest_profile(void);
|
||||
|
||||
// Conservative profile used until an unknown hash can be structurally probed.
|
||||
// Single-snapshot mechanics require positive evidence and are not inherited.
|
||||
const DartVerLayout *dart_fingerprint_profile(void);
|
||||
|
||||
// Human-readable name for a DartVersionSource value.
|
||||
const char *dart_version_source_str(int source);
|
||||
|
||||
// Free a DartVerLayout allocated by dart_layout_from_hash
|
||||
void dart_ver_layout_free(DartVerLayout *layout);
|
||||
|
||||
void dart_version_set_verbose(int level);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#ifndef R2FLUTTER_VERSION_H
|
||||
#define R2FLUTTER_VERSION_H
|
||||
|
||||
#define R2FLUTTER_VERSION "0.2.0"
|
||||
#define R2FLUTTER_VERSION "0.6.0"
|
||||
#define R2FLUTTER_VERSION_MAJOR 0
|
||||
#define R2FLUTTER_VERSION_MINOR 2
|
||||
#define R2FLUTTER_VERSION_MINOR 6
|
||||
#define R2FLUTTER_VERSION_PATCH 0
|
||||
|
||||
#endif
|
||||
+52
-3
@@ -1,12 +1,48 @@
|
||||
project(
|
||||
'r2flutter',
|
||||
'c',
|
||||
version : '0.2.0',
|
||||
meson_version : '>=0.53.0',
|
||||
version : '0.6.0',
|
||||
meson_version : '>=0.56.0',
|
||||
default_options : ['warning_level=2', 'werror=false']
|
||||
)
|
||||
|
||||
r_core_dep = dependency('r_core', method: 'pkg-config')
|
||||
cc = meson.get_compiler('c')
|
||||
|
||||
r2 = find_program('r2', required: true)
|
||||
r2_cflags_res = run_command(r2, '-H', 'R2_CFLAGS', check: true)
|
||||
r2_ldflags_res = run_command(r2, '-H', 'R2_LDFLAGS', check: true)
|
||||
r2_libs_res = run_command(r2, '-H', 'R2_LIBS', check: true)
|
||||
r2_plugdir_res = run_command(r2, '-H', 'R2_LIBR_PLUGINS', check: false)
|
||||
|
||||
r2_compile_args = r2_cflags_res.stdout().strip().split()
|
||||
r2_link_args = r2_ldflags_res.stdout().strip().split() + r2_libs_res.stdout().strip().split()
|
||||
|
||||
r2_plugdir = r2_plugdir_res.stdout().strip()
|
||||
if r2_plugdir == ''
|
||||
r2_plugdir = join_paths(get_option('prefix'), get_option('libdir'), 'radare2', 'last')
|
||||
endif
|
||||
|
||||
# r2 -H always reports gcc style flags, msvc needs its own spelling for them
|
||||
if cc.get_argument_syntax() == 'msvc'
|
||||
msvc_link_args = []
|
||||
foreach flag : r2_link_args
|
||||
if flag.startswith('-L')
|
||||
msvc_link_args += '/LIBPATH:' + flag.substring(2)
|
||||
elif flag.startswith('-l')
|
||||
msvc_link_args += flag.substring(2) + '.lib'
|
||||
else
|
||||
msvc_link_args += flag
|
||||
endif
|
||||
endforeach
|
||||
r2_link_args = msvc_link_args
|
||||
# posix names like strdup are available, just noisy without this
|
||||
r2_compile_args += ['-D_CRT_SECURE_NO_WARNINGS', '-D_CRT_NONSTDC_NO_DEPRECATE']
|
||||
endif
|
||||
|
||||
r_core_dep = declare_dependency(
|
||||
compile_args: r2_compile_args,
|
||||
link_args: r2_link_args,
|
||||
)
|
||||
r2flutter_inc = include_directories('include')
|
||||
|
||||
r2flutter_sources = files(
|
||||
@@ -54,4 +90,17 @@ r2flutter_cli = executable(
|
||||
install: true,
|
||||
)
|
||||
|
||||
# The radare2 core plugin, loaded by r2 as core_flutter.{so,dylib,dll}
|
||||
core_flutter = shared_module(
|
||||
'core_flutter',
|
||||
'src/r2/core_flutter.c',
|
||||
'src/r2/flutter_analysis.c',
|
||||
name_prefix: '',
|
||||
include_directories: r2flutter_inc,
|
||||
link_with: r2flutter_static,
|
||||
dependencies: r_core_dep,
|
||||
install: true,
|
||||
install_dir: r2_plugdir,
|
||||
)
|
||||
|
||||
test('r2flutter-version', r2flutter_cli, args: ['-V'])
|
||||
+30
-20
@@ -5,29 +5,37 @@
|
||||
"function_name_offsets": [24, 8]
|
||||
},
|
||||
"profiles": {
|
||||
"2.10.0": {"cws": 8, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_INT32", "header_fields": 4, "typed_data_internal_base": 108, "typed_data_internal_limit": 150, "typed_data_stride": 3, "cids": {"ILLEGAL": 0, "CLASS": 4, "PATCH_CLASS": 5, "FUNCTION": 6, "CLOSURE_DATA": 7, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 77, "CODE": 16, "OBJECT_POOL": 20, "PC_DESCRIPTORS": 21, "CODE_SOURCE_MAP": 22, "COMPRESSED_STACK_MAPS": 23, "EXCEPTION_HANDLERS": 25, "CONTEXT": 26, "CONTEXT_SCOPE": 27, "SINGLE_TARGET_CACHE": 29, "UNLINKED_CALL": 30, "MONOMORPHIC_SMIABLE_CALL": 31, "CALL_SITE_DATA": 32, "ICDATA": 33, "MEGAMORPHIC_CACHE": 34, "SUBTYPE_TEST_CACHE": 35, "LOADING_UNIT": 36, "LANGUAGE_ERROR": 39, "UNHANDLED_EXCEPTION": 40, "INSTANCE": 42, "LIBRARY_PREFIX": 43, "TYPE_ARGUMENTS": 44, "TYPE": 46, "TYPE_REF": 47, "TYPE_PARAMETER": 48, "CLOSURE": 49, "MINT": 53, "DOUBLE": 54, "FLOAT32X4": 57, "INT32X4": 58, "FLOAT64X2": 59, "TYPED_DATA": 61, "EXTERNAL_TYPED_DATA": 62, "TYPED_DATA_VIEW": 63, "CAPABILITY": 66, "RECEIVE_PORT": 67, "SEND_PORT": 68, "STACK_TRACE": 69, "REGEXP": 70, "WEAK_PROPERTY": 71, "FUTURE_OR": 74, "USER_TAG": 75, "TRANSFERABLE_TYPED_DATA": 76, "ARRAY": 78, "IMMUTABLE_ARRAY": 79, "GROWABLE_OBJECT_ARRAY": 56, "STRING": 80, "ONE_BYTE_STRING": 81, "TWO_BYTE_STRING": 82, "NUM_PREDEFINED_CIDS": 156}},
|
||||
"2.12.0": {"cws": 8, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_INT32", "header_fields": 4, "typed_data_internal_base": 100, "typed_data_internal_limit": 142, "typed_data_stride": 3, "cids": {"ILLEGAL": 0, "CLASS": 4, "PATCH_CLASS": 5, "FUNCTION": 6, "CLOSURE_DATA": 7, "FFI_TRAMPOLINE_DATA": 8, "FIELD": 9, "SCRIPT": 10, "LIBRARY": 11, "NAMESPACE": 12, "KERNEL_PROGRAM_INFO": 13, "WEAK_SERIALIZATION_REFERENCE": 74, "CODE": 14, "OBJECT_POOL": 17, "PC_DESCRIPTORS": 18, "CODE_SOURCE_MAP": 19, "COMPRESSED_STACK_MAPS": 20, "EXCEPTION_HANDLERS": 22, "CONTEXT": 23, "CONTEXT_SCOPE": 24, "SINGLE_TARGET_CACHE": 25, "UNLINKED_CALL": 26, "MONOMORPHIC_SMIABLE_CALL": 27, "CALL_SITE_DATA": 28, "ICDATA": 29, "MEGAMORPHIC_CACHE": 30, "SUBTYPE_TEST_CACHE": 31, "LOADING_UNIT": 32, "LANGUAGE_ERROR": 35, "UNHANDLED_EXCEPTION": 36, "INSTANCE": 38, "LIBRARY_PREFIX": 39, "TYPE_ARGUMENTS": 40, "TYPE": 42, "FUNCTION_TYPE": 43, "TYPE_REF": 44, "TYPE_PARAMETER": 45, "CLOSURE": 46, "MINT": 50, "DOUBLE": 51, "FLOAT32X4": 54, "INT32X4": 55, "FLOAT64X2": 56, "TYPED_DATA": 58, "EXTERNAL_TYPED_DATA": 59, "TYPED_DATA_VIEW": 60, "CAPABILITY": 63, "RECEIVE_PORT": 64, "SEND_PORT": 65, "STACK_TRACE": 66, "REGEXP": 67, "WEAK_PROPERTY": 68, "FUTURE_OR": 71, "USER_TAG": 72, "TRANSFERABLE_TYPED_DATA": 73, "ARRAY": 75, "IMMUTABLE_ARRAY": 76, "GROWABLE_OBJECT_ARRAY": 53, "STRING": 77, "ONE_BYTE_STRING": 78, "TWO_BYTE_STRING": 79, "NUM_PREDEFINED_CIDS": 148}},
|
||||
"2.13.0": {"cws": 8, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_INT32", "header_fields": 5, "typed_data_internal_base": 100, "typed_data_internal_limit": 142, "typed_data_stride": 3, "cids": {"ILLEGAL": 0, "CLASS": 4, "PATCH_CLASS": 5, "FUNCTION": 6, "CLOSURE_DATA": 7, "FFI_TRAMPOLINE_DATA": 8, "FIELD": 9, "SCRIPT": 10, "LIBRARY": 11, "NAMESPACE": 12, "KERNEL_PROGRAM_INFO": 13, "WEAK_SERIALIZATION_REFERENCE": 14, "CODE": 15, "OBJECT_POOL": 18, "PC_DESCRIPTORS": 19, "CODE_SOURCE_MAP": 20, "COMPRESSED_STACK_MAPS": 21, "EXCEPTION_HANDLERS": 23, "CONTEXT": 24, "CONTEXT_SCOPE": 25, "SINGLE_TARGET_CACHE": 26, "UNLINKED_CALL": 27, "MONOMORPHIC_SMIABLE_CALL": 28, "CALL_SITE_DATA": 29, "ICDATA": 30, "MEGAMORPHIC_CACHE": 31, "SUBTYPE_TEST_CACHE": 32, "LOADING_UNIT": 33, "LANGUAGE_ERROR": 36, "UNHANDLED_EXCEPTION": 37, "INSTANCE": 39, "LIBRARY_PREFIX": 40, "TYPE_ARGUMENTS": 41, "TYPE": 43, "FUNCTION_TYPE": 44, "TYPE_REF": 45, "TYPE_PARAMETER": 46, "CLOSURE": 47, "MINT": 51, "DOUBLE": 52, "FLOAT32X4": 55, "INT32X4": 56, "FLOAT64X2": 57, "TYPED_DATA": 59, "EXTERNAL_TYPED_DATA": 60, "TYPED_DATA_VIEW": 61, "CAPABILITY": 64, "RECEIVE_PORT": 65, "SEND_PORT": 66, "STACK_TRACE": 67, "REGEXP": 68, "WEAK_PROPERTY": 69, "FUTURE_OR": 72, "USER_TAG": 73, "TRANSFERABLE_TYPED_DATA": 74, "ARRAY": 75, "IMMUTABLE_ARRAY": 76, "GROWABLE_OBJECT_ARRAY": 54, "STRING": 77, "ONE_BYTE_STRING": 78, "TWO_BYTE_STRING": 79, "NUM_PREDEFINED_CIDS": 148}},
|
||||
"2.14.0": {"cws": 8, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_SHIFT1", "header_fields": 5, "typed_data_internal_base": 104, "typed_data_internal_limit": 146, "typed_data_stride": 3, "cids": {"ILLEGAL": 0, "CLASS": 4, "PATCH_CLASS": 5, "FUNCTION": 6, "TYPE_PARAMETERS": 7, "CLOSURE_DATA": 8, "FFI_TRAMPOLINE_DATA": 9, "FIELD": 10, "SCRIPT": 11, "LIBRARY": 12, "NAMESPACE": 13, "KERNEL_PROGRAM_INFO": 14, "WEAK_SERIALIZATION_REFERENCE": 15, "CODE": 16, "OBJECT_POOL": 20, "PC_DESCRIPTORS": 21, "CODE_SOURCE_MAP": 22, "COMPRESSED_STACK_MAPS": 23, "EXCEPTION_HANDLERS": 25, "CONTEXT": 26, "CONTEXT_SCOPE": 27, "SENTINEL": 28, "SINGLE_TARGET_CACHE": 29, "UNLINKED_CALL": 30, "MONOMORPHIC_SMIABLE_CALL": 31, "CALL_SITE_DATA": 32, "ICDATA": 33, "MEGAMORPHIC_CACHE": 34, "SUBTYPE_TEST_CACHE": 35, "LOADING_UNIT": 36, "LANGUAGE_ERROR": 39, "UNHANDLED_EXCEPTION": 40, "INSTANCE": 42, "LIBRARY_PREFIX": 43, "TYPE_ARGUMENTS": 44, "TYPE": 46, "FUNCTION_TYPE": 47, "TYPE_REF": 48, "TYPE_PARAMETER": 49, "CLOSURE": 50, "MINT": 54, "DOUBLE": 55, "FLOAT32X4": 58, "INT32X4": 59, "FLOAT64X2": 60, "TYPED_DATA": 62, "EXTERNAL_TYPED_DATA": 63, "TYPED_DATA_VIEW": 64, "CAPABILITY": 67, "RECEIVE_PORT": 68, "SEND_PORT": 69, "STACK_TRACE": 70, "REGEXP": 71, "WEAK_PROPERTY": 72, "FUTURE_OR": 74, "USER_TAG": 75, "TRANSFERABLE_TYPED_DATA": 76, "ARRAY": 79, "IMMUTABLE_ARRAY": 80, "GROWABLE_OBJECT_ARRAY": 57, "STRING": 81, "ONE_BYTE_STRING": 82, "TWO_BYTE_STRING": 83, "NUM_PREDEFINED_CIDS": 152}},
|
||||
"2.15.0": {"cws": 8, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_SHIFT1", "header_fields": 5, "typed_data_internal_base": 106, "typed_data_internal_limit": 148, "typed_data_stride": 3, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "CODE": 17, "OBJECT_POOL": 21, "PC_DESCRIPTORS": 22, "CODE_SOURCE_MAP": 23, "COMPRESSED_STACK_MAPS": 24, "EXCEPTION_HANDLERS": 26, "CONTEXT": 27, "CONTEXT_SCOPE": 28, "SENTINEL": 29, "SINGLE_TARGET_CACHE": 30, "UNLINKED_CALL": 31, "MONOMORPHIC_SMIABLE_CALL": 32, "CALL_SITE_DATA": 33, "ICDATA": 34, "MEGAMORPHIC_CACHE": 35, "SUBTYPE_TEST_CACHE": 36, "LOADING_UNIT": 37, "LANGUAGE_ERROR": 40, "UNHANDLED_EXCEPTION": 41, "INSTANCE": 43, "LIBRARY_PREFIX": 44, "TYPE_ARGUMENTS": 45, "TYPE": 47, "FUNCTION_TYPE": 48, "TYPE_REF": 49, "TYPE_PARAMETER": 50, "CLOSURE": 51, "MINT": 55, "DOUBLE": 56, "FLOAT32X4": 58, "INT32X4": 59, "FLOAT64X2": 60, "TYPED_DATA": 62, "EXTERNAL_TYPED_DATA": 63, "TYPED_DATA_VIEW": 64, "CAPABILITY": 67, "RECEIVE_PORT": 68, "SEND_PORT": 69, "STACK_TRACE": 70, "REGEXP": 71, "WEAK_PROPERTY": 72, "FUTURE_OR": 74, "USER_TAG": 75, "TRANSFERABLE_TYPED_DATA": 76, "ARRAY": 81, "IMMUTABLE_ARRAY": 82, "GROWABLE_OBJECT_ARRAY": 83, "STRING": 84, "ONE_BYTE_STRING": 85, "TWO_BYTE_STRING": 86, "NUM_PREDEFINED_CIDS": 154}},
|
||||
"2.16.0": {"cws": 8, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_SHIFT1", "header_fields": 6, "typed_data_internal_base": 106, "typed_data_internal_limit": 148, "typed_data_stride": 3, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "CODE": 17, "OBJECT_POOL": 21, "PC_DESCRIPTORS": 22, "CODE_SOURCE_MAP": 23, "COMPRESSED_STACK_MAPS": 24, "EXCEPTION_HANDLERS": 26, "CONTEXT": 27, "CONTEXT_SCOPE": 28, "SENTINEL": 29, "SINGLE_TARGET_CACHE": 30, "UNLINKED_CALL": 31, "MONOMORPHIC_SMIABLE_CALL": 32, "CALL_SITE_DATA": 33, "ICDATA": 34, "MEGAMORPHIC_CACHE": 35, "SUBTYPE_TEST_CACHE": 36, "LOADING_UNIT": 37, "LANGUAGE_ERROR": 40, "UNHANDLED_EXCEPTION": 41, "INSTANCE": 43, "LIBRARY_PREFIX": 44, "TYPE_ARGUMENTS": 45, "TYPE": 47, "FUNCTION_TYPE": 48, "TYPE_REF": 49, "TYPE_PARAMETER": 50, "CLOSURE": 51, "MINT": 55, "DOUBLE": 56, "FLOAT32X4": 58, "INT32X4": 59, "FLOAT64X2": 60, "TYPED_DATA": 62, "EXTERNAL_TYPED_DATA": 63, "TYPED_DATA_VIEW": 64, "CAPABILITY": 67, "RECEIVE_PORT": 68, "SEND_PORT": 69, "STACK_TRACE": 70, "REGEXP": 71, "WEAK_PROPERTY": 72, "FUTURE_OR": 74, "USER_TAG": 75, "TRANSFERABLE_TYPED_DATA": 76, "ARRAY": 81, "IMMUTABLE_ARRAY": 82, "GROWABLE_OBJECT_ARRAY": 83, "STRING": 84, "ONE_BYTE_STRING": 85, "TWO_BYTE_STRING": 86, "NUM_PREDEFINED_CIDS": 154}},
|
||||
"2.17.0": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_SHIFT1", "header_fields": 6, "typed_data_internal_base": 110, "typed_data_internal_limit": 152, "typed_data_stride": 3, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "CODE": 17, "OBJECT_POOL": 21, "PC_DESCRIPTORS": 22, "CODE_SOURCE_MAP": 23, "COMPRESSED_STACK_MAPS": 24, "EXCEPTION_HANDLERS": 26, "CONTEXT": 27, "CONTEXT_SCOPE": 28, "SENTINEL": 29, "SINGLE_TARGET_CACHE": 30, "UNLINKED_CALL": 31, "MONOMORPHIC_SMIABLE_CALL": 32, "CALL_SITE_DATA": 33, "ICDATA": 34, "MEGAMORPHIC_CACHE": 35, "SUBTYPE_TEST_CACHE": 36, "LOADING_UNIT": 37, "LANGUAGE_ERROR": 40, "UNHANDLED_EXCEPTION": 41, "INSTANCE": 43, "LIBRARY_PREFIX": 44, "TYPE_ARGUMENTS": 45, "TYPE": 47, "FUNCTION_TYPE": 52, "TYPE_REF": 53, "TYPE_PARAMETER": 54, "CLOSURE": 55, "MINT": 59, "DOUBLE": 60, "FLOAT32X4": 62, "INT32X4": 63, "FLOAT64X2": 64, "TYPED_DATA": 66, "EXTERNAL_TYPED_DATA": 67, "TYPED_DATA_VIEW": 68, "CAPABILITY": 71, "RECEIVE_PORT": 72, "SEND_PORT": 73, "STACK_TRACE": 74, "REGEXP": 75, "WEAK_PROPERTY": 76, "WEAK_REFERENCE": 77, "FUTURE_OR": 79, "USER_TAG": 80, "TRANSFERABLE_TYPED_DATA": 81, "ARRAY": 86, "IMMUTABLE_ARRAY": 87, "GROWABLE_OBJECT_ARRAY": 88, "STRING": 89, "ONE_BYTE_STRING": 90, "TWO_BYTE_STRING": 91, "NUM_PREDEFINED_CIDS": 158}},
|
||||
"2.18.0": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_SHIFT1", "header_fields": 5, "typed_data_internal_base": 111, "typed_data_internal_limit": 153, "typed_data_stride": 3, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "CODE": 17, "OBJECT_POOL": 21, "PC_DESCRIPTORS": 22, "CODE_SOURCE_MAP": 23, "COMPRESSED_STACK_MAPS": 24, "EXCEPTION_HANDLERS": 26, "CONTEXT": 27, "CONTEXT_SCOPE": 28, "SENTINEL": 29, "SINGLE_TARGET_CACHE": 30, "UNLINKED_CALL": 31, "MONOMORPHIC_SMIABLE_CALL": 32, "CALL_SITE_DATA": 33, "ICDATA": 34, "MEGAMORPHIC_CACHE": 35, "SUBTYPE_TEST_CACHE": 36, "LOADING_UNIT": 37, "LANGUAGE_ERROR": 40, "UNHANDLED_EXCEPTION": 41, "INSTANCE": 43, "LIBRARY_PREFIX": 44, "TYPE_ARGUMENTS": 45, "TYPE": 47, "FUNCTION_TYPE": 52, "TYPE_REF": 53, "TYPE_PARAMETER": 54, "CLOSURE": 55, "MINT": 59, "DOUBLE": 60, "FLOAT32X4": 62, "INT32X4": 63, "FLOAT64X2": 64, "TYPED_DATA": 66, "EXTERNAL_TYPED_DATA": 67, "TYPED_DATA_VIEW": 68, "CAPABILITY": 71, "RECEIVE_PORT": 72, "SEND_PORT": 73, "STACK_TRACE": 74, "SUSPEND_STATE": 75, "REGEXP": 76, "WEAK_PROPERTY": 77, "WEAK_REFERENCE": 78, "FUTURE_OR": 80, "USER_TAG": 81, "TRANSFERABLE_TYPED_DATA": 82, "ARRAY": 87, "IMMUTABLE_ARRAY": 88, "GROWABLE_OBJECT_ARRAY": 89, "STRING": 90, "ONE_BYTE_STRING": 91, "TWO_BYTE_STRING": 92, "NUM_PREDEFINED_CIDS": 159}},
|
||||
"2.18.2": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "OBJECT_HEADER", "header_fields": 5, "typed_data_internal_base": 112, "typed_data_internal_limit": 168, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "WEAK_ARRAY": 17, "CODE": 18, "OBJECT_POOL": 23, "PC_DESCRIPTORS": 24, "CODE_SOURCE_MAP": 25, "COMPRESSED_STACK_MAPS": 26, "EXCEPTION_HANDLERS": 28, "CONTEXT": 29, "CONTEXT_SCOPE": 30, "SENTINEL": 31, "SINGLE_TARGET_CACHE": 32, "UNLINKED_CALL": 35, "MONOMORPHIC_SMIABLE_CALL": 33, "CALL_SITE_DATA": 34, "ICDATA": 36, "MEGAMORPHIC_CACHE": 37, "SUBTYPE_TEST_CACHE": 38, "LOADING_UNIT": 39, "LANGUAGE_ERROR": 42, "UNHANDLED_EXCEPTION": 43, "INSTANCE": 45, "LIBRARY_PREFIX": 46, "TYPE_ARGUMENTS": 47, "TYPE": 49, "FUNCTION_TYPE": 50, "RECORD_TYPE": 51, "TYPE_PARAMETER": 52, "CLOSURE": 57, "MINT": 61, "DOUBLE": 62, "FLOAT32X4": 64, "INT32X4": 65, "FLOAT64X2": 66, "RECORD": 67, "TYPED_DATA": 69, "EXTERNAL_TYPED_DATA": 70, "TYPED_DATA_VIEW": 71, "CAPABILITY": 74, "RECEIVE_PORT": 75, "SEND_PORT": 76, "STACK_TRACE": 77, "SUSPEND_STATE": 78, "REGEXP": 79, "WEAK_PROPERTY": 80, "WEAK_REFERENCE": 81, "FUTURE_OR": 83, "USER_TAG": 84, "TRANSFERABLE_TYPED_DATA": 85, "MAP": 86, "CONST_MAP": 87, "SET": 88, "CONST_SET": 89, "ARRAY": 90, "IMMUTABLE_ARRAY": 91, "GROWABLE_OBJECT_ARRAY": 92, "STRING": 93, "ONE_BYTE_STRING": 94, "TWO_BYTE_STRING": 95, "NUM_PREDEFINED_CIDS": 175}, "note": "Uses fallback CIDs from 3.9.2 for the in-tree Flutter engine snapshot; not standalone Dart SDK 2.18.2 CIDs."},
|
||||
"2.18.3": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_SHIFT1", "header_fields": 5, "typed_data_internal_base": 111, "typed_data_internal_limit": 153, "typed_data_stride": 3, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "CODE": 17, "OBJECT_POOL": 21, "PC_DESCRIPTORS": 22, "CODE_SOURCE_MAP": 23, "COMPRESSED_STACK_MAPS": 24, "EXCEPTION_HANDLERS": 26, "CONTEXT": 27, "CONTEXT_SCOPE": 28, "SENTINEL": 29, "SINGLE_TARGET_CACHE": 30, "UNLINKED_CALL": 31, "MONOMORPHIC_SMIABLE_CALL": 32, "CALL_SITE_DATA": 33, "ICDATA": 34, "MEGAMORPHIC_CACHE": 35, "SUBTYPE_TEST_CACHE": 36, "LOADING_UNIT": 37, "LANGUAGE_ERROR": 40, "UNHANDLED_EXCEPTION": 41, "INSTANCE": 43, "LIBRARY_PREFIX": 44, "TYPE_ARGUMENTS": 45, "TYPE": 47, "FUNCTION_TYPE": 52, "TYPE_REF": 53, "TYPE_PARAMETER": 54, "CLOSURE": 55, "MINT": 59, "DOUBLE": 60, "FLOAT32X4": 62, "INT32X4": 63, "FLOAT64X2": 64, "TYPED_DATA": 66, "EXTERNAL_TYPED_DATA": 67, "TYPED_DATA_VIEW": 68, "CAPABILITY": 71, "RECEIVE_PORT": 72, "SEND_PORT": 73, "STACK_TRACE": 74, "SUSPEND_STATE": 75, "REGEXP": 76, "WEAK_PROPERTY": 77, "WEAK_REFERENCE": 78, "FUTURE_OR": 80, "USER_TAG": 81, "TRANSFERABLE_TYPED_DATA": 82, "ARRAY": 87, "IMMUTABLE_ARRAY": 88, "GROWABLE_OBJECT_ARRAY": 89, "STRING": 90, "ONE_BYTE_STRING": 91, "TWO_BYTE_STRING": 92, "NUM_PREDEFINED_CIDS": 159}},
|
||||
"2.19.0": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_SHIFT1", "header_fields": 5, "typed_data_internal_base": 113, "typed_data_internal_limit": 169, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "CODE": 17, "OBJECT_POOL": 21, "PC_DESCRIPTORS": 22, "CODE_SOURCE_MAP": 23, "COMPRESSED_STACK_MAPS": 24, "EXCEPTION_HANDLERS": 26, "CONTEXT": 27, "CONTEXT_SCOPE": 28, "SENTINEL": 29, "SINGLE_TARGET_CACHE": 30, "UNLINKED_CALL": 31, "MONOMORPHIC_SMIABLE_CALL": 32, "CALL_SITE_DATA": 33, "ICDATA": 34, "MEGAMORPHIC_CACHE": 35, "SUBTYPE_TEST_CACHE": 36, "LOADING_UNIT": 37, "LANGUAGE_ERROR": 40, "UNHANDLED_EXCEPTION": 41, "INSTANCE": 43, "LIBRARY_PREFIX": 44, "TYPE_ARGUMENTS": 45, "TYPE": 47, "FUNCTION_TYPE": 48, "RECORD_TYPE": 49, "TYPE_REF": 50, "TYPE_PARAMETER": 51, "CLOSURE": 56, "MINT": 60, "DOUBLE": 61, "FLOAT32X4": 63, "INT32X4": 64, "FLOAT64X2": 65, "RECORD": 66, "TYPED_DATA": 68, "EXTERNAL_TYPED_DATA": 69, "TYPED_DATA_VIEW": 70, "CAPABILITY": 73, "RECEIVE_PORT": 74, "SEND_PORT": 75, "STACK_TRACE": 76, "SUSPEND_STATE": 77, "REGEXP": 78, "WEAK_PROPERTY": 79, "WEAK_REFERENCE": 80, "FUTURE_OR": 82, "USER_TAG": 83, "TRANSFERABLE_TYPED_DATA": 84, "MAP": 85, "CONST_MAP": 86, "SET": 87, "CONST_SET": 88, "ARRAY": 89, "IMMUTABLE_ARRAY": 90, "GROWABLE_OBJECT_ARRAY": 91, "STRING": 92, "ONE_BYTE_STRING": 93, "TWO_BYTE_STRING": 94, "NUM_PREDEFINED_CIDS": 176}},
|
||||
"3.0.0": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_SHIFT1", "header_fields": 5, "typed_data_internal_base": 114, "typed_data_internal_limit": 170, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "WEAK_ARRAY": 17, "CODE": 18, "OBJECT_POOL": 22, "PC_DESCRIPTORS": 23, "CODE_SOURCE_MAP": 24, "COMPRESSED_STACK_MAPS": 25, "EXCEPTION_HANDLERS": 27, "CONTEXT": 28, "CONTEXT_SCOPE": 29, "SENTINEL": 30, "SINGLE_TARGET_CACHE": 31, "UNLINKED_CALL": 32, "MONOMORPHIC_SMIABLE_CALL": 33, "CALL_SITE_DATA": 34, "ICDATA": 35, "MEGAMORPHIC_CACHE": 36, "SUBTYPE_TEST_CACHE": 37, "LOADING_UNIT": 38, "LANGUAGE_ERROR": 41, "UNHANDLED_EXCEPTION": 42, "INSTANCE": 44, "LIBRARY_PREFIX": 45, "TYPE_ARGUMENTS": 46, "TYPE": 48, "FUNCTION_TYPE": 49, "RECORD_TYPE": 50, "TYPE_REF": 51, "TYPE_PARAMETER": 52, "CLOSURE": 57, "MINT": 61, "DOUBLE": 62, "FLOAT32X4": 64, "INT32X4": 65, "FLOAT64X2": 66, "RECORD": 67, "TYPED_DATA": 69, "EXTERNAL_TYPED_DATA": 70, "TYPED_DATA_VIEW": 71, "CAPABILITY": 74, "RECEIVE_PORT": 75, "SEND_PORT": 76, "STACK_TRACE": 77, "SUSPEND_STATE": 78, "REGEXP": 79, "WEAK_PROPERTY": 80, "WEAK_REFERENCE": 81, "FUTURE_OR": 83, "USER_TAG": 84, "TRANSFERABLE_TYPED_DATA": 85, "MAP": 86, "CONST_MAP": 87, "SET": 88, "CONST_SET": 89, "ARRAY": 90, "IMMUTABLE_ARRAY": 91, "GROWABLE_OBJECT_ARRAY": 92, "STRING": 93, "ONE_BYTE_STRING": 94, "TWO_BYTE_STRING": 95, "NUM_PREDEFINED_CIDS": 177}},
|
||||
"3.1.0": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_SHIFT1", "header_fields": 5, "typed_data_internal_base": 113, "typed_data_internal_limit": 169, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "WEAK_ARRAY": 17, "CODE": 18, "OBJECT_POOL": 22, "PC_DESCRIPTORS": 23, "CODE_SOURCE_MAP": 24, "COMPRESSED_STACK_MAPS": 25, "EXCEPTION_HANDLERS": 27, "CONTEXT": 28, "CONTEXT_SCOPE": 29, "SENTINEL": 30, "SINGLE_TARGET_CACHE": 31, "UNLINKED_CALL": 32, "MONOMORPHIC_SMIABLE_CALL": 33, "CALL_SITE_DATA": 34, "ICDATA": 35, "MEGAMORPHIC_CACHE": 36, "SUBTYPE_TEST_CACHE": 37, "LOADING_UNIT": 38, "LANGUAGE_ERROR": 41, "UNHANDLED_EXCEPTION": 42, "INSTANCE": 44, "LIBRARY_PREFIX": 45, "TYPE_ARGUMENTS": 46, "TYPE": 48, "FUNCTION_TYPE": 49, "RECORD_TYPE": 50, "TYPE_PARAMETER": 51, "CLOSURE": 56, "MINT": 60, "DOUBLE": 61, "FLOAT32X4": 63, "INT32X4": 64, "FLOAT64X2": 65, "RECORD": 66, "TYPED_DATA": 68, "EXTERNAL_TYPED_DATA": 69, "TYPED_DATA_VIEW": 70, "CAPABILITY": 73, "RECEIVE_PORT": 74, "SEND_PORT": 75, "STACK_TRACE": 76, "SUSPEND_STATE": 77, "REGEXP": 78, "WEAK_PROPERTY": 79, "WEAK_REFERENCE": 80, "FUTURE_OR": 82, "USER_TAG": 83, "TRANSFERABLE_TYPED_DATA": 84, "MAP": 85, "CONST_MAP": 86, "SET": 87, "CONST_SET": 88, "ARRAY": 89, "IMMUTABLE_ARRAY": 90, "GROWABLE_OBJECT_ARRAY": 91, "STRING": 92, "ONE_BYTE_STRING": 93, "TWO_BYTE_STRING": 94, "NUM_PREDEFINED_CIDS": 176}},
|
||||
"3.2.0": {"cws": 8, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_SHIFT1", "header_fields": 5, "typed_data_internal_base": 113, "typed_data_internal_limit": 169, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "WEAK_ARRAY": 17, "CODE": 18, "OBJECT_POOL": 22, "PC_DESCRIPTORS": 23, "CODE_SOURCE_MAP": 24, "COMPRESSED_STACK_MAPS": 25, "EXCEPTION_HANDLERS": 27, "CONTEXT": 28, "CONTEXT_SCOPE": 29, "SENTINEL": 30, "SINGLE_TARGET_CACHE": 31, "UNLINKED_CALL": 32, "MONOMORPHIC_SMIABLE_CALL": 33, "CALL_SITE_DATA": 34, "ICDATA": 35, "MEGAMORPHIC_CACHE": 36, "SUBTYPE_TEST_CACHE": 37, "LOADING_UNIT": 38, "LANGUAGE_ERROR": 41, "UNHANDLED_EXCEPTION": 42, "INSTANCE": 44, "LIBRARY_PREFIX": 45, "TYPE_ARGUMENTS": 46, "TYPE": 48, "FUNCTION_TYPE": 49, "RECORD_TYPE": 50, "TYPE_PARAMETER": 51, "CLOSURE": 56, "MINT": 60, "DOUBLE": 61, "FLOAT32X4": 63, "INT32X4": 64, "FLOAT64X2": 65, "RECORD": 66, "TYPED_DATA": 68, "EXTERNAL_TYPED_DATA": 69, "TYPED_DATA_VIEW": 70, "CAPABILITY": 73, "RECEIVE_PORT": 74, "SEND_PORT": 75, "STACK_TRACE": 76, "SUSPEND_STATE": 77, "REGEXP": 78, "WEAK_PROPERTY": 79, "WEAK_REFERENCE": 80, "FUTURE_OR": 82, "USER_TAG": 83, "TRANSFERABLE_TYPED_DATA": 84, "MAP": 85, "CONST_MAP": 86, "SET": 87, "CONST_SET": 88, "ARRAY": 89, "IMMUTABLE_ARRAY": 90, "GROWABLE_OBJECT_ARRAY": 91, "STRING": 92, "ONE_BYTE_STRING": 93, "TWO_BYTE_STRING": 94, "NUM_PREDEFINED_CIDS": 176}},
|
||||
"3.3.0": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_SHIFT1", "header_fields": 5, "typed_data_internal_base": 113, "typed_data_internal_limit": 169, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "WEAK_ARRAY": 17, "CODE": 18, "OBJECT_POOL": 22, "PC_DESCRIPTORS": 23, "CODE_SOURCE_MAP": 24, "COMPRESSED_STACK_MAPS": 25, "EXCEPTION_HANDLERS": 27, "CONTEXT": 28, "CONTEXT_SCOPE": 29, "SENTINEL": 30, "SINGLE_TARGET_CACHE": 31, "UNLINKED_CALL": 32, "MONOMORPHIC_SMIABLE_CALL": 33, "CALL_SITE_DATA": 34, "ICDATA": 35, "MEGAMORPHIC_CACHE": 36, "SUBTYPE_TEST_CACHE": 37, "LOADING_UNIT": 38, "LANGUAGE_ERROR": 41, "UNHANDLED_EXCEPTION": 42, "INSTANCE": 44, "LIBRARY_PREFIX": 45, "TYPE_ARGUMENTS": 46, "TYPE": 48, "FUNCTION_TYPE": 49, "RECORD_TYPE": 50, "TYPE_PARAMETER": 51, "CLOSURE": 56, "MINT": 60, "DOUBLE": 61, "FLOAT32X4": 63, "INT32X4": 64, "FLOAT64X2": 65, "RECORD": 66, "TYPED_DATA": 68, "EXTERNAL_TYPED_DATA": 69, "TYPED_DATA_VIEW": 70, "CAPABILITY": 73, "RECEIVE_PORT": 74, "SEND_PORT": 75, "STACK_TRACE": 76, "SUSPEND_STATE": 77, "REGEXP": 78, "WEAK_PROPERTY": 79, "WEAK_REFERENCE": 80, "FUTURE_OR": 82, "USER_TAG": 83, "TRANSFERABLE_TYPED_DATA": 84, "MAP": 85, "CONST_MAP": 86, "SET": 87, "CONST_SET": 88, "ARRAY": 89, "IMMUTABLE_ARRAY": 90, "GROWABLE_OBJECT_ARRAY": 91, "STRING": 92, "ONE_BYTE_STRING": 93, "TWO_BYTE_STRING": 94, "NUM_PREDEFINED_CIDS": 176}},
|
||||
"3.4.0": {"cws": 8, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "OBJECT_HEADER", "header_fields": 5, "typed_data_internal_base": 111, "typed_data_internal_limit": 167, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "WEAK_ARRAY": 17, "CODE": 18, "OBJECT_POOL": 22, "PC_DESCRIPTORS": 23, "CODE_SOURCE_MAP": 24, "COMPRESSED_STACK_MAPS": 25, "EXCEPTION_HANDLERS": 27, "CONTEXT": 28, "CONTEXT_SCOPE": 29, "SENTINEL": 30, "SINGLE_TARGET_CACHE": 31, "UNLINKED_CALL": 32, "MONOMORPHIC_SMIABLE_CALL": 33, "CALL_SITE_DATA": 34, "ICDATA": 35, "MEGAMORPHIC_CACHE": 36, "SUBTYPE_TEST_CACHE": 37, "LOADING_UNIT": 38, "LANGUAGE_ERROR": 41, "UNHANDLED_EXCEPTION": 42, "INSTANCE": 44, "LIBRARY_PREFIX": 45, "TYPE_ARGUMENTS": 46, "TYPE": 48, "FUNCTION_TYPE": 49, "RECORD_TYPE": 50, "TYPE_PARAMETER": 51, "CLOSURE": 56, "MINT": 60, "DOUBLE": 61, "FLOAT32X4": 63, "INT32X4": 64, "FLOAT64X2": 65, "RECORD": 66, "TYPED_DATA": 68, "EXTERNAL_TYPED_DATA": 69, "TYPED_DATA_VIEW": 70, "CAPABILITY": 73, "RECEIVE_PORT": 74, "SEND_PORT": 75, "STACK_TRACE": 76, "SUSPEND_STATE": 77, "REGEXP": 78, "WEAK_PROPERTY": 79, "WEAK_REFERENCE": 80, "FUTURE_OR": 82, "USER_TAG": 83, "TRANSFERABLE_TYPED_DATA": 84, "MAP": 85, "CONST_MAP": 86, "SET": 87, "CONST_SET": 88, "ARRAY": 89, "IMMUTABLE_ARRAY": 90, "GROWABLE_OBJECT_ARRAY": 91, "STRING": 92, "ONE_BYTE_STRING": 93, "TWO_BYTE_STRING": 94, "NUM_PREDEFINED_CIDS": 174}},
|
||||
"3.5.0": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "OBJECT_HEADER", "header_fields": 5, "typed_data_internal_base": 111, "typed_data_internal_limit": 167, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "WEAK_ARRAY": 17, "CODE": 18, "OBJECT_POOL": 22, "PC_DESCRIPTORS": 23, "CODE_SOURCE_MAP": 24, "COMPRESSED_STACK_MAPS": 25, "EXCEPTION_HANDLERS": 27, "CONTEXT": 28, "CONTEXT_SCOPE": 29, "SENTINEL": 30, "SINGLE_TARGET_CACHE": 31, "UNLINKED_CALL": 32, "MONOMORPHIC_SMIABLE_CALL": 33, "CALL_SITE_DATA": 34, "ICDATA": 35, "MEGAMORPHIC_CACHE": 36, "SUBTYPE_TEST_CACHE": 37, "LOADING_UNIT": 38, "LANGUAGE_ERROR": 41, "UNHANDLED_EXCEPTION": 42, "INSTANCE": 44, "LIBRARY_PREFIX": 45, "TYPE_ARGUMENTS": 46, "TYPE": 48, "FUNCTION_TYPE": 49, "RECORD_TYPE": 50, "TYPE_PARAMETER": 51, "CLOSURE": 56, "MINT": 60, "DOUBLE": 61, "FLOAT32X4": 63, "INT32X4": 64, "FLOAT64X2": 65, "RECORD": 66, "TYPED_DATA": 68, "EXTERNAL_TYPED_DATA": 69, "TYPED_DATA_VIEW": 70, "CAPABILITY": 73, "RECEIVE_PORT": 74, "SEND_PORT": 75, "STACK_TRACE": 76, "SUSPEND_STATE": 77, "REGEXP": 78, "WEAK_PROPERTY": 79, "WEAK_REFERENCE": 80, "FUTURE_OR": 82, "USER_TAG": 83, "TRANSFERABLE_TYPED_DATA": 84, "MAP": 85, "CONST_MAP": 86, "SET": 87, "CONST_SET": 88, "ARRAY": 89, "IMMUTABLE_ARRAY": 90, "GROWABLE_OBJECT_ARRAY": 91, "STRING": 92, "ONE_BYTE_STRING": 93, "TWO_BYTE_STRING": 94, "NUM_PREDEFINED_CIDS": 174}},
|
||||
"3.6.0": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "OBJECT_HEADER", "header_fields": 5, "typed_data_internal_base": 112, "typed_data_internal_limit": 168, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "WEAK_ARRAY": 17, "CODE": 18, "OBJECT_POOL": 23, "PC_DESCRIPTORS": 24, "CODE_SOURCE_MAP": 25, "COMPRESSED_STACK_MAPS": 26, "EXCEPTION_HANDLERS": 28, "CONTEXT": 29, "CONTEXT_SCOPE": 30, "SENTINEL": 31, "SINGLE_TARGET_CACHE": 32, "UNLINKED_CALL": 33, "MONOMORPHIC_SMIABLE_CALL": 34, "CALL_SITE_DATA": 35, "ICDATA": 36, "MEGAMORPHIC_CACHE": 37, "SUBTYPE_TEST_CACHE": 38, "LOADING_UNIT": 39, "LANGUAGE_ERROR": 42, "UNHANDLED_EXCEPTION": 43, "INSTANCE": 45, "LIBRARY_PREFIX": 46, "TYPE_ARGUMENTS": 47, "TYPE": 49, "FUNCTION_TYPE": 50, "RECORD_TYPE": 51, "TYPE_PARAMETER": 52, "CLOSURE": 57, "MINT": 61, "DOUBLE": 62, "FLOAT32X4": 64, "INT32X4": 65, "FLOAT64X2": 66, "RECORD": 67, "TYPED_DATA": 69, "EXTERNAL_TYPED_DATA": 70, "TYPED_DATA_VIEW": 71, "CAPABILITY": 74, "RECEIVE_PORT": 75, "SEND_PORT": 76, "STACK_TRACE": 77, "SUSPEND_STATE": 78, "REGEXP": 79, "WEAK_PROPERTY": 80, "WEAK_REFERENCE": 81, "FUTURE_OR": 83, "USER_TAG": 84, "TRANSFERABLE_TYPED_DATA": 85, "MAP": 86, "CONST_MAP": 87, "SET": 88, "CONST_SET": 89, "ARRAY": 90, "IMMUTABLE_ARRAY": 91, "GROWABLE_OBJECT_ARRAY": 92, "STRING": 93, "ONE_BYTE_STRING": 94, "TWO_BYTE_STRING": 95, "NUM_PREDEFINED_CIDS": 175}},
|
||||
"3.9.0": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "OBJECT_HEADER", "header_fields": 5, "typed_data_internal_base": 112, "typed_data_internal_limit": 168, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "WEAK_ARRAY": 17, "CODE": 18, "OBJECT_POOL": 23, "PC_DESCRIPTORS": 24, "CODE_SOURCE_MAP": 25, "COMPRESSED_STACK_MAPS": 26, "EXCEPTION_HANDLERS": 28, "CONTEXT": 29, "CONTEXT_SCOPE": 30, "SENTINEL": 31, "SINGLE_TARGET_CACHE": 32, "UNLINKED_CALL": 35, "MONOMORPHIC_SMIABLE_CALL": 33, "CALL_SITE_DATA": 34, "ICDATA": 36, "MEGAMORPHIC_CACHE": 37, "SUBTYPE_TEST_CACHE": 38, "LOADING_UNIT": 39, "LANGUAGE_ERROR": 42, "UNHANDLED_EXCEPTION": 43, "INSTANCE": 45, "LIBRARY_PREFIX": 46, "TYPE_ARGUMENTS": 47, "TYPE": 49, "FUNCTION_TYPE": 50, "RECORD_TYPE": 51, "TYPE_PARAMETER": 52, "CLOSURE": 57, "MINT": 61, "DOUBLE": 62, "FLOAT32X4": 64, "INT32X4": 65, "FLOAT64X2": 66, "RECORD": 67, "TYPED_DATA": 69, "EXTERNAL_TYPED_DATA": 70, "TYPED_DATA_VIEW": 71, "CAPABILITY": 74, "RECEIVE_PORT": 75, "SEND_PORT": 76, "STACK_TRACE": 77, "SUSPEND_STATE": 78, "REGEXP": 79, "WEAK_PROPERTY": 80, "WEAK_REFERENCE": 81, "FUTURE_OR": 83, "USER_TAG": 84, "TRANSFERABLE_TYPED_DATA": 85, "MAP": 86, "CONST_MAP": 87, "SET": 88, "CONST_SET": 89, "ARRAY": 90, "IMMUTABLE_ARRAY": 91, "GROWABLE_OBJECT_ARRAY": 92, "STRING": 93, "ONE_BYTE_STRING": 94, "TWO_BYTE_STRING": 95, "NUM_PREDEFINED_CIDS": 175}}
|
||||
"2.10.0": {"cws": 8, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_INT32", "header_fields": 4, "typed_data_internal_base": 108, "typed_data_internal_limit": 150, "typed_data_stride": 3, "cids": {"ILLEGAL": 0, "CLASS": 4, "PATCH_CLASS": 5, "FUNCTION": 6, "CLOSURE_DATA": 7, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 77, "CODE": 16, "OBJECT_POOL": 20, "PC_DESCRIPTORS": 21, "CODE_SOURCE_MAP": 22, "COMPRESSED_STACK_MAPS": 23, "LOCAL_VAR_DESCRIPTORS": 24, "EXCEPTION_HANDLERS": 25, "CONTEXT": 26, "CONTEXT_SCOPE": 27, "SINGLE_TARGET_CACHE": 29, "UNLINKED_CALL": 30, "MONOMORPHIC_SMIABLE_CALL": 31, "CALL_SITE_DATA": 32, "ICDATA": 33, "MEGAMORPHIC_CACHE": 34, "SUBTYPE_TEST_CACHE": 35, "LOADING_UNIT": 36, "API_ERROR": 38, "LANGUAGE_ERROR": 39, "UNHANDLED_EXCEPTION": 40, "UNWIND_ERROR": 41, "INSTANCE": 42, "LIBRARY_PREFIX": 43, "TYPE_ARGUMENTS": 44, "TYPE": 46, "TYPE_REF": 47, "TYPE_PARAMETER": 48, "CLOSURE": 49, "MINT": 53, "DOUBLE": 54, "FLOAT32X4": 57, "INT32X4": 58, "FLOAT64X2": 59, "TYPED_DATA": 61, "EXTERNAL_TYPED_DATA": 62, "TYPED_DATA_VIEW": 63, "CAPABILITY": 66, "RECEIVE_PORT": 67, "SEND_PORT": 68, "STACK_TRACE": 69, "REGEXP": 70, "WEAK_PROPERTY": 71, "FUTURE_OR": 74, "USER_TAG": 75, "TRANSFERABLE_TYPED_DATA": 76, "ARRAY": 78, "IMMUTABLE_ARRAY": 79, "GROWABLE_OBJECT_ARRAY": 56, "STRING": 80, "ONE_BYTE_STRING": 81, "TWO_BYTE_STRING": 82, "NUM_PREDEFINED_CIDS": 156}},
|
||||
"2.12.0": {"cws": 8, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_INT32", "header_fields": 5, "typed_data_internal_base": 100, "typed_data_internal_limit": 142, "typed_data_stride": 3, "cids": {"ILLEGAL": 0, "CLASS": 4, "PATCH_CLASS": 5, "FUNCTION": 6, "CLOSURE_DATA": 7, "FFI_TRAMPOLINE_DATA": 8, "FIELD": 9, "SCRIPT": 10, "LIBRARY": 11, "NAMESPACE": 12, "KERNEL_PROGRAM_INFO": 13, "WEAK_SERIALIZATION_REFERENCE": 74, "CODE": 14, "OBJECT_POOL": 17, "PC_DESCRIPTORS": 18, "CODE_SOURCE_MAP": 19, "COMPRESSED_STACK_MAPS": 20, "LOCAL_VAR_DESCRIPTORS": 21, "EXCEPTION_HANDLERS": 22, "CONTEXT": 23, "CONTEXT_SCOPE": 24, "SINGLE_TARGET_CACHE": 25, "UNLINKED_CALL": 26, "MONOMORPHIC_SMIABLE_CALL": 27, "CALL_SITE_DATA": 28, "ICDATA": 29, "MEGAMORPHIC_CACHE": 30, "SUBTYPE_TEST_CACHE": 31, "LOADING_UNIT": 32, "API_ERROR": 34, "LANGUAGE_ERROR": 35, "UNHANDLED_EXCEPTION": 36, "UNWIND_ERROR": 37, "INSTANCE": 38, "LIBRARY_PREFIX": 39, "TYPE_ARGUMENTS": 40, "TYPE": 42, "FUNCTION_TYPE": 43, "TYPE_REF": 44, "TYPE_PARAMETER": 45, "CLOSURE": 46, "MINT": 50, "DOUBLE": 51, "FLOAT32X4": 54, "INT32X4": 55, "FLOAT64X2": 56, "TYPED_DATA": 58, "EXTERNAL_TYPED_DATA": 59, "TYPED_DATA_VIEW": 60, "CAPABILITY": 63, "RECEIVE_PORT": 64, "SEND_PORT": 65, "STACK_TRACE": 66, "REGEXP": 67, "WEAK_PROPERTY": 68, "FUTURE_OR": 71, "USER_TAG": 72, "TRANSFERABLE_TYPED_DATA": 73, "ARRAY": 75, "IMMUTABLE_ARRAY": 76, "GROWABLE_OBJECT_ARRAY": 53, "STRING": 77, "ONE_BYTE_STRING": 78, "TWO_BYTE_STRING": 79, "NUM_PREDEFINED_CIDS": 148}},
|
||||
"2.13.0": {"cws": 8, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_INT32", "header_fields": 5, "typed_data_internal_base": 100, "typed_data_internal_limit": 142, "typed_data_stride": 3, "cids": {"ILLEGAL": 0, "CLASS": 4, "PATCH_CLASS": 5, "FUNCTION": 6, "CLOSURE_DATA": 7, "FFI_TRAMPOLINE_DATA": 8, "FIELD": 9, "SCRIPT": 10, "LIBRARY": 11, "NAMESPACE": 12, "KERNEL_PROGRAM_INFO": 13, "WEAK_SERIALIZATION_REFERENCE": 14, "CODE": 15, "OBJECT_POOL": 18, "PC_DESCRIPTORS": 19, "CODE_SOURCE_MAP": 20, "COMPRESSED_STACK_MAPS": 21, "LOCAL_VAR_DESCRIPTORS": 22, "EXCEPTION_HANDLERS": 23, "CONTEXT": 24, "CONTEXT_SCOPE": 25, "SINGLE_TARGET_CACHE": 26, "UNLINKED_CALL": 27, "MONOMORPHIC_SMIABLE_CALL": 28, "CALL_SITE_DATA": 29, "ICDATA": 30, "MEGAMORPHIC_CACHE": 31, "SUBTYPE_TEST_CACHE": 32, "LOADING_UNIT": 33, "API_ERROR": 35, "LANGUAGE_ERROR": 36, "UNHANDLED_EXCEPTION": 37, "UNWIND_ERROR": 38, "INSTANCE": 39, "LIBRARY_PREFIX": 40, "TYPE_ARGUMENTS": 41, "TYPE": 43, "FUNCTION_TYPE": 44, "TYPE_REF": 45, "TYPE_PARAMETER": 46, "CLOSURE": 47, "MINT": 51, "DOUBLE": 52, "FLOAT32X4": 55, "INT32X4": 56, "FLOAT64X2": 57, "TYPED_DATA": 59, "EXTERNAL_TYPED_DATA": 60, "TYPED_DATA_VIEW": 61, "CAPABILITY": 64, "RECEIVE_PORT": 65, "SEND_PORT": 66, "STACK_TRACE": 67, "REGEXP": 68, "WEAK_PROPERTY": 69, "FUTURE_OR": 72, "USER_TAG": 73, "TRANSFERABLE_TYPED_DATA": 74, "ARRAY": 75, "IMMUTABLE_ARRAY": 76, "GROWABLE_OBJECT_ARRAY": 54, "STRING": 77, "ONE_BYTE_STRING": 78, "TWO_BYTE_STRING": 79, "NUM_PREDEFINED_CIDS": 148}},
|
||||
"2.14.0": {"cws": 8, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_SHIFT1", "header_fields": 5, "typed_data_internal_base": 104, "typed_data_internal_limit": 146, "typed_data_stride": 3, "cids": {"ILLEGAL": 0, "CLASS": 4, "PATCH_CLASS": 5, "FUNCTION": 6, "TYPE_PARAMETERS": 7, "CLOSURE_DATA": 8, "FFI_TRAMPOLINE_DATA": 9, "FIELD": 10, "SCRIPT": 11, "LIBRARY": 12, "NAMESPACE": 13, "KERNEL_PROGRAM_INFO": 14, "WEAK_SERIALIZATION_REFERENCE": 15, "CODE": 16, "OBJECT_POOL": 20, "PC_DESCRIPTORS": 21, "CODE_SOURCE_MAP": 22, "COMPRESSED_STACK_MAPS": 23, "LOCAL_VAR_DESCRIPTORS": 24, "EXCEPTION_HANDLERS": 25, "CONTEXT": 26, "CONTEXT_SCOPE": 27, "SENTINEL": 28, "SINGLE_TARGET_CACHE": 29, "UNLINKED_CALL": 30, "MONOMORPHIC_SMIABLE_CALL": 31, "CALL_SITE_DATA": 32, "ICDATA": 33, "MEGAMORPHIC_CACHE": 34, "SUBTYPE_TEST_CACHE": 35, "LOADING_UNIT": 36, "API_ERROR": 38, "LANGUAGE_ERROR": 39, "UNHANDLED_EXCEPTION": 40, "UNWIND_ERROR": 41, "INSTANCE": 42, "LIBRARY_PREFIX": 43, "TYPE_ARGUMENTS": 44, "TYPE": 46, "FUNCTION_TYPE": 47, "TYPE_REF": 48, "TYPE_PARAMETER": 49, "CLOSURE": 50, "MINT": 54, "DOUBLE": 55, "FLOAT32X4": 58, "INT32X4": 59, "FLOAT64X2": 60, "TYPED_DATA": 62, "EXTERNAL_TYPED_DATA": 63, "TYPED_DATA_VIEW": 64, "CAPABILITY": 67, "RECEIVE_PORT": 68, "SEND_PORT": 69, "STACK_TRACE": 70, "REGEXP": 71, "WEAK_PROPERTY": 72, "FUTURE_OR": 74, "USER_TAG": 75, "TRANSFERABLE_TYPED_DATA": 76, "ARRAY": 79, "IMMUTABLE_ARRAY": 80, "GROWABLE_OBJECT_ARRAY": 57, "STRING": 81, "ONE_BYTE_STRING": 82, "TWO_BYTE_STRING": 83, "NUM_PREDEFINED_CIDS": 152}},
|
||||
"2.15.0": {"cws": 8, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_SHIFT1", "header_fields": 5, "typed_data_internal_base": 106, "typed_data_internal_limit": 148, "typed_data_stride": 3, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "CODE": 17, "OBJECT_POOL": 21, "PC_DESCRIPTORS": 22, "CODE_SOURCE_MAP": 23, "COMPRESSED_STACK_MAPS": 24, "LOCAL_VAR_DESCRIPTORS": 25, "EXCEPTION_HANDLERS": 26, "CONTEXT": 27, "CONTEXT_SCOPE": 28, "SENTINEL": 29, "SINGLE_TARGET_CACHE": 30, "UNLINKED_CALL": 31, "MONOMORPHIC_SMIABLE_CALL": 32, "CALL_SITE_DATA": 33, "ICDATA": 34, "MEGAMORPHIC_CACHE": 35, "SUBTYPE_TEST_CACHE": 36, "LOADING_UNIT": 37, "API_ERROR": 39, "LANGUAGE_ERROR": 40, "UNHANDLED_EXCEPTION": 41, "UNWIND_ERROR": 42, "INSTANCE": 43, "LIBRARY_PREFIX": 44, "TYPE_ARGUMENTS": 45, "TYPE": 47, "FUNCTION_TYPE": 48, "TYPE_REF": 49, "TYPE_PARAMETER": 50, "CLOSURE": 51, "MINT": 55, "DOUBLE": 56, "FLOAT32X4": 58, "INT32X4": 59, "FLOAT64X2": 60, "TYPED_DATA": 62, "EXTERNAL_TYPED_DATA": 63, "TYPED_DATA_VIEW": 64, "CAPABILITY": 67, "RECEIVE_PORT": 68, "SEND_PORT": 69, "STACK_TRACE": 70, "REGEXP": 71, "WEAK_PROPERTY": 72, "FUTURE_OR": 74, "USER_TAG": 75, "TRANSFERABLE_TYPED_DATA": 76, "ARRAY": 81, "IMMUTABLE_ARRAY": 82, "GROWABLE_OBJECT_ARRAY": 83, "STRING": 84, "ONE_BYTE_STRING": 85, "TWO_BYTE_STRING": 86, "NUM_PREDEFINED_CIDS": 154}},
|
||||
"2.16.0": {"cws": 8, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_SHIFT1", "header_fields": 6, "typed_data_internal_base": 106, "typed_data_internal_limit": 148, "typed_data_stride": 3, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "CODE": 17, "OBJECT_POOL": 21, "PC_DESCRIPTORS": 22, "CODE_SOURCE_MAP": 23, "COMPRESSED_STACK_MAPS": 24, "LOCAL_VAR_DESCRIPTORS": 25, "EXCEPTION_HANDLERS": 26, "CONTEXT": 27, "CONTEXT_SCOPE": 28, "SENTINEL": 29, "SINGLE_TARGET_CACHE": 30, "UNLINKED_CALL": 31, "MONOMORPHIC_SMIABLE_CALL": 32, "CALL_SITE_DATA": 33, "ICDATA": 34, "MEGAMORPHIC_CACHE": 35, "SUBTYPE_TEST_CACHE": 36, "LOADING_UNIT": 37, "API_ERROR": 39, "LANGUAGE_ERROR": 40, "UNHANDLED_EXCEPTION": 41, "UNWIND_ERROR": 42, "INSTANCE": 43, "LIBRARY_PREFIX": 44, "TYPE_ARGUMENTS": 45, "TYPE": 47, "FUNCTION_TYPE": 48, "TYPE_REF": 49, "TYPE_PARAMETER": 50, "CLOSURE": 51, "MINT": 55, "DOUBLE": 56, "FLOAT32X4": 58, "INT32X4": 59, "FLOAT64X2": 60, "TYPED_DATA": 62, "EXTERNAL_TYPED_DATA": 63, "TYPED_DATA_VIEW": 64, "CAPABILITY": 67, "RECEIVE_PORT": 68, "SEND_PORT": 69, "STACK_TRACE": 70, "REGEXP": 71, "WEAK_PROPERTY": 72, "FUTURE_OR": 74, "USER_TAG": 75, "TRANSFERABLE_TYPED_DATA": 76, "ARRAY": 81, "IMMUTABLE_ARRAY": 82, "GROWABLE_OBJECT_ARRAY": 83, "STRING": 84, "ONE_BYTE_STRING": 85, "TWO_BYTE_STRING": 86, "NUM_PREDEFINED_CIDS": 154}},
|
||||
"2.17.0": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_SHIFT1", "header_fields": 6, "typed_data_internal_base": 110, "typed_data_internal_limit": 152, "typed_data_stride": 3, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "CODE": 17, "OBJECT_POOL": 21, "PC_DESCRIPTORS": 22, "CODE_SOURCE_MAP": 23, "COMPRESSED_STACK_MAPS": 24, "LOCAL_VAR_DESCRIPTORS": 25, "EXCEPTION_HANDLERS": 26, "CONTEXT": 27, "CONTEXT_SCOPE": 28, "SENTINEL": 29, "SINGLE_TARGET_CACHE": 30, "UNLINKED_CALL": 31, "MONOMORPHIC_SMIABLE_CALL": 32, "CALL_SITE_DATA": 33, "ICDATA": 34, "MEGAMORPHIC_CACHE": 35, "SUBTYPE_TEST_CACHE": 36, "LOADING_UNIT": 37, "API_ERROR": 39, "LANGUAGE_ERROR": 40, "UNHANDLED_EXCEPTION": 41, "UNWIND_ERROR": 42, "INSTANCE": 43, "LIBRARY_PREFIX": 44, "TYPE_ARGUMENTS": 45, "TYPE": 47, "FUNCTION_TYPE": 52, "TYPE_REF": 53, "TYPE_PARAMETER": 54, "CLOSURE": 55, "MINT": 59, "DOUBLE": 60, "FLOAT32X4": 62, "INT32X4": 63, "FLOAT64X2": 64, "TYPED_DATA": 66, "EXTERNAL_TYPED_DATA": 67, "TYPED_DATA_VIEW": 68, "CAPABILITY": 71, "RECEIVE_PORT": 72, "SEND_PORT": 73, "STACK_TRACE": 74, "REGEXP": 75, "WEAK_PROPERTY": 76, "WEAK_REFERENCE": 77, "FUTURE_OR": 79, "USER_TAG": 80, "TRANSFERABLE_TYPED_DATA": 81, "ARRAY": 86, "IMMUTABLE_ARRAY": 87, "GROWABLE_OBJECT_ARRAY": 88, "STRING": 89, "ONE_BYTE_STRING": 90, "TWO_BYTE_STRING": 91, "NUM_PREDEFINED_CIDS": 158}},
|
||||
"2.18.0": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_SHIFT1", "header_fields": 5, "typed_data_internal_base": 111, "typed_data_internal_limit": 153, "typed_data_stride": 3, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "CODE": 17, "OBJECT_POOL": 21, "PC_DESCRIPTORS": 22, "CODE_SOURCE_MAP": 23, "COMPRESSED_STACK_MAPS": 24, "LOCAL_VAR_DESCRIPTORS": 25, "EXCEPTION_HANDLERS": 26, "CONTEXT": 27, "CONTEXT_SCOPE": 28, "SENTINEL": 29, "SINGLE_TARGET_CACHE": 30, "UNLINKED_CALL": 31, "MONOMORPHIC_SMIABLE_CALL": 32, "CALL_SITE_DATA": 33, "ICDATA": 34, "MEGAMORPHIC_CACHE": 35, "SUBTYPE_TEST_CACHE": 36, "LOADING_UNIT": 37, "API_ERROR": 39, "LANGUAGE_ERROR": 40, "UNHANDLED_EXCEPTION": 41, "UNWIND_ERROR": 42, "INSTANCE": 43, "LIBRARY_PREFIX": 44, "TYPE_ARGUMENTS": 45, "TYPE": 47, "FUNCTION_TYPE": 52, "TYPE_REF": 53, "TYPE_PARAMETER": 54, "CLOSURE": 55, "MINT": 59, "DOUBLE": 60, "FLOAT32X4": 62, "INT32X4": 63, "FLOAT64X2": 64, "TYPED_DATA": 66, "EXTERNAL_TYPED_DATA": 67, "TYPED_DATA_VIEW": 68, "CAPABILITY": 71, "RECEIVE_PORT": 72, "SEND_PORT": 73, "STACK_TRACE": 74, "SUSPEND_STATE": 75, "REGEXP": 76, "WEAK_PROPERTY": 77, "WEAK_REFERENCE": 78, "FUTURE_OR": 80, "USER_TAG": 81, "TRANSFERABLE_TYPED_DATA": 82, "MAP": 83, "CONST_MAP": 84, "SET": 85, "CONST_SET": 86, "ARRAY": 87, "IMMUTABLE_ARRAY": 88, "GROWABLE_OBJECT_ARRAY": 89, "STRING": 90, "ONE_BYTE_STRING": 91, "TWO_BYTE_STRING": 92, "NUM_PREDEFINED_CIDS": 159}},
|
||||
"2.18.2": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "OBJECT_HEADER", "header_fields": 5, "typed_data_internal_base": 112, "typed_data_internal_limit": 168, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "WEAK_ARRAY": 17, "CODE": 18, "OBJECT_POOL": 23, "PC_DESCRIPTORS": 24, "CODE_SOURCE_MAP": 25, "COMPRESSED_STACK_MAPS": 26, "LOCAL_VAR_DESCRIPTORS": 27, "EXCEPTION_HANDLERS": 28, "CONTEXT": 29, "CONTEXT_SCOPE": 30, "SENTINEL": 31, "SINGLE_TARGET_CACHE": 32, "UNLINKED_CALL": 35, "MONOMORPHIC_SMIABLE_CALL": 33, "CALL_SITE_DATA": 34, "ICDATA": 36, "MEGAMORPHIC_CACHE": 37, "SUBTYPE_TEST_CACHE": 38, "LOADING_UNIT": 39, "API_ERROR": 41, "LANGUAGE_ERROR": 42, "UNHANDLED_EXCEPTION": 43, "UNWIND_ERROR": 44, "INSTANCE": 45, "LIBRARY_PREFIX": 46, "TYPE_ARGUMENTS": 47, "TYPE": 49, "FUNCTION_TYPE": 50, "RECORD_TYPE": 51, "TYPE_PARAMETER": 52, "CLOSURE": 57, "MINT": 61, "DOUBLE": 62, "FLOAT32X4": 64, "INT32X4": 65, "FLOAT64X2": 66, "RECORD": 67, "TYPED_DATA": 69, "EXTERNAL_TYPED_DATA": 70, "TYPED_DATA_VIEW": 71, "CAPABILITY": 74, "RECEIVE_PORT": 75, "SEND_PORT": 76, "STACK_TRACE": 77, "SUSPEND_STATE": 78, "REGEXP": 79, "WEAK_PROPERTY": 80, "WEAK_REFERENCE": 81, "FUTURE_OR": 83, "USER_TAG": 84, "TRANSFERABLE_TYPED_DATA": 85, "MAP": 86, "CONST_MAP": 87, "SET": 88, "CONST_SET": 89, "ARRAY": 90, "IMMUTABLE_ARRAY": 91, "GROWABLE_OBJECT_ARRAY": 92, "STRING": 93, "ONE_BYTE_STRING": 94, "TWO_BYTE_STRING": 95, "NUM_PREDEFINED_CIDS": 175}, "note": "Uses fallback CIDs from 3.9.2 for the in-tree Flutter engine snapshot; not standalone Dart SDK 2.18.2 CIDs."},
|
||||
"2.18.3": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_SHIFT1", "header_fields": 5, "typed_data_internal_base": 111, "typed_data_internal_limit": 153, "typed_data_stride": 3, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "CODE": 17, "OBJECT_POOL": 21, "PC_DESCRIPTORS": 22, "CODE_SOURCE_MAP": 23, "COMPRESSED_STACK_MAPS": 24, "LOCAL_VAR_DESCRIPTORS": 25, "EXCEPTION_HANDLERS": 26, "CONTEXT": 27, "CONTEXT_SCOPE": 28, "SENTINEL": 29, "SINGLE_TARGET_CACHE": 30, "UNLINKED_CALL": 31, "MONOMORPHIC_SMIABLE_CALL": 32, "CALL_SITE_DATA": 33, "ICDATA": 34, "MEGAMORPHIC_CACHE": 35, "SUBTYPE_TEST_CACHE": 36, "LOADING_UNIT": 37, "API_ERROR": 39, "LANGUAGE_ERROR": 40, "UNHANDLED_EXCEPTION": 41, "UNWIND_ERROR": 42, "INSTANCE": 43, "LIBRARY_PREFIX": 44, "TYPE_ARGUMENTS": 45, "TYPE": 47, "FUNCTION_TYPE": 52, "TYPE_REF": 53, "TYPE_PARAMETER": 54, "CLOSURE": 55, "MINT": 59, "DOUBLE": 60, "FLOAT32X4": 62, "INT32X4": 63, "FLOAT64X2": 64, "TYPED_DATA": 66, "EXTERNAL_TYPED_DATA": 67, "TYPED_DATA_VIEW": 68, "CAPABILITY": 71, "RECEIVE_PORT": 72, "SEND_PORT": 73, "STACK_TRACE": 74, "SUSPEND_STATE": 75, "REGEXP": 76, "WEAK_PROPERTY": 77, "WEAK_REFERENCE": 78, "FUTURE_OR": 80, "USER_TAG": 81, "TRANSFERABLE_TYPED_DATA": 82, "MAP": 83, "CONST_MAP": 84, "SET": 85, "CONST_SET": 86, "ARRAY": 87, "IMMUTABLE_ARRAY": 88, "GROWABLE_OBJECT_ARRAY": 89, "STRING": 90, "ONE_BYTE_STRING": 91, "TWO_BYTE_STRING": 92, "NUM_PREDEFINED_CIDS": 159}},
|
||||
"2.19.0": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_SHIFT1", "header_fields": 5, "typed_data_internal_base": 113, "typed_data_internal_limit": 169, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "CODE": 17, "OBJECT_POOL": 21, "PC_DESCRIPTORS": 22, "CODE_SOURCE_MAP": 23, "COMPRESSED_STACK_MAPS": 24, "LOCAL_VAR_DESCRIPTORS": 25, "EXCEPTION_HANDLERS": 26, "CONTEXT": 27, "CONTEXT_SCOPE": 28, "SENTINEL": 29, "SINGLE_TARGET_CACHE": 30, "UNLINKED_CALL": 31, "MONOMORPHIC_SMIABLE_CALL": 32, "CALL_SITE_DATA": 33, "ICDATA": 34, "MEGAMORPHIC_CACHE": 35, "SUBTYPE_TEST_CACHE": 36, "LOADING_UNIT": 37, "API_ERROR": 39, "LANGUAGE_ERROR": 40, "UNHANDLED_EXCEPTION": 41, "UNWIND_ERROR": 42, "INSTANCE": 43, "LIBRARY_PREFIX": 44, "TYPE_ARGUMENTS": 45, "TYPE": 47, "FUNCTION_TYPE": 48, "RECORD_TYPE": 49, "TYPE_REF": 50, "TYPE_PARAMETER": 51, "CLOSURE": 56, "MINT": 60, "DOUBLE": 61, "FLOAT32X4": 63, "INT32X4": 64, "FLOAT64X2": 65, "RECORD": 66, "TYPED_DATA": 68, "EXTERNAL_TYPED_DATA": 69, "TYPED_DATA_VIEW": 70, "CAPABILITY": 73, "RECEIVE_PORT": 74, "SEND_PORT": 75, "STACK_TRACE": 76, "SUSPEND_STATE": 77, "REGEXP": 78, "WEAK_PROPERTY": 79, "WEAK_REFERENCE": 80, "FUTURE_OR": 82, "USER_TAG": 83, "TRANSFERABLE_TYPED_DATA": 84, "MAP": 85, "CONST_MAP": 86, "SET": 87, "CONST_SET": 88, "ARRAY": 89, "IMMUTABLE_ARRAY": 90, "GROWABLE_OBJECT_ARRAY": 91, "STRING": 92, "ONE_BYTE_STRING": 93, "TWO_BYTE_STRING": 94, "NUM_PREDEFINED_CIDS": 176}},
|
||||
"3.0.0": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_SHIFT1", "header_fields": 5, "typed_data_internal_base": 114, "typed_data_internal_limit": 170, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "WEAK_ARRAY": 17, "CODE": 18, "OBJECT_POOL": 22, "PC_DESCRIPTORS": 23, "CODE_SOURCE_MAP": 24, "COMPRESSED_STACK_MAPS": 25, "LOCAL_VAR_DESCRIPTORS": 26, "EXCEPTION_HANDLERS": 27, "CONTEXT": 28, "CONTEXT_SCOPE": 29, "SENTINEL": 30, "SINGLE_TARGET_CACHE": 31, "UNLINKED_CALL": 32, "MONOMORPHIC_SMIABLE_CALL": 33, "CALL_SITE_DATA": 34, "ICDATA": 35, "MEGAMORPHIC_CACHE": 36, "SUBTYPE_TEST_CACHE": 37, "LOADING_UNIT": 38, "API_ERROR": 40, "LANGUAGE_ERROR": 41, "UNHANDLED_EXCEPTION": 42, "UNWIND_ERROR": 43, "INSTANCE": 44, "LIBRARY_PREFIX": 45, "TYPE_ARGUMENTS": 46, "TYPE": 48, "FUNCTION_TYPE": 49, "RECORD_TYPE": 50, "TYPE_REF": 51, "TYPE_PARAMETER": 52, "CLOSURE": 57, "MINT": 61, "DOUBLE": 62, "FLOAT32X4": 64, "INT32X4": 65, "FLOAT64X2": 66, "RECORD": 67, "TYPED_DATA": 69, "EXTERNAL_TYPED_DATA": 70, "TYPED_DATA_VIEW": 71, "CAPABILITY": 74, "RECEIVE_PORT": 75, "SEND_PORT": 76, "STACK_TRACE": 77, "SUSPEND_STATE": 78, "REGEXP": 79, "WEAK_PROPERTY": 80, "WEAK_REFERENCE": 81, "FUTURE_OR": 83, "USER_TAG": 84, "TRANSFERABLE_TYPED_DATA": 85, "MAP": 86, "CONST_MAP": 87, "SET": 88, "CONST_SET": 89, "ARRAY": 90, "IMMUTABLE_ARRAY": 91, "GROWABLE_OBJECT_ARRAY": 92, "STRING": 93, "ONE_BYTE_STRING": 94, "TWO_BYTE_STRING": 95, "NUM_PREDEFINED_CIDS": 177}},
|
||||
"3.1.0": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_SHIFT1", "header_fields": 5, "typed_data_internal_base": 113, "typed_data_internal_limit": 169, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "WEAK_ARRAY": 17, "CODE": 18, "OBJECT_POOL": 22, "PC_DESCRIPTORS": 23, "CODE_SOURCE_MAP": 24, "COMPRESSED_STACK_MAPS": 25, "LOCAL_VAR_DESCRIPTORS": 26, "EXCEPTION_HANDLERS": 27, "CONTEXT": 28, "CONTEXT_SCOPE": 29, "SENTINEL": 30, "SINGLE_TARGET_CACHE": 31, "UNLINKED_CALL": 32, "MONOMORPHIC_SMIABLE_CALL": 33, "CALL_SITE_DATA": 34, "ICDATA": 35, "MEGAMORPHIC_CACHE": 36, "SUBTYPE_TEST_CACHE": 37, "LOADING_UNIT": 38, "API_ERROR": 40, "LANGUAGE_ERROR": 41, "UNHANDLED_EXCEPTION": 42, "UNWIND_ERROR": 43, "INSTANCE": 44, "LIBRARY_PREFIX": 45, "TYPE_ARGUMENTS": 46, "TYPE": 48, "FUNCTION_TYPE": 49, "RECORD_TYPE": 50, "TYPE_PARAMETER": 51, "CLOSURE": 56, "MINT": 60, "DOUBLE": 61, "FLOAT32X4": 63, "INT32X4": 64, "FLOAT64X2": 65, "RECORD": 66, "TYPED_DATA": 68, "EXTERNAL_TYPED_DATA": 69, "TYPED_DATA_VIEW": 70, "CAPABILITY": 73, "RECEIVE_PORT": 74, "SEND_PORT": 75, "STACK_TRACE": 76, "SUSPEND_STATE": 77, "REGEXP": 78, "WEAK_PROPERTY": 79, "WEAK_REFERENCE": 80, "FUTURE_OR": 82, "USER_TAG": 83, "TRANSFERABLE_TYPED_DATA": 84, "MAP": 85, "CONST_MAP": 86, "SET": 87, "CONST_SET": 88, "ARRAY": 89, "IMMUTABLE_ARRAY": 90, "GROWABLE_OBJECT_ARRAY": 91, "STRING": 92, "ONE_BYTE_STRING": 93, "TWO_BYTE_STRING": 94, "NUM_PREDEFINED_CIDS": 176}},
|
||||
"3.2.0": {"cws": 8, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_SHIFT1", "header_fields": 5, "typed_data_internal_base": 113, "typed_data_internal_limit": 169, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "WEAK_ARRAY": 17, "CODE": 18, "OBJECT_POOL": 22, "PC_DESCRIPTORS": 23, "CODE_SOURCE_MAP": 24, "COMPRESSED_STACK_MAPS": 25, "LOCAL_VAR_DESCRIPTORS": 26, "EXCEPTION_HANDLERS": 27, "CONTEXT": 28, "CONTEXT_SCOPE": 29, "SENTINEL": 30, "SINGLE_TARGET_CACHE": 31, "UNLINKED_CALL": 32, "MONOMORPHIC_SMIABLE_CALL": 33, "CALL_SITE_DATA": 34, "ICDATA": 35, "MEGAMORPHIC_CACHE": 36, "SUBTYPE_TEST_CACHE": 37, "LOADING_UNIT": 38, "API_ERROR": 40, "LANGUAGE_ERROR": 41, "UNHANDLED_EXCEPTION": 42, "UNWIND_ERROR": 43, "INSTANCE": 44, "LIBRARY_PREFIX": 45, "TYPE_ARGUMENTS": 46, "TYPE": 48, "FUNCTION_TYPE": 49, "RECORD_TYPE": 50, "TYPE_PARAMETER": 51, "CLOSURE": 56, "MINT": 60, "DOUBLE": 61, "FLOAT32X4": 63, "INT32X4": 64, "FLOAT64X2": 65, "RECORD": 66, "TYPED_DATA": 68, "EXTERNAL_TYPED_DATA": 69, "TYPED_DATA_VIEW": 70, "CAPABILITY": 73, "RECEIVE_PORT": 74, "SEND_PORT": 75, "STACK_TRACE": 76, "SUSPEND_STATE": 77, "REGEXP": 78, "WEAK_PROPERTY": 79, "WEAK_REFERENCE": 80, "FUTURE_OR": 82, "USER_TAG": 83, "TRANSFERABLE_TYPED_DATA": 84, "MAP": 85, "CONST_MAP": 86, "SET": 87, "CONST_SET": 88, "ARRAY": 89, "IMMUTABLE_ARRAY": 90, "GROWABLE_OBJECT_ARRAY": 91, "STRING": 92, "ONE_BYTE_STRING": 93, "TWO_BYTE_STRING": 94, "NUM_PREDEFINED_CIDS": 176}},
|
||||
"3.3.0": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "CID_SHIFT1", "header_fields": 5, "typed_data_internal_base": 113, "typed_data_internal_limit": 169, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "WEAK_ARRAY": 17, "CODE": 18, "OBJECT_POOL": 22, "PC_DESCRIPTORS": 23, "CODE_SOURCE_MAP": 24, "COMPRESSED_STACK_MAPS": 25, "LOCAL_VAR_DESCRIPTORS": 26, "EXCEPTION_HANDLERS": 27, "CONTEXT": 28, "CONTEXT_SCOPE": 29, "SENTINEL": 30, "SINGLE_TARGET_CACHE": 31, "UNLINKED_CALL": 32, "MONOMORPHIC_SMIABLE_CALL": 33, "CALL_SITE_DATA": 34, "ICDATA": 35, "MEGAMORPHIC_CACHE": 36, "SUBTYPE_TEST_CACHE": 37, "LOADING_UNIT": 38, "API_ERROR": 40, "LANGUAGE_ERROR": 41, "UNHANDLED_EXCEPTION": 42, "UNWIND_ERROR": 43, "INSTANCE": 44, "LIBRARY_PREFIX": 45, "TYPE_ARGUMENTS": 46, "TYPE": 48, "FUNCTION_TYPE": 49, "RECORD_TYPE": 50, "TYPE_PARAMETER": 51, "CLOSURE": 56, "MINT": 60, "DOUBLE": 61, "FLOAT32X4": 63, "INT32X4": 64, "FLOAT64X2": 65, "RECORD": 66, "TYPED_DATA": 68, "EXTERNAL_TYPED_DATA": 69, "TYPED_DATA_VIEW": 70, "CAPABILITY": 73, "RECEIVE_PORT": 74, "SEND_PORT": 75, "STACK_TRACE": 76, "SUSPEND_STATE": 77, "REGEXP": 78, "WEAK_PROPERTY": 79, "WEAK_REFERENCE": 80, "FUTURE_OR": 82, "USER_TAG": 83, "TRANSFERABLE_TYPED_DATA": 84, "MAP": 85, "CONST_MAP": 86, "SET": 87, "CONST_SET": 88, "ARRAY": 89, "IMMUTABLE_ARRAY": 90, "GROWABLE_OBJECT_ARRAY": 91, "STRING": 92, "ONE_BYTE_STRING": 93, "TWO_BYTE_STRING": 94, "NUM_PREDEFINED_CIDS": 176}},
|
||||
"3.4.0": {"cws": 8, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "OBJECT_HEADER", "header_fields": 5, "typed_data_internal_base": 111, "typed_data_internal_limit": 167, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "WEAK_ARRAY": 17, "CODE": 18, "OBJECT_POOL": 22, "PC_DESCRIPTORS": 23, "CODE_SOURCE_MAP": 24, "COMPRESSED_STACK_MAPS": 25, "LOCAL_VAR_DESCRIPTORS": 26, "EXCEPTION_HANDLERS": 27, "CONTEXT": 28, "CONTEXT_SCOPE": 29, "SENTINEL": 30, "SINGLE_TARGET_CACHE": 31, "UNLINKED_CALL": 32, "MONOMORPHIC_SMIABLE_CALL": 33, "CALL_SITE_DATA": 34, "ICDATA": 35, "MEGAMORPHIC_CACHE": 36, "SUBTYPE_TEST_CACHE": 37, "LOADING_UNIT": 38, "API_ERROR": 40, "LANGUAGE_ERROR": 41, "UNHANDLED_EXCEPTION": 42, "UNWIND_ERROR": 43, "INSTANCE": 44, "LIBRARY_PREFIX": 45, "TYPE_ARGUMENTS": 46, "TYPE": 48, "FUNCTION_TYPE": 49, "RECORD_TYPE": 50, "TYPE_PARAMETER": 51, "CLOSURE": 56, "MINT": 60, "DOUBLE": 61, "FLOAT32X4": 63, "INT32X4": 64, "FLOAT64X2": 65, "RECORD": 66, "TYPED_DATA": 68, "EXTERNAL_TYPED_DATA": 69, "TYPED_DATA_VIEW": 70, "CAPABILITY": 73, "RECEIVE_PORT": 74, "SEND_PORT": 75, "STACK_TRACE": 76, "SUSPEND_STATE": 77, "REGEXP": 78, "WEAK_PROPERTY": 79, "WEAK_REFERENCE": 80, "FUTURE_OR": 82, "USER_TAG": 83, "TRANSFERABLE_TYPED_DATA": 84, "MAP": 85, "CONST_MAP": 86, "SET": 87, "CONST_SET": 88, "ARRAY": 89, "IMMUTABLE_ARRAY": 90, "GROWABLE_OBJECT_ARRAY": 91, "STRING": 92, "ONE_BYTE_STRING": 93, "TWO_BYTE_STRING": 94, "NUM_PREDEFINED_CIDS": 174}},
|
||||
"3.5.0": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "OBJECT_HEADER", "header_fields": 5, "typed_data_internal_base": 111, "typed_data_internal_limit": 167, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "WEAK_ARRAY": 17, "CODE": 18, "OBJECT_POOL": 22, "PC_DESCRIPTORS": 23, "CODE_SOURCE_MAP": 24, "COMPRESSED_STACK_MAPS": 25, "LOCAL_VAR_DESCRIPTORS": 26, "EXCEPTION_HANDLERS": 27, "CONTEXT": 28, "CONTEXT_SCOPE": 29, "SENTINEL": 30, "SINGLE_TARGET_CACHE": 31, "UNLINKED_CALL": 32, "MONOMORPHIC_SMIABLE_CALL": 33, "CALL_SITE_DATA": 34, "ICDATA": 35, "MEGAMORPHIC_CACHE": 36, "SUBTYPE_TEST_CACHE": 37, "LOADING_UNIT": 38, "API_ERROR": 40, "LANGUAGE_ERROR": 41, "UNHANDLED_EXCEPTION": 42, "UNWIND_ERROR": 43, "INSTANCE": 44, "LIBRARY_PREFIX": 45, "TYPE_ARGUMENTS": 46, "TYPE": 48, "FUNCTION_TYPE": 49, "RECORD_TYPE": 50, "TYPE_PARAMETER": 51, "CLOSURE": 56, "MINT": 60, "DOUBLE": 61, "FLOAT32X4": 63, "INT32X4": 64, "FLOAT64X2": 65, "RECORD": 66, "TYPED_DATA": 68, "EXTERNAL_TYPED_DATA": 69, "TYPED_DATA_VIEW": 70, "CAPABILITY": 73, "RECEIVE_PORT": 74, "SEND_PORT": 75, "STACK_TRACE": 76, "SUSPEND_STATE": 77, "REGEXP": 78, "WEAK_PROPERTY": 79, "WEAK_REFERENCE": 80, "FUTURE_OR": 82, "USER_TAG": 83, "TRANSFERABLE_TYPED_DATA": 84, "MAP": 85, "CONST_MAP": 86, "SET": 87, "CONST_SET": 88, "ARRAY": 89, "IMMUTABLE_ARRAY": 90, "GROWABLE_OBJECT_ARRAY": 91, "STRING": 92, "ONE_BYTE_STRING": 93, "TWO_BYTE_STRING": 94, "NUM_PREDEFINED_CIDS": 174}},
|
||||
"3.6.0": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "OBJECT_HEADER", "header_fields": 5, "typed_data_internal_base": 112, "typed_data_internal_limit": 168, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "WEAK_ARRAY": 17, "CODE": 18, "OBJECT_POOL": 23, "PC_DESCRIPTORS": 24, "CODE_SOURCE_MAP": 25, "COMPRESSED_STACK_MAPS": 26, "LOCAL_VAR_DESCRIPTORS": 27, "EXCEPTION_HANDLERS": 28, "CONTEXT": 29, "CONTEXT_SCOPE": 30, "SENTINEL": 31, "SINGLE_TARGET_CACHE": 32, "UNLINKED_CALL": 33, "MONOMORPHIC_SMIABLE_CALL": 34, "CALL_SITE_DATA": 35, "ICDATA": 36, "MEGAMORPHIC_CACHE": 37, "SUBTYPE_TEST_CACHE": 38, "LOADING_UNIT": 39, "API_ERROR": 41, "LANGUAGE_ERROR": 42, "UNHANDLED_EXCEPTION": 43, "UNWIND_ERROR": 44, "INSTANCE": 45, "LIBRARY_PREFIX": 46, "TYPE_ARGUMENTS": 47, "TYPE": 49, "FUNCTION_TYPE": 50, "RECORD_TYPE": 51, "TYPE_PARAMETER": 52, "CLOSURE": 57, "MINT": 61, "DOUBLE": 62, "FLOAT32X4": 64, "INT32X4": 65, "FLOAT64X2": 66, "RECORD": 67, "TYPED_DATA": 69, "EXTERNAL_TYPED_DATA": 70, "TYPED_DATA_VIEW": 71, "CAPABILITY": 74, "RECEIVE_PORT": 75, "SEND_PORT": 76, "STACK_TRACE": 77, "SUSPEND_STATE": 78, "REGEXP": 79, "WEAK_PROPERTY": 80, "WEAK_REFERENCE": 81, "FUTURE_OR": 83, "USER_TAG": 84, "TRANSFERABLE_TYPED_DATA": 85, "MAP": 86, "CONST_MAP": 87, "SET": 88, "CONST_SET": 89, "ARRAY": 90, "IMMUTABLE_ARRAY": 91, "GROWABLE_OBJECT_ARRAY": 92, "STRING": 93, "ONE_BYTE_STRING": 94, "TWO_BYTE_STRING": 95, "NUM_PREDEFINED_CIDS": 175}},
|
||||
"3.7.0": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "OBJECT_HEADER", "header_fields": 5, "typed_data_internal_base": 112, "typed_data_internal_limit": 168, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "WEAK_ARRAY": 17, "CODE": 18, "OBJECT_POOL": 23, "PC_DESCRIPTORS": 24, "CODE_SOURCE_MAP": 25, "COMPRESSED_STACK_MAPS": 26, "LOCAL_VAR_DESCRIPTORS": 27, "EXCEPTION_HANDLERS": 28, "CONTEXT": 29, "CONTEXT_SCOPE": 30, "SENTINEL": 31, "SINGLE_TARGET_CACHE": 32, "UNLINKED_CALL": 33, "MONOMORPHIC_SMIABLE_CALL": 34, "CALL_SITE_DATA": 35, "ICDATA": 36, "MEGAMORPHIC_CACHE": 37, "SUBTYPE_TEST_CACHE": 38, "LOADING_UNIT": 39, "API_ERROR": 41, "LANGUAGE_ERROR": 42, "UNHANDLED_EXCEPTION": 43, "UNWIND_ERROR": 44, "INSTANCE": 45, "LIBRARY_PREFIX": 46, "TYPE_ARGUMENTS": 47, "TYPE": 49, "FUNCTION_TYPE": 50, "RECORD_TYPE": 51, "TYPE_PARAMETER": 52, "CLOSURE": 57, "MINT": 61, "DOUBLE": 62, "FLOAT32X4": 64, "INT32X4": 65, "FLOAT64X2": 66, "RECORD": 67, "TYPED_DATA": 69, "EXTERNAL_TYPED_DATA": 70, "TYPED_DATA_VIEW": 71, "CAPABILITY": 74, "RECEIVE_PORT": 75, "SEND_PORT": 76, "STACK_TRACE": 77, "SUSPEND_STATE": 78, "REGEXP": 79, "WEAK_PROPERTY": 80, "WEAK_REFERENCE": 81, "FUTURE_OR": 83, "USER_TAG": 84, "TRANSFERABLE_TYPED_DATA": 85, "MAP": 86, "CONST_MAP": 87, "SET": 88, "CONST_SET": 89, "ARRAY": 90, "IMMUTABLE_ARRAY": 91, "GROWABLE_OBJECT_ARRAY": 92, "STRING": 93, "ONE_BYTE_STRING": 94, "TWO_BYTE_STRING": 95, "NUM_PREDEFINED_CIDS": 175}},
|
||||
"3.8.0": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "OBJECT_HEADER", "header_fields": 5, "typed_data_internal_base": 112, "typed_data_internal_limit": 168, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "WEAK_ARRAY": 17, "CODE": 18, "OBJECT_POOL": 23, "PC_DESCRIPTORS": 24, "CODE_SOURCE_MAP": 25, "COMPRESSED_STACK_MAPS": 26, "LOCAL_VAR_DESCRIPTORS": 27, "EXCEPTION_HANDLERS": 28, "CONTEXT": 29, "CONTEXT_SCOPE": 30, "SENTINEL": 31, "SINGLE_TARGET_CACHE": 32, "UNLINKED_CALL": 33, "MONOMORPHIC_SMIABLE_CALL": 34, "CALL_SITE_DATA": 35, "ICDATA": 36, "MEGAMORPHIC_CACHE": 37, "SUBTYPE_TEST_CACHE": 38, "LOADING_UNIT": 39, "API_ERROR": 41, "LANGUAGE_ERROR": 42, "UNHANDLED_EXCEPTION": 43, "UNWIND_ERROR": 44, "INSTANCE": 45, "LIBRARY_PREFIX": 46, "TYPE_ARGUMENTS": 47, "TYPE": 49, "FUNCTION_TYPE": 50, "RECORD_TYPE": 51, "TYPE_PARAMETER": 52, "CLOSURE": 57, "MINT": 61, "DOUBLE": 62, "FLOAT32X4": 64, "INT32X4": 65, "FLOAT64X2": 66, "RECORD": 67, "TYPED_DATA": 69, "EXTERNAL_TYPED_DATA": 70, "TYPED_DATA_VIEW": 71, "CAPABILITY": 74, "RECEIVE_PORT": 75, "SEND_PORT": 76, "STACK_TRACE": 77, "SUSPEND_STATE": 78, "REGEXP": 79, "WEAK_PROPERTY": 80, "WEAK_REFERENCE": 81, "FUTURE_OR": 83, "USER_TAG": 84, "TRANSFERABLE_TYPED_DATA": 85, "MAP": 86, "CONST_MAP": 87, "SET": 88, "CONST_SET": 89, "ARRAY": 90, "IMMUTABLE_ARRAY": 91, "GROWABLE_OBJECT_ARRAY": 92, "STRING": 93, "ONE_BYTE_STRING": 94, "TWO_BYTE_STRING": 95, "NUM_PREDEFINED_CIDS": 175}},
|
||||
"3.9.0": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "OBJECT_HEADER", "header_fields": 5, "typed_data_internal_base": 112, "typed_data_internal_limit": 168, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "WEAK_ARRAY": 17, "CODE": 18, "OBJECT_POOL": 23, "PC_DESCRIPTORS": 24, "CODE_SOURCE_MAP": 25, "COMPRESSED_STACK_MAPS": 26, "LOCAL_VAR_DESCRIPTORS": 27, "EXCEPTION_HANDLERS": 28, "CONTEXT": 29, "CONTEXT_SCOPE": 30, "SENTINEL": 31, "SINGLE_TARGET_CACHE": 32, "UNLINKED_CALL": 35, "MONOMORPHIC_SMIABLE_CALL": 33, "CALL_SITE_DATA": 34, "ICDATA": 36, "MEGAMORPHIC_CACHE": 37, "SUBTYPE_TEST_CACHE": 38, "LOADING_UNIT": 39, "API_ERROR": 41, "LANGUAGE_ERROR": 42, "UNHANDLED_EXCEPTION": 43, "UNWIND_ERROR": 44, "INSTANCE": 45, "LIBRARY_PREFIX": 46, "TYPE_ARGUMENTS": 47, "TYPE": 49, "FUNCTION_TYPE": 50, "RECORD_TYPE": 51, "TYPE_PARAMETER": 52, "CLOSURE": 57, "MINT": 61, "DOUBLE": 62, "FLOAT32X4": 64, "INT32X4": 65, "FLOAT64X2": 66, "RECORD": 67, "TYPED_DATA": 69, "EXTERNAL_TYPED_DATA": 70, "TYPED_DATA_VIEW": 71, "CAPABILITY": 74, "RECEIVE_PORT": 75, "SEND_PORT": 76, "STACK_TRACE": 77, "SUSPEND_STATE": 78, "REGEXP": 79, "WEAK_PROPERTY": 80, "WEAK_REFERENCE": 81, "FUTURE_OR": 83, "USER_TAG": 84, "TRANSFERABLE_TYPED_DATA": 85, "MAP": 86, "CONST_MAP": 87, "SET": 88, "CONST_SET": 89, "ARRAY": 90, "IMMUTABLE_ARRAY": 91, "GROWABLE_OBJECT_ARRAY": 92, "STRING": 93, "ONE_BYTE_STRING": 94, "TWO_BYTE_STRING": 95, "NUM_PREDEFINED_CIDS": 175}},
|
||||
"3.10.0": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "OBJECT_HEADER", "header_fields": 5, "typed_data_internal_base": 112, "typed_data_internal_limit": 168, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "WEAK_ARRAY": 17, "CODE": 18, "OBJECT_POOL": 23, "PC_DESCRIPTORS": 24, "CODE_SOURCE_MAP": 25, "COMPRESSED_STACK_MAPS": 26, "LOCAL_VAR_DESCRIPTORS": 27, "EXCEPTION_HANDLERS": 28, "CONTEXT": 29, "CONTEXT_SCOPE": 30, "SENTINEL": 31, "SINGLE_TARGET_CACHE": 32, "UNLINKED_CALL": 35, "MONOMORPHIC_SMIABLE_CALL": 33, "CALL_SITE_DATA": 34, "ICDATA": 36, "MEGAMORPHIC_CACHE": 37, "SUBTYPE_TEST_CACHE": 38, "LOADING_UNIT": 39, "API_ERROR": 41, "LANGUAGE_ERROR": 42, "UNHANDLED_EXCEPTION": 43, "UNWIND_ERROR": 44, "INSTANCE": 45, "LIBRARY_PREFIX": 46, "TYPE_ARGUMENTS": 47, "TYPE": 49, "FUNCTION_TYPE": 50, "RECORD_TYPE": 51, "TYPE_PARAMETER": 52, "CLOSURE": 57, "MINT": 61, "DOUBLE": 62, "FLOAT32X4": 64, "INT32X4": 65, "FLOAT64X2": 66, "RECORD": 67, "TYPED_DATA": 69, "EXTERNAL_TYPED_DATA": 70, "TYPED_DATA_VIEW": 71, "CAPABILITY": 74, "RECEIVE_PORT": 75, "SEND_PORT": 76, "STACK_TRACE": 77, "SUSPEND_STATE": 78, "REGEXP": 79, "WEAK_PROPERTY": 80, "WEAK_REFERENCE": 81, "FUTURE_OR": 83, "USER_TAG": 84, "TRANSFERABLE_TYPED_DATA": 85, "MAP": 86, "CONST_MAP": 87, "SET": 88, "CONST_SET": 89, "ARRAY": 90, "IMMUTABLE_ARRAY": 91, "GROWABLE_OBJECT_ARRAY": 92, "STRING": 93, "ONE_BYTE_STRING": 94, "TWO_BYTE_STRING": 95, "NUM_PREDEFINED_CIDS": 175}},
|
||||
"3.11.0": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "OBJECT_HEADER", "header_fields": 5, "typed_data_internal_base": 112, "typed_data_internal_limit": 168, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "WEAK_ARRAY": 17, "CODE": 18, "OBJECT_POOL": 23, "PC_DESCRIPTORS": 24, "CODE_SOURCE_MAP": 25, "COMPRESSED_STACK_MAPS": 26, "LOCAL_VAR_DESCRIPTORS": 27, "EXCEPTION_HANDLERS": 28, "CONTEXT": 29, "CONTEXT_SCOPE": 30, "SENTINEL": 31, "SINGLE_TARGET_CACHE": 32, "UNLINKED_CALL": 35, "MONOMORPHIC_SMIABLE_CALL": 33, "CALL_SITE_DATA": 34, "ICDATA": 36, "MEGAMORPHIC_CACHE": 37, "SUBTYPE_TEST_CACHE": 38, "LOADING_UNIT": 39, "API_ERROR": 41, "LANGUAGE_ERROR": 42, "UNHANDLED_EXCEPTION": 43, "UNWIND_ERROR": 44, "INSTANCE": 45, "LIBRARY_PREFIX": 46, "TYPE_ARGUMENTS": 47, "TYPE": 49, "FUNCTION_TYPE": 50, "RECORD_TYPE": 51, "TYPE_PARAMETER": 52, "CLOSURE": 57, "MINT": 61, "DOUBLE": 62, "FLOAT32X4": 64, "INT32X4": 65, "FLOAT64X2": 66, "RECORD": 67, "TYPED_DATA": 69, "EXTERNAL_TYPED_DATA": 70, "TYPED_DATA_VIEW": 71, "CAPABILITY": 74, "RECEIVE_PORT": 75, "SEND_PORT": 76, "STACK_TRACE": 77, "SUSPEND_STATE": 78, "REGEXP": 79, "WEAK_PROPERTY": 80, "WEAK_REFERENCE": 81, "FUTURE_OR": 83, "USER_TAG": 84, "TRANSFERABLE_TYPED_DATA": 85, "MAP": 86, "CONST_MAP": 87, "SET": 88, "CONST_SET": 89, "ARRAY": 90, "IMMUTABLE_ARRAY": 91, "GROWABLE_OBJECT_ARRAY": 92, "STRING": 93, "ONE_BYTE_STRING": 94, "TWO_BYTE_STRING": 95, "NUM_PREDEFINED_CIDS": 175}},
|
||||
"3.12.0": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "OBJECT_HEADER", "header_fields": 5, "typed_data_internal_base": 112, "typed_data_internal_limit": 168, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "WEAK_ARRAY": 17, "CODE": 18, "OBJECT_POOL": 23, "PC_DESCRIPTORS": 24, "CODE_SOURCE_MAP": 25, "COMPRESSED_STACK_MAPS": 26, "LOCAL_VAR_DESCRIPTORS": 27, "EXCEPTION_HANDLERS": 28, "CONTEXT": 29, "CONTEXT_SCOPE": 30, "SENTINEL": 31, "SINGLE_TARGET_CACHE": 32, "UNLINKED_CALL": 35, "MONOMORPHIC_SMIABLE_CALL": 33, "CALL_SITE_DATA": 34, "ICDATA": 36, "MEGAMORPHIC_CACHE": 37, "SUBTYPE_TEST_CACHE": 38, "LOADING_UNIT": 39, "API_ERROR": 41, "LANGUAGE_ERROR": 42, "UNHANDLED_EXCEPTION": 43, "UNWIND_ERROR": 44, "INSTANCE": 45, "LIBRARY_PREFIX": 46, "TYPE_ARGUMENTS": 47, "TYPE": 49, "FUNCTION_TYPE": 50, "RECORD_TYPE": 51, "TYPE_PARAMETER": 52, "CLOSURE": 57, "MINT": 61, "DOUBLE": 62, "FLOAT32X4": 64, "INT32X4": 65, "FLOAT64X2": 66, "RECORD": 67, "TYPED_DATA": 69, "EXTERNAL_TYPED_DATA": 70, "TYPED_DATA_VIEW": 71, "CAPABILITY": 74, "RECEIVE_PORT": 75, "SEND_PORT": 76, "STACK_TRACE": 77, "SUSPEND_STATE": 78, "REGEXP": 79, "WEAK_PROPERTY": 80, "WEAK_REFERENCE": 81, "FUTURE_OR": 83, "USER_TAG": 84, "TRANSFERABLE_TYPED_DATA": 85, "MAP": 86, "CONST_MAP": 87, "SET": 88, "CONST_SET": 89, "ARRAY": 90, "IMMUTABLE_ARRAY": 91, "GROWABLE_OBJECT_ARRAY": 92, "STRING": 93, "ONE_BYTE_STRING": 94, "TWO_BYTE_STRING": 95, "NUM_PREDEFINED_CIDS": 175}},
|
||||
"3.13.0": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "OBJECT_HEADER", "header_fields": 5, "typed_data_internal_base": 113, "typed_data_internal_limit": 169, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "WEAK_ARRAY": 17, "CODE": 18, "OBJECT_POOL": 23, "PC_DESCRIPTORS": 24, "CODE_SOURCE_MAP": 25, "COMPRESSED_STACK_MAPS": 26, "LOCAL_VAR_DESCRIPTORS": 27, "EXCEPTION_HANDLERS": 28, "CONTEXT": 29, "CONTEXT_SCOPE": 30, "SENTINEL": 31, "SINGLE_TARGET_CACHE": 32, "UNLINKED_CALL": 35, "MONOMORPHIC_SMIABLE_CALL": 33, "CALL_SITE_DATA": 34, "ICDATA": 36, "MEGAMORPHIC_CACHE": 37, "SUBTYPE_TEST_CACHE": 38, "LOADING_UNIT": 39, "API_ERROR": 41, "LANGUAGE_ERROR": 42, "UNHANDLED_EXCEPTION": 43, "UNWIND_ERROR": 44, "INSTANCE": 45, "LIBRARY_PREFIX": 46, "TYPE_ARGUMENTS": 47, "TYPE": 49, "FUNCTION_TYPE": 50, "RECORD_TYPE": 51, "TYPE_PARAMETER": 52, "CLOSURE": 57, "MINT": 61, "DOUBLE": 62, "FLOAT32X4": 64, "INT32X4": 65, "FLOAT64X2": 66, "RECORD": 67, "TYPED_DATA": 69, "EXTERNAL_TYPED_DATA": 70, "TYPED_DATA_VIEW": 71, "CAPABILITY": 74, "RECEIVE_PORT": 75, "SEND_PORT": 76, "STACK_TRACE": 77, "SUSPEND_STATE": 78, "REGEXP": 79, "WEAK_PROPERTY": 80, "WEAK_REFERENCE": 81, "FUTURE_OR": 83, "USER_TAG": 84, "TRANSFERABLE_TYPED_DATA": 85, "MAP": 86, "CONST_MAP": 87, "SET": 88, "CONST_SET": 89, "ARRAY": 90, "IMMUTABLE_ARRAY": 91, "GROWABLE_OBJECT_ARRAY": 92, "STRING": 93, "ONE_BYTE_STRING": 94, "TWO_BYTE_STRING": 95, "NUM_PREDEFINED_CIDS": 176}},
|
||||
"3.14.0": {"cws": 4, "heap_object_tag": 1, "max_alignment": 16, "it_cap": 20000, "tag_style": "OBJECT_HEADER", "header_fields": 5, "typed_data_internal_base": 113, "typed_data_internal_limit": 169, "typed_data_stride": 4, "cids": {"ILLEGAL": 0, "NATIVE_POINTER": 1, "CLASS": 5, "PATCH_CLASS": 6, "FUNCTION": 7, "TYPE_PARAMETERS": 8, "CLOSURE_DATA": 9, "FFI_TRAMPOLINE_DATA": 10, "FIELD": 11, "SCRIPT": 12, "LIBRARY": 13, "NAMESPACE": 14, "KERNEL_PROGRAM_INFO": 15, "WEAK_SERIALIZATION_REFERENCE": 16, "WEAK_ARRAY": 17, "CODE": 18, "OBJECT_POOL": 23, "PC_DESCRIPTORS": 24, "CODE_SOURCE_MAP": 25, "COMPRESSED_STACK_MAPS": 26, "LOCAL_VAR_DESCRIPTORS": 27, "EXCEPTION_HANDLERS": 28, "CONTEXT": 29, "CONTEXT_SCOPE": 30, "SENTINEL": 31, "SINGLE_TARGET_CACHE": 32, "UNLINKED_CALL": 35, "MONOMORPHIC_SMIABLE_CALL": 33, "CALL_SITE_DATA": 34, "ICDATA": 36, "MEGAMORPHIC_CACHE": 37, "SUBTYPE_TEST_CACHE": 38, "LOADING_UNIT": 39, "API_ERROR": 41, "LANGUAGE_ERROR": 42, "UNHANDLED_EXCEPTION": 43, "UNWIND_ERROR": 44, "INSTANCE": 45, "LIBRARY_PREFIX": 46, "TYPE_ARGUMENTS": 47, "TYPE": 49, "FUNCTION_TYPE": 50, "RECORD_TYPE": 51, "TYPE_PARAMETER": 52, "CLOSURE": 57, "MINT": 61, "DOUBLE": 62, "FLOAT32X4": 64, "INT32X4": 65, "FLOAT64X2": 66, "RECORD": 67, "TYPED_DATA": 69, "EXTERNAL_TYPED_DATA": 70, "TYPED_DATA_VIEW": 71, "CAPABILITY": 74, "RECEIVE_PORT": 75, "SEND_PORT": 76, "STACK_TRACE": 77, "SUSPEND_STATE": 78, "REGEXP": 79, "WEAK_PROPERTY": 80, "WEAK_REFERENCE": 81, "FUTURE_OR": 83, "USER_TAG": 84, "TRANSFERABLE_TYPED_DATA": 85, "MAP": 86, "CONST_MAP": 87, "SET": 88, "CONST_SET": 89, "ARRAY": 90, "IMMUTABLE_ARRAY": 91, "GROWABLE_OBJECT_ARRAY": 92, "STRING": 93, "ONE_BYTE_STRING": 94, "TWO_BYTE_STRING": 95, "NUM_PREDEFINED_CIDS": 176}}
|
||||
},
|
||||
"hashes": {
|
||||
"8ee4ef7a67df9845fba331734198a953": "2.10.0",
|
||||
"d979fa911b038a8682e98cc8c8569261": "2.10.0",
|
||||
"5b97292b25f0a715613b7a28e0734f77": "2.12.0",
|
||||
"e036be9d5df68283e1a043e348fa7ff4": "2.12.0",
|
||||
"0c7f1aa25fc597a7b3b9a19ed390afa1": "2.13.0",
|
||||
"34f6eec64e9371856eaaa278ccf56538": "2.13.0",
|
||||
@@ -86,6 +94,8 @@
|
||||
"1ce86630892e2dca9a8543fdb8ed8e22": "3.10.7",
|
||||
"78da37fed6bf1489361a312568249f3f": "3.11.5",
|
||||
"41be3daaabd524b8aa7423bc24584957": "3.12.0",
|
||||
"ace654289f5abc240509fc941453ebc5": "3.12.1"
|
||||
"ace654289f5abc240509fc941453ebc5": "3.12.1",
|
||||
"0451907c2eaa8467e848c0067bfe8ed4": "3.13.0",
|
||||
"7a1ea3f6f5cf1089a7f6e55d7f20dbfd": "3.14.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Patch Dart AOT RawSecureSocket bad-certificate wrappers in an opened r2 file.
|
||||
*
|
||||
* Usage:
|
||||
* r2 -w -e bin.relocs.apply=true -i scripts/dart_cert_bypass.qjs libapp.so
|
||||
* r2 -w -e bin.relocs.apply=true -i scripts/dart_cert_bypass.qjs Runner.app/Frameworks/App.framework/App
|
||||
*
|
||||
* The opened file must be the Dart AOT image itself, not an APK/IPA/bundle
|
||||
* directory. Android usually uses libapp.so. iOS Flutter apps usually use
|
||||
* Frameworks/App.framework/App.
|
||||
*
|
||||
* This script is intentionally suffix-agnostic: it resolves the
|
||||
* method._RawSecureSocket_*._onBadCertificateWrapper_* flag produced by
|
||||
* r2flutter, translates the method vaddr to a raw file offset, and writes:
|
||||
*
|
||||
* add x0, x22, 0x20
|
||||
* ret
|
||||
*
|
||||
* Those bytes are AArch64-specific and return Dart true in the observed
|
||||
* Android/iOS AOT snapshots.
|
||||
*/
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function trim (s) {
|
||||
return String (s || "").replace (/^\s+|\s+$/g, "");
|
||||
}
|
||||
|
||||
function cmd (s) {
|
||||
return r2.cmd (s);
|
||||
}
|
||||
|
||||
function cmdj (s) {
|
||||
try {
|
||||
return r2.cmdj (s);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function fail (msg) {
|
||||
console.log ("error=" + msg);
|
||||
throw new Error (msg);
|
||||
}
|
||||
|
||||
function hex (n) {
|
||||
var s = Number (n).toString (16);
|
||||
if (s.length & 1) {
|
||||
s = "0" + s;
|
||||
}
|
||||
return "0x" + s;
|
||||
}
|
||||
|
||||
function findWrapperFlag () {
|
||||
var out = cmd ("f~onBadCertificateWrapper");
|
||||
var lines = out.split (/\r?\n/);
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
var line = trim (lines[i]);
|
||||
var m = line.match (/^(0x[0-9a-fA-F]+)\s+\d+\s+(method\._RawSecureSocket_.*\._onBadCertificateWrapper_.*)$/);
|
||||
if (m) {
|
||||
return {
|
||||
addr: parseInt (m[1], 16),
|
||||
name: m[2],
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function fileOffsetFor (addr) {
|
||||
var sections = cmdj ("iSj") || [];
|
||||
for (var i = 0; i < sections.length; i++) {
|
||||
var s = sections[i];
|
||||
var vaddr = Number (s.vaddr);
|
||||
var vsize = Number (s.vsize || s.size);
|
||||
var paddr = Number (s.paddr);
|
||||
if (!isFinite (vaddr) || !isFinite (vsize) || !isFinite (paddr)) {
|
||||
continue;
|
||||
}
|
||||
if (addr >= vaddr && addr < vaddr + vsize) {
|
||||
return {
|
||||
offset: paddr + (addr - vaddr),
|
||||
section: s.name || "",
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
offset: addr,
|
||||
section: "unmapped-fallback",
|
||||
};
|
||||
}
|
||||
|
||||
cmd ("e scr.color=0");
|
||||
cmd ("e bin.relocs.apply=true");
|
||||
|
||||
var arch = trim (cmd ("e asm.arch"));
|
||||
var bits = trim (cmd ("e asm.bits"));
|
||||
if (arch.indexOf ("arm") === -1 || bits !== "64") {
|
||||
fail ("unsupported-arch-" + arch + "-" + bits + "-expected-arm64");
|
||||
}
|
||||
|
||||
var flag = findWrapperFlag ();
|
||||
if (!flag) {
|
||||
cmd ("r2flutter -A");
|
||||
flag = findWrapperFlag ();
|
||||
}
|
||||
if (!flag) {
|
||||
fail ("missing-RawSecureSocket-onBadCertificateWrapper-flag");
|
||||
}
|
||||
|
||||
var loc = fileOffsetFor (flag.addr);
|
||||
var oldIoVa = trim (cmd ("e io.va"));
|
||||
var oldSeek = trim (cmd ("s"));
|
||||
var patch = "c0820091c0035fd6";
|
||||
|
||||
cmd ("e io.va=false");
|
||||
var before = trim (cmd ("p8 8 @ " + hex (loc.offset)));
|
||||
cmd ("wx " + patch + " @ " + hex (loc.offset));
|
||||
var after = trim (cmd ("p8 8 @ " + hex (loc.offset)));
|
||||
cmd ("e io.va=" + oldIoVa);
|
||||
if (oldSeek) {
|
||||
cmd ("s " + oldSeek);
|
||||
}
|
||||
|
||||
if (after !== patch) {
|
||||
fail ("write-failed-before-" + before + "-after-" + after);
|
||||
}
|
||||
|
||||
console.log ("target=" + flag.name);
|
||||
console.log ("vaddr=" + hex (flag.addr));
|
||||
console.log ("file_offset=" + hex (loc.offset));
|
||||
console.log ("section=" + loc.section);
|
||||
console.log ("before=" + before);
|
||||
console.log ("after=" + after);
|
||||
console.log ("status=" + (before === patch ? "already-patched" : "patched"));
|
||||
})();
|
||||
@@ -52,6 +52,30 @@ TAG_STYLE_NAMES = {
|
||||
"OBJECT_HEADER": "DART_TAG_STYLE_OBJECT_HEADER",
|
||||
}
|
||||
|
||||
HEADER_STYLE_NAMES = {
|
||||
"modern": "DART_HEADER_STYLE_MODERN",
|
||||
"2.10": "DART_HEADER_STYLE_210",
|
||||
"2.12": "DART_HEADER_STYLE_212",
|
||||
"2.14": "DART_HEADER_STYLE_214",
|
||||
"2.16": "DART_HEADER_STYLE_216",
|
||||
}
|
||||
|
||||
REF_ENCODING_NAMES = {
|
||||
"ref_id128": "DART_REF_ENCODING_REF_ID128",
|
||||
"unsigned": "DART_REF_ENCODING_UNSIGNED",
|
||||
}
|
||||
|
||||
POOL_ENCODING_NAMES = {
|
||||
"modern": "DART_POOL_ENCODING_MODERN",
|
||||
"low7": "DART_POOL_ENCODING_LOW7",
|
||||
"low7_swapped": "DART_POOL_ENCODING_LOW7_SWAPPED",
|
||||
}
|
||||
|
||||
CODE_ALLOC_NAMES = {
|
||||
"state_deferred": "DART_CODE_ALLOC_STATE_DEFERRED",
|
||||
"deferred_only": "DART_CODE_ALLOC_DEFERRED_ONLY",
|
||||
}
|
||||
|
||||
TAG_STYLE_VALUES = {v: k for k, v in TAG_STYLE_NAMES.items()}
|
||||
|
||||
CID_KIND_TO_CLASS_NAME = [
|
||||
@@ -75,6 +99,7 @@ CID_KIND_TO_CLASS_NAME = [
|
||||
("PC_DESCRIPTORS", "PcDescriptors"),
|
||||
("CODE_SOURCE_MAP", "CodeSourceMap"),
|
||||
("COMPRESSED_STACK_MAPS", "CompressedStackMaps"),
|
||||
("LOCAL_VAR_DESCRIPTORS", "LocalVarDescriptors"),
|
||||
("EXCEPTION_HANDLERS", "ExceptionHandlers"),
|
||||
("CONTEXT", "Context"),
|
||||
("CONTEXT_SCOPE", "ContextScope"),
|
||||
@@ -87,8 +112,10 @@ CID_KIND_TO_CLASS_NAME = [
|
||||
("MEGAMORPHIC_CACHE", "MegamorphicCache"),
|
||||
("SUBTYPE_TEST_CACHE", "SubtypeTestCache"),
|
||||
("LOADING_UNIT", "LoadingUnit"),
|
||||
("API_ERROR", "ApiError"),
|
||||
("LANGUAGE_ERROR", "LanguageError"),
|
||||
("UNHANDLED_EXCEPTION", "UnhandledException"),
|
||||
("UNWIND_ERROR", "UnwindError"),
|
||||
("INSTANCE", "Instance"),
|
||||
("LIBRARY_PREFIX", "LibraryPrefix"),
|
||||
("TYPE_ARGUMENTS", "TypeArguments"),
|
||||
@@ -555,7 +582,7 @@ def infer_profile_for_version(version):
|
||||
tag_style = "CID_INT32"
|
||||
if (2, 16) <= ver_tuple < (2, 18):
|
||||
hdr = 6
|
||||
elif ver_tuple >= (2, 13):
|
||||
elif ver_tuple >= (2, 12):
|
||||
hdr = 5
|
||||
else:
|
||||
hdr = 4
|
||||
@@ -573,6 +600,55 @@ def infer_profile_for_version(version):
|
||||
}
|
||||
|
||||
|
||||
def header_style_for_version(version):
|
||||
ver_tuple = parse_version(version)
|
||||
if ver_tuple < (2, 12):
|
||||
return HEADER_STYLE_NAMES["2.10"]
|
||||
if ver_tuple < (2, 14):
|
||||
return HEADER_STYLE_NAMES["2.12"]
|
||||
if ver_tuple < (2, 16):
|
||||
return HEADER_STYLE_NAMES["2.14"]
|
||||
if ver_tuple < (2, 18):
|
||||
return HEADER_STYLE_NAMES["2.16"]
|
||||
return HEADER_STYLE_NAMES["modern"]
|
||||
|
||||
|
||||
def ref_encoding_for_version(version):
|
||||
if parse_version(version) < (2, 18):
|
||||
return REF_ENCODING_NAMES["unsigned"]
|
||||
return REF_ENCODING_NAMES["ref_id128"]
|
||||
|
||||
|
||||
def pool_encoding_for_version(version):
|
||||
ver_tuple = parse_version(version)
|
||||
if ver_tuple < (3, 2):
|
||||
return POOL_ENCODING_NAMES["low7"]
|
||||
if ver_tuple < (3, 3):
|
||||
return POOL_ENCODING_NAMES["low7_swapped"]
|
||||
return POOL_ENCODING_NAMES["modern"]
|
||||
|
||||
|
||||
def code_layout_for_version(version):
|
||||
ver_tuple = parse_version(version)
|
||||
return {
|
||||
"alloc": CODE_ALLOC_NAMES["deferred_only" if ver_tuple < (2, 14) else "state_deferred"],
|
||||
"refs": 7 if ver_tuple < (2, 17) else 6,
|
||||
"leading_refs": 2 if ver_tuple >= (3, 13) else 0,
|
||||
"has_text_offset": "true" if ver_tuple < (2, 16) else "false",
|
||||
}
|
||||
|
||||
|
||||
def snapshot_layout_for_version(version):
|
||||
parsed = parse_version(version)
|
||||
modern_single = parsed >= (3, 13)
|
||||
return {
|
||||
"single": "true" if modern_single else "false",
|
||||
"class_fixed": "true" if modern_single else "false",
|
||||
"closure_variable": "true" if modern_single else "false",
|
||||
"class_top_level_cid20": "true" if parsed >= (2, 19) else "false",
|
||||
}
|
||||
|
||||
|
||||
def profile_diff(existing, inferred):
|
||||
diffs = []
|
||||
for key in ("cws", "heap_object_tag", "max_alignment", "it_cap", "tag_style", "header_fields"):
|
||||
@@ -686,10 +762,18 @@ def gen_profile_entries(offsets):
|
||||
for ver in sorted(profiles.keys(), key=parse_version):
|
||||
v = profiles[ver]
|
||||
tag_name = TAG_STYLE_NAMES.get(v["tag_style"], "0")
|
||||
header_style = header_style_for_version(ver)
|
||||
ref_encoding = ref_encoding_for_version(ver)
|
||||
pool_encoding = pool_encoding_for_version(ver)
|
||||
code = code_layout_for_version(ver)
|
||||
snapshot = snapshot_layout_for_version(ver)
|
||||
lines.append(f'\t{{ "", "{ver}", {v["cws"]}, {v["heap_object_tag"]}, '
|
||||
f'{v["max_alignment"]}, {v["it_cap"]}, {tag_name}, '
|
||||
f'{v["header_fields"]} }}, // Dart {ver}')
|
||||
lines.append("\t{ { 0 }, NULL, 0, 0, 0, 0, 0, 0 }")
|
||||
f'{v["header_fields"]}, {header_style}, {ref_encoding}, {pool_encoding}, '
|
||||
f'{code["alloc"]}, {code["refs"]}, {code["leading_refs"]}, {code["has_text_offset"]}, '
|
||||
f'{snapshot["single"]}, {snapshot["class_fixed"]}, {snapshot["closure_variable"]}, '
|
||||
f'{snapshot["class_top_level_cid20"]} }}, // Dart {ver}')
|
||||
lines.append("\t{ { 0 }, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }")
|
||||
lines.append("};")
|
||||
return "\n".join(lines)
|
||||
|
||||
@@ -728,11 +812,14 @@ def gen_dart_offsets_h(offsets):
|
||||
lines.append(f"#define DART_HASH_ENTRIES_COUNT {len(hashes)}")
|
||||
lines.append("")
|
||||
lines.append("// Default offset arrays (identical across all known Dart versions)")
|
||||
lines.append(f"#define DART_DEFAULT_EP_OFFSETS {{ {', '.join(str(x) for x in ep)} }}")
|
||||
lines.append("#define DART_DEFAULT_EP_OFFSETS \\")
|
||||
lines.append(f"\t{{ {', '.join(str(x) for x in ep)} }}")
|
||||
lines.append(f"#define DART_DEFAULT_EP_OFFSETS_COUNT {len(ep)}")
|
||||
lines.append(f"#define DART_DEFAULT_OWNER_OFFSETS {{ {', '.join(str(x) for x in owner)} }}")
|
||||
lines.append("#define DART_DEFAULT_OWNER_OFFSETS \\")
|
||||
lines.append(f"\t{{ {', '.join(str(x) for x in owner)} }}")
|
||||
lines.append(f"#define DART_DEFAULT_OWNER_OFFSETS_COUNT {len(owner)}")
|
||||
lines.append(f"#define DART_DEFAULT_NAME_OFFSETS {{ {', '.join(str(x) for x in name)} }}")
|
||||
lines.append("#define DART_DEFAULT_NAME_OFFSETS \\")
|
||||
lines.append(f"\t{{ {', '.join(str(x) for x in name)} }}")
|
||||
lines.append(f"#define DART_DEFAULT_NAME_OFFSETS_COUNT {len(name)}")
|
||||
lines.append("")
|
||||
lines.append("#endif // R2C_DART_OFFSETS_H")
|
||||
|
||||
+40
-17
@@ -141,7 +141,7 @@ static bool find_dart_note_in_fat_macho(RBuffer *b, ut64 fsize, bool fat64, Dart
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dart_app_find_macho_embedded_dart(const char *path, DartAppEmbeddedPayload *out) {
|
||||
bool dart_app_find_macho_dart(const char *path, DartAppEmbeddedPayload *out) {
|
||||
if (R_STR_ISEMPTY (path) || !out) {
|
||||
return false;
|
||||
}
|
||||
@@ -179,14 +179,25 @@ static FILE *open_payload_temp(char **out_path) {
|
||||
FILE *out = try_open_temp_path (r_file_temp ("r2flutter-dart-app-snap"), out_path);
|
||||
if (!out) {
|
||||
char *name = r_str_newf ("r2flutter-dart-app-snap.%" PFMT64x, (ut64)r_time_now ());
|
||||
char *path = name? r_file_new (P_tmpdir, name, NULL): NULL;
|
||||
char *tmpdir = r_file_tmpdir ();
|
||||
char *path = (name && tmpdir)? r_file_new (tmpdir, name, NULL): NULL;
|
||||
free (tmpdir);
|
||||
free (name);
|
||||
out = try_open_temp_path (path, out_path);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
char *dart_app_extract_embedded_payload(const char *path, const DartAppEmbeddedPayload *payload) {
|
||||
// msvc has no fseeko, but it ships an equivalent 64bit seek
|
||||
static bool payload_seek(FILE *f, ut64 off) {
|
||||
#if defined(_MSC_VER)
|
||||
return _fseeki64 (f, (__int64)off, SEEK_SET) == 0;
|
||||
#else
|
||||
return fseeko (f, (off_t)off, SEEK_SET) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
char *dart_app_extract_payload(const char *path, const DartAppEmbeddedPayload *payload) {
|
||||
if (R_STR_ISEMPTY (path) || !payload || !payload->payload_size) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -202,7 +213,7 @@ char *dart_app_extract_embedded_payload(const char *path, const DartAppEmbeddedP
|
||||
fclose (in);
|
||||
return NULL;
|
||||
}
|
||||
bool ok = fseeko (in, (off_t)payload->payload_offset, SEEK_SET) == 0;
|
||||
bool ok = payload_seek (in, payload->payload_offset);
|
||||
ut64 remaining = payload->payload_size;
|
||||
ut8 buf[65536];
|
||||
while (ok && remaining > 0) {
|
||||
@@ -231,6 +242,7 @@ char *dart_app_extract_embedded_payload(const char *path, const DartAppEmbeddedP
|
||||
|
||||
void dart_function_fini(DartFunction *fn) {
|
||||
free (fn->name);
|
||||
free (fn->signature);
|
||||
}
|
||||
|
||||
static ut64 dart_normalize_code_addr(ut64 addr) {
|
||||
@@ -253,6 +265,9 @@ static int dart_function_cmp(const DartFunction *fa, const DartFunction *fb) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Above placeholder names (func.*, method.fn_*) but below any resolved name.
|
||||
#define DART_CLASS_METHOD_NAME_QUALITY 20
|
||||
|
||||
static int dart_function_name_quality(const char *name) {
|
||||
if (R_STR_ISEMPTY (name)) {
|
||||
return 0;
|
||||
@@ -284,7 +299,7 @@ static char *dart_strdup_filtered(const char *name) {
|
||||
return dup;
|
||||
}
|
||||
|
||||
static void dart_app_add_or_update_fn(DartApp *app, const char *name, ut64 addr, ut64 size, int quality) {
|
||||
static void dart_app_add_or_update_fn(DartApp *app, const char *name, ut64 addr, ut64 size, int quality, const char *signature) {
|
||||
if (!app || !app->functions || !addr) {
|
||||
return;
|
||||
}
|
||||
@@ -300,6 +315,9 @@ static void dart_app_add_or_update_fn(DartApp *app, const char *name, ut64 addr,
|
||||
if (size > fn->size) {
|
||||
fn->size = size;
|
||||
}
|
||||
if (R_STR_ISNOTEMPTY (signature) && !fn->signature) {
|
||||
fn->signature = strdup (signature);
|
||||
}
|
||||
if (quality > fn->quality || (!fn->name && filtered)) {
|
||||
free (fn->name);
|
||||
fn->name = filtered;
|
||||
@@ -321,6 +339,7 @@ static void dart_app_add_or_update_fn(DartApp *app, const char *name, ut64 addr,
|
||||
newfn->addr = addr;
|
||||
newfn->size = size;
|
||||
newfn->quality = quality;
|
||||
newfn->signature = R_STR_ISNOTEMPTY (signature)? strdup (signature): NULL;
|
||||
if (filtered) {
|
||||
newfn->name = filtered;
|
||||
} else {
|
||||
@@ -460,14 +479,17 @@ static char *dart_format_method_full_name(const DartClassInfo *ci, const DartMet
|
||||
if (!mi || R_STR_ISEMPTY (mi->name)) {
|
||||
return NULL;
|
||||
}
|
||||
char *lib = dart_sanitize_component (ci && ci->library_name? ci->library_name: "app", true);
|
||||
// Without a library name use the InstructionTable form (method.Owner.leaf)
|
||||
// so both sources agree on the name of the same entry.
|
||||
char *lib = ci && ci->library_name? dart_sanitize_component (ci->library_name, true): NULL;
|
||||
char *owner = dart_sanitize_component (ci && ci->name? ci->name: mi->owner_name, false);
|
||||
char *leaf = dart_format_method_leaf_name (ci? ci->name: mi->owner_name, mi);
|
||||
const char *name = leaf? leaf: mi->name;
|
||||
char *out = NULL;
|
||||
if (R_STR_ISNOTEMPTY (owner)) {
|
||||
out = r_str_newf ("method.%s.%s.%s", lib, owner, leaf? leaf: mi->name);
|
||||
out = lib? r_str_newf ("method.%s.%s.%s", lib, owner, name): r_str_newf ("method.%s.%s", owner, name);
|
||||
} else {
|
||||
out = r_str_newf ("method.%s..%s", lib, leaf? leaf: mi->name);
|
||||
out = lib? r_str_newf ("method.%s..%s", lib, name): r_str_newf ("method.%s", name);
|
||||
}
|
||||
r_name_filter (out, 0);
|
||||
free (lib);
|
||||
@@ -490,7 +512,8 @@ static void dart_app_load_it_functions(DartApp *app) {
|
||||
entry->name? entry->name: "method.unknown",
|
||||
dart_normalize_code_addr (entry->address),
|
||||
0,
|
||||
dart_function_name_quality (entry->name));
|
||||
dart_function_name_quality (entry->name),
|
||||
entry->signature);
|
||||
}
|
||||
dart_instruction_table_list_free (entries);
|
||||
}
|
||||
@@ -516,11 +539,15 @@ static void dart_app_merge_class_methods(DartApp *app) {
|
||||
continue;
|
||||
}
|
||||
char *fullname = dart_format_method_full_name (ci, mi);
|
||||
// InstructionTable names stay authoritative: deduplicated code is
|
||||
// shared by several methods, so class metadata only fills the
|
||||
// placeholder names the table could not resolve.
|
||||
dart_app_add_or_update_fn (app,
|
||||
fullname? fullname: mi->name,
|
||||
dart_normalize_code_addr (mi->entry_point),
|
||||
0,
|
||||
dart_function_name_quality (fullname? fullname: mi->name));
|
||||
R_MIN (dart_function_name_quality (fullname? fullname: mi->name), DART_CLASS_METHOD_NAME_QUALITY),
|
||||
mi->signature);
|
||||
free (fullname);
|
||||
}
|
||||
}
|
||||
@@ -570,7 +597,8 @@ static void add_fn_cb(const char *name, ut64 addr, ut64 size, void *user) {
|
||||
name,
|
||||
dart_normalize_code_addr (addr),
|
||||
size,
|
||||
dart_function_name_quality (name));
|
||||
dart_function_name_quality (name),
|
||||
NULL);
|
||||
}
|
||||
|
||||
void dart_app_load_info(DartApp *app) {
|
||||
@@ -586,12 +614,7 @@ void dart_app_load_info(DartApp *app) {
|
||||
dart_app_load_it_functions (app);
|
||||
dart_app_merge_class_methods (app);
|
||||
if (RVecDartFunction_length (app->functions) == 0) {
|
||||
ut64 base = 0, heap_base = 0;
|
||||
int rc = dart_pool_enumerate (&app->dctx, app->file_path, add_fn_cb, app, &base, &heap_base);
|
||||
if (rc == 0) {
|
||||
app->base_addr = base;
|
||||
app->heap_base = heap_base;
|
||||
}
|
||||
(void)dart_pool_scan (&app->dctx, add_fn_cb, app);
|
||||
}
|
||||
RVecDartFunction_sort (app->functions, dart_function_cmp);
|
||||
if (app->dctx.verbose) {
|
||||
|
||||
+57
-26
@@ -1,6 +1,7 @@
|
||||
/* r2flutter - MIT - Copyright 2026 - pancake */
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../../include/r2flutter/dart_cid.h"
|
||||
|
||||
@@ -16,25 +17,32 @@ typedef struct {
|
||||
|
||||
static const DartCidTable cid_tables[] = {
|
||||
// clang-format off
|
||||
{ "2.10.0", { CID (ILLEGAL, 0), CID (CLASS, 4), CID (PATCH_CLASS, 5), CID (FUNCTION, 6), CID (CLOSURE_DATA, 7), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 77), CID (CODE, 16), CID (OBJECT_POOL, 20), CID (PC_DESCRIPTORS, 21), CID (CODE_SOURCE_MAP, 22), CID (COMPRESSED_STACK_MAPS, 23), CID (EXCEPTION_HANDLERS, 25), CID (CONTEXT, 26), CID (CONTEXT_SCOPE, 27), CID (SINGLE_TARGET_CACHE, 29), CID (UNLINKED_CALL, 30), CID (MONOMORPHIC_SMIABLE_CALL, 31), CID (CALL_SITE_DATA, 32), CID (ICDATA, 33), CID (MEGAMORPHIC_CACHE, 34), CID (SUBTYPE_TEST_CACHE, 35), CID (LOADING_UNIT, 36), CID (LANGUAGE_ERROR, 39), CID (UNHANDLED_EXCEPTION, 40), CID (INSTANCE, 42), CID (LIBRARY_PREFIX, 43), CID (TYPE_ARGUMENTS, 44), CID (TYPE, 46), CID (TYPE_REF, 47), CID (TYPE_PARAMETER, 48), CID (CLOSURE, 49), CID (MINT, 53), CID (DOUBLE, 54), CID (FLOAT32X4, 57), CID (INT32X4, 58), CID (FLOAT64X2, 59), CID (TYPED_DATA, 61), CID (EXTERNAL_TYPED_DATA, 62), CID (TYPED_DATA_VIEW, 63), CID (CAPABILITY, 66), CID (RECEIVE_PORT, 67), CID (SEND_PORT, 68), CID (STACK_TRACE, 69), CID (REGEXP, 70), CID (WEAK_PROPERTY, 71), CID (FUTURE_OR, 74), CID (USER_TAG, 75), CID (TRANSFERABLE_TYPED_DATA, 76), CID (ARRAY, 78), CID (IMMUTABLE_ARRAY, 79), CID (GROWABLE_OBJECT_ARRAY, 56), CID (STRING, 80), CID (ONE_BYTE_STRING, 81), CID (TWO_BYTE_STRING, 82), CID (NUM_PREDEFINED_CIDS, 156) }, 108, 150, 3 },
|
||||
{ "2.12.0", { CID (ILLEGAL, 0), CID (CLASS, 4), CID (PATCH_CLASS, 5), CID (FUNCTION, 6), CID (CLOSURE_DATA, 7), CID (FFI_TRAMPOLINE_DATA, 8), CID (FIELD, 9), CID (SCRIPT, 10), CID (LIBRARY, 11), CID (NAMESPACE, 12), CID (KERNEL_PROGRAM_INFO, 13), CID (WEAK_SERIALIZATION_REFERENCE, 74), CID (CODE, 14), CID (OBJECT_POOL, 17), CID (PC_DESCRIPTORS, 18), CID (CODE_SOURCE_MAP, 19), CID (COMPRESSED_STACK_MAPS, 20), CID (EXCEPTION_HANDLERS, 22), CID (CONTEXT, 23), CID (CONTEXT_SCOPE, 24), CID (SINGLE_TARGET_CACHE, 25), CID (UNLINKED_CALL, 26), CID (MONOMORPHIC_SMIABLE_CALL, 27), CID (CALL_SITE_DATA, 28), CID (ICDATA, 29), CID (MEGAMORPHIC_CACHE, 30), CID (SUBTYPE_TEST_CACHE, 31), CID (LOADING_UNIT, 32), CID (LANGUAGE_ERROR, 35), CID (UNHANDLED_EXCEPTION, 36), CID (INSTANCE, 38), CID (LIBRARY_PREFIX, 39), CID (TYPE_ARGUMENTS, 40), CID (TYPE, 42), CID (FUNCTION_TYPE, 43), CID (TYPE_REF, 44), CID (TYPE_PARAMETER, 45), CID (CLOSURE, 46), CID (MINT, 50), CID (DOUBLE, 51), CID (FLOAT32X4, 54), CID (INT32X4, 55), CID (FLOAT64X2, 56), CID (TYPED_DATA, 58), CID (EXTERNAL_TYPED_DATA, 59), CID (TYPED_DATA_VIEW, 60), CID (CAPABILITY, 63), CID (RECEIVE_PORT, 64), CID (SEND_PORT, 65), CID (STACK_TRACE, 66), CID (REGEXP, 67), CID (WEAK_PROPERTY, 68), CID (FUTURE_OR, 71), CID (USER_TAG, 72), CID (TRANSFERABLE_TYPED_DATA, 73), CID (ARRAY, 75), CID (IMMUTABLE_ARRAY, 76), CID (GROWABLE_OBJECT_ARRAY, 53), CID (STRING, 77), CID (ONE_BYTE_STRING, 78), CID (TWO_BYTE_STRING, 79), CID (NUM_PREDEFINED_CIDS, 148) }, 100, 142, 3 },
|
||||
{ "2.13.0", { CID (ILLEGAL, 0), CID (CLASS, 4), CID (PATCH_CLASS, 5), CID (FUNCTION, 6), CID (CLOSURE_DATA, 7), CID (FFI_TRAMPOLINE_DATA, 8), CID (FIELD, 9), CID (SCRIPT, 10), CID (LIBRARY, 11), CID (NAMESPACE, 12), CID (KERNEL_PROGRAM_INFO, 13), CID (WEAK_SERIALIZATION_REFERENCE, 14), CID (CODE, 15), CID (OBJECT_POOL, 18), CID (PC_DESCRIPTORS, 19), CID (CODE_SOURCE_MAP, 20), CID (COMPRESSED_STACK_MAPS, 21), CID (EXCEPTION_HANDLERS, 23), CID (CONTEXT, 24), CID (CONTEXT_SCOPE, 25), CID (SINGLE_TARGET_CACHE, 26), CID (UNLINKED_CALL, 27), CID (MONOMORPHIC_SMIABLE_CALL, 28), CID (CALL_SITE_DATA, 29), CID (ICDATA, 30), CID (MEGAMORPHIC_CACHE, 31), CID (SUBTYPE_TEST_CACHE, 32), CID (LOADING_UNIT, 33), CID (LANGUAGE_ERROR, 36), CID (UNHANDLED_EXCEPTION, 37), CID (INSTANCE, 39), CID (LIBRARY_PREFIX, 40), CID (TYPE_ARGUMENTS, 41), CID (TYPE, 43), CID (FUNCTION_TYPE, 44), CID (TYPE_REF, 45), CID (TYPE_PARAMETER, 46), CID (CLOSURE, 47), CID (MINT, 51), CID (DOUBLE, 52), CID (FLOAT32X4, 55), CID (INT32X4, 56), CID (FLOAT64X2, 57), CID (TYPED_DATA, 59), CID (EXTERNAL_TYPED_DATA, 60), CID (TYPED_DATA_VIEW, 61), CID (CAPABILITY, 64), CID (RECEIVE_PORT, 65), CID (SEND_PORT, 66), CID (STACK_TRACE, 67), CID (REGEXP, 68), CID (WEAK_PROPERTY, 69), CID (FUTURE_OR, 72), CID (USER_TAG, 73), CID (TRANSFERABLE_TYPED_DATA, 74), CID (ARRAY, 75), CID (IMMUTABLE_ARRAY, 76), CID (GROWABLE_OBJECT_ARRAY, 54), CID (STRING, 77), CID (ONE_BYTE_STRING, 78), CID (TWO_BYTE_STRING, 79), CID (NUM_PREDEFINED_CIDS, 148) }, 100, 142, 3 },
|
||||
{ "2.14.0", { CID (ILLEGAL, 0), CID (CLASS, 4), CID (PATCH_CLASS, 5), CID (FUNCTION, 6), CID (TYPE_PARAMETERS, 7), CID (CLOSURE_DATA, 8), CID (FFI_TRAMPOLINE_DATA, 9), CID (FIELD, 10), CID (SCRIPT, 11), CID (LIBRARY, 12), CID (NAMESPACE, 13), CID (KERNEL_PROGRAM_INFO, 14), CID (WEAK_SERIALIZATION_REFERENCE, 15), CID (CODE, 16), CID (OBJECT_POOL, 20), CID (PC_DESCRIPTORS, 21), CID (CODE_SOURCE_MAP, 22), CID (COMPRESSED_STACK_MAPS, 23), CID (EXCEPTION_HANDLERS, 25), CID (CONTEXT, 26), CID (CONTEXT_SCOPE, 27), CID (SENTINEL, 28), CID (SINGLE_TARGET_CACHE, 29), CID (UNLINKED_CALL, 30), CID (MONOMORPHIC_SMIABLE_CALL, 31), CID (CALL_SITE_DATA, 32), CID (ICDATA, 33), CID (MEGAMORPHIC_CACHE, 34), CID (SUBTYPE_TEST_CACHE, 35), CID (LOADING_UNIT, 36), CID (LANGUAGE_ERROR, 39), CID (UNHANDLED_EXCEPTION, 40), CID (INSTANCE, 42), CID (LIBRARY_PREFIX, 43), CID (TYPE_ARGUMENTS, 44), CID (TYPE, 46), CID (FUNCTION_TYPE, 47), CID (TYPE_REF, 48), CID (TYPE_PARAMETER, 49), CID (CLOSURE, 50), CID (MINT, 54), CID (DOUBLE, 55), CID (FLOAT32X4, 58), CID (INT32X4, 59), CID (FLOAT64X2, 60), CID (TYPED_DATA, 62), CID (EXTERNAL_TYPED_DATA, 63), CID (TYPED_DATA_VIEW, 64), CID (CAPABILITY, 67), CID (RECEIVE_PORT, 68), CID (SEND_PORT, 69), CID (STACK_TRACE, 70), CID (REGEXP, 71), CID (WEAK_PROPERTY, 72), CID (FUTURE_OR, 74), CID (USER_TAG, 75), CID (TRANSFERABLE_TYPED_DATA, 76), CID (ARRAY, 79), CID (IMMUTABLE_ARRAY, 80), CID (GROWABLE_OBJECT_ARRAY, 57), CID (STRING, 81), CID (ONE_BYTE_STRING, 82), CID (TWO_BYTE_STRING, 83), CID (NUM_PREDEFINED_CIDS, 152) }, 104, 146, 3 },
|
||||
{ "2.15.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (CODE, 17), CID (OBJECT_POOL, 21), CID (PC_DESCRIPTORS, 22), CID (CODE_SOURCE_MAP, 23), CID (COMPRESSED_STACK_MAPS, 24), CID (EXCEPTION_HANDLERS, 26), CID (CONTEXT, 27), CID (CONTEXT_SCOPE, 28), CID (SENTINEL, 29), CID (SINGLE_TARGET_CACHE, 30), CID (UNLINKED_CALL, 31), CID (MONOMORPHIC_SMIABLE_CALL, 32), CID (CALL_SITE_DATA, 33), CID (ICDATA, 34), CID (MEGAMORPHIC_CACHE, 35), CID (SUBTYPE_TEST_CACHE, 36), CID (LOADING_UNIT, 37), CID (LANGUAGE_ERROR, 40), CID (UNHANDLED_EXCEPTION, 41), CID (INSTANCE, 43), CID (LIBRARY_PREFIX, 44), CID (TYPE_ARGUMENTS, 45), CID (TYPE, 47), CID (FUNCTION_TYPE, 48), CID (TYPE_REF, 49), CID (TYPE_PARAMETER, 50), CID (CLOSURE, 51), CID (MINT, 55), CID (DOUBLE, 56), CID (FLOAT32X4, 58), CID (INT32X4, 59), CID (FLOAT64X2, 60), CID (TYPED_DATA, 62), CID (EXTERNAL_TYPED_DATA, 63), CID (TYPED_DATA_VIEW, 64), CID (CAPABILITY, 67), CID (RECEIVE_PORT, 68), CID (SEND_PORT, 69), CID (STACK_TRACE, 70), CID (REGEXP, 71), CID (WEAK_PROPERTY, 72), CID (FUTURE_OR, 74), CID (USER_TAG, 75), CID (TRANSFERABLE_TYPED_DATA, 76), CID (ARRAY, 81), CID (IMMUTABLE_ARRAY, 82), CID (GROWABLE_OBJECT_ARRAY, 83), CID (STRING, 84), CID (ONE_BYTE_STRING, 85), CID (TWO_BYTE_STRING, 86), CID (NUM_PREDEFINED_CIDS, 154) }, 106, 148, 3 },
|
||||
{ "2.16.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (CODE, 17), CID (OBJECT_POOL, 21), CID (PC_DESCRIPTORS, 22), CID (CODE_SOURCE_MAP, 23), CID (COMPRESSED_STACK_MAPS, 24), CID (EXCEPTION_HANDLERS, 26), CID (CONTEXT, 27), CID (CONTEXT_SCOPE, 28), CID (SENTINEL, 29), CID (SINGLE_TARGET_CACHE, 30), CID (UNLINKED_CALL, 31), CID (MONOMORPHIC_SMIABLE_CALL, 32), CID (CALL_SITE_DATA, 33), CID (ICDATA, 34), CID (MEGAMORPHIC_CACHE, 35), CID (SUBTYPE_TEST_CACHE, 36), CID (LOADING_UNIT, 37), CID (LANGUAGE_ERROR, 40), CID (UNHANDLED_EXCEPTION, 41), CID (INSTANCE, 43), CID (LIBRARY_PREFIX, 44), CID (TYPE_ARGUMENTS, 45), CID (TYPE, 47), CID (FUNCTION_TYPE, 48), CID (TYPE_REF, 49), CID (TYPE_PARAMETER, 50), CID (CLOSURE, 51), CID (MINT, 55), CID (DOUBLE, 56), CID (FLOAT32X4, 58), CID (INT32X4, 59), CID (FLOAT64X2, 60), CID (TYPED_DATA, 62), CID (EXTERNAL_TYPED_DATA, 63), CID (TYPED_DATA_VIEW, 64), CID (CAPABILITY, 67), CID (RECEIVE_PORT, 68), CID (SEND_PORT, 69), CID (STACK_TRACE, 70), CID (REGEXP, 71), CID (WEAK_PROPERTY, 72), CID (FUTURE_OR, 74), CID (USER_TAG, 75), CID (TRANSFERABLE_TYPED_DATA, 76), CID (ARRAY, 81), CID (IMMUTABLE_ARRAY, 82), CID (GROWABLE_OBJECT_ARRAY, 83), CID (STRING, 84), CID (ONE_BYTE_STRING, 85), CID (TWO_BYTE_STRING, 86), CID (NUM_PREDEFINED_CIDS, 154) }, 106, 148, 3 },
|
||||
{ "2.17.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (CODE, 17), CID (OBJECT_POOL, 21), CID (PC_DESCRIPTORS, 22), CID (CODE_SOURCE_MAP, 23), CID (COMPRESSED_STACK_MAPS, 24), CID (EXCEPTION_HANDLERS, 26), CID (CONTEXT, 27), CID (CONTEXT_SCOPE, 28), CID (SENTINEL, 29), CID (SINGLE_TARGET_CACHE, 30), CID (UNLINKED_CALL, 31), CID (MONOMORPHIC_SMIABLE_CALL, 32), CID (CALL_SITE_DATA, 33), CID (ICDATA, 34), CID (MEGAMORPHIC_CACHE, 35), CID (SUBTYPE_TEST_CACHE, 36), CID (LOADING_UNIT, 37), CID (LANGUAGE_ERROR, 40), CID (UNHANDLED_EXCEPTION, 41), CID (INSTANCE, 43), CID (LIBRARY_PREFIX, 44), CID (TYPE_ARGUMENTS, 45), CID (TYPE, 47), CID (FUNCTION_TYPE, 52), CID (TYPE_REF, 53), CID (TYPE_PARAMETER, 54), CID (CLOSURE, 55), CID (MINT, 59), CID (DOUBLE, 60), CID (FLOAT32X4, 62), CID (INT32X4, 63), CID (FLOAT64X2, 64), CID (TYPED_DATA, 66), CID (EXTERNAL_TYPED_DATA, 67), CID (TYPED_DATA_VIEW, 68), CID (CAPABILITY, 71), CID (RECEIVE_PORT, 72), CID (SEND_PORT, 73), CID (STACK_TRACE, 74), CID (REGEXP, 75), CID (WEAK_PROPERTY, 76), CID (WEAK_REFERENCE, 77), CID (FUTURE_OR, 79), CID (USER_TAG, 80), CID (TRANSFERABLE_TYPED_DATA, 81), CID (ARRAY, 86), CID (IMMUTABLE_ARRAY, 87), CID (GROWABLE_OBJECT_ARRAY, 88), CID (STRING, 89), CID (ONE_BYTE_STRING, 90), CID (TWO_BYTE_STRING, 91), CID (NUM_PREDEFINED_CIDS, 158) }, 110, 152, 3 },
|
||||
{ "2.18.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (CODE, 17), CID (OBJECT_POOL, 21), CID (PC_DESCRIPTORS, 22), CID (CODE_SOURCE_MAP, 23), CID (COMPRESSED_STACK_MAPS, 24), CID (EXCEPTION_HANDLERS, 26), CID (CONTEXT, 27), CID (CONTEXT_SCOPE, 28), CID (SENTINEL, 29), CID (SINGLE_TARGET_CACHE, 30), CID (UNLINKED_CALL, 31), CID (MONOMORPHIC_SMIABLE_CALL, 32), CID (CALL_SITE_DATA, 33), CID (ICDATA, 34), CID (MEGAMORPHIC_CACHE, 35), CID (SUBTYPE_TEST_CACHE, 36), CID (LOADING_UNIT, 37), CID (LANGUAGE_ERROR, 40), CID (UNHANDLED_EXCEPTION, 41), CID (INSTANCE, 43), CID (LIBRARY_PREFIX, 44), CID (TYPE_ARGUMENTS, 45), CID (TYPE, 47), CID (FUNCTION_TYPE, 52), CID (TYPE_REF, 53), CID (TYPE_PARAMETER, 54), CID (CLOSURE, 55), CID (MINT, 59), CID (DOUBLE, 60), CID (FLOAT32X4, 62), CID (INT32X4, 63), CID (FLOAT64X2, 64), CID (TYPED_DATA, 66), CID (EXTERNAL_TYPED_DATA, 67), CID (TYPED_DATA_VIEW, 68), CID (CAPABILITY, 71), CID (RECEIVE_PORT, 72), CID (SEND_PORT, 73), CID (STACK_TRACE, 74), CID (SUSPEND_STATE, 75), CID (REGEXP, 76), CID (WEAK_PROPERTY, 77), CID (WEAK_REFERENCE, 78), CID (FUTURE_OR, 80), CID (USER_TAG, 81), CID (TRANSFERABLE_TYPED_DATA, 82), CID (ARRAY, 87), CID (IMMUTABLE_ARRAY, 88), CID (GROWABLE_OBJECT_ARRAY, 89), CID (STRING, 90), CID (ONE_BYTE_STRING, 91), CID (TWO_BYTE_STRING, 92), CID (NUM_PREDEFINED_CIDS, 159) }, 111, 153, 3 },
|
||||
{ "2.18.2", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (WEAK_ARRAY, 17), CID (CODE, 18), CID (OBJECT_POOL, 23), CID (PC_DESCRIPTORS, 24), CID (CODE_SOURCE_MAP, 25), CID (COMPRESSED_STACK_MAPS, 26), CID (EXCEPTION_HANDLERS, 28), CID (CONTEXT, 29), CID (CONTEXT_SCOPE, 30), CID (SENTINEL, 31), CID (SINGLE_TARGET_CACHE, 32), CID (UNLINKED_CALL, 35), CID (MONOMORPHIC_SMIABLE_CALL, 33), CID (CALL_SITE_DATA, 34), CID (ICDATA, 36), CID (MEGAMORPHIC_CACHE, 37), CID (SUBTYPE_TEST_CACHE, 38), CID (LOADING_UNIT, 39), CID (LANGUAGE_ERROR, 42), CID (UNHANDLED_EXCEPTION, 43), CID (INSTANCE, 45), CID (LIBRARY_PREFIX, 46), CID (TYPE_ARGUMENTS, 47), CID (TYPE, 49), CID (FUNCTION_TYPE, 50), CID (RECORD_TYPE, 51), CID (TYPE_PARAMETER, 52), CID (CLOSURE, 57), CID (MINT, 61), CID (DOUBLE, 62), CID (FLOAT32X4, 64), CID (INT32X4, 65), CID (FLOAT64X2, 66), CID (RECORD, 67), CID (TYPED_DATA, 69), CID (EXTERNAL_TYPED_DATA, 70), CID (TYPED_DATA_VIEW, 71), CID (CAPABILITY, 74), CID (RECEIVE_PORT, 75), CID (SEND_PORT, 76), CID (STACK_TRACE, 77), CID (SUSPEND_STATE, 78), CID (REGEXP, 79), CID (WEAK_PROPERTY, 80), CID (WEAK_REFERENCE, 81), CID (FUTURE_OR, 83), CID (USER_TAG, 84), CID (TRANSFERABLE_TYPED_DATA, 85), CID (MAP, 86), CID (CONST_MAP, 87), CID (SET, 88), CID (CONST_SET, 89), CID (ARRAY, 90), CID (IMMUTABLE_ARRAY, 91), CID (GROWABLE_OBJECT_ARRAY, 92), CID (STRING, 93), CID (ONE_BYTE_STRING, 94), CID (TWO_BYTE_STRING, 95), CID (NUM_PREDEFINED_CIDS, 175) }, 112, 168, 4 },
|
||||
{ "2.18.3", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (CODE, 17), CID (OBJECT_POOL, 21), CID (PC_DESCRIPTORS, 22), CID (CODE_SOURCE_MAP, 23), CID (COMPRESSED_STACK_MAPS, 24), CID (EXCEPTION_HANDLERS, 26), CID (CONTEXT, 27), CID (CONTEXT_SCOPE, 28), CID (SENTINEL, 29), CID (SINGLE_TARGET_CACHE, 30), CID (UNLINKED_CALL, 31), CID (MONOMORPHIC_SMIABLE_CALL, 32), CID (CALL_SITE_DATA, 33), CID (ICDATA, 34), CID (MEGAMORPHIC_CACHE, 35), CID (SUBTYPE_TEST_CACHE, 36), CID (LOADING_UNIT, 37), CID (LANGUAGE_ERROR, 40), CID (UNHANDLED_EXCEPTION, 41), CID (INSTANCE, 43), CID (LIBRARY_PREFIX, 44), CID (TYPE_ARGUMENTS, 45), CID (TYPE, 47), CID (FUNCTION_TYPE, 52), CID (TYPE_REF, 53), CID (TYPE_PARAMETER, 54), CID (CLOSURE, 55), CID (MINT, 59), CID (DOUBLE, 60), CID (FLOAT32X4, 62), CID (INT32X4, 63), CID (FLOAT64X2, 64), CID (TYPED_DATA, 66), CID (EXTERNAL_TYPED_DATA, 67), CID (TYPED_DATA_VIEW, 68), CID (CAPABILITY, 71), CID (RECEIVE_PORT, 72), CID (SEND_PORT, 73), CID (STACK_TRACE, 74), CID (SUSPEND_STATE, 75), CID (REGEXP, 76), CID (WEAK_PROPERTY, 77), CID (WEAK_REFERENCE, 78), CID (FUTURE_OR, 80), CID (USER_TAG, 81), CID (TRANSFERABLE_TYPED_DATA, 82), CID (ARRAY, 87), CID (IMMUTABLE_ARRAY, 88), CID (GROWABLE_OBJECT_ARRAY, 89), CID (STRING, 90), CID (ONE_BYTE_STRING, 91), CID (TWO_BYTE_STRING, 92), CID (NUM_PREDEFINED_CIDS, 159) }, 111, 153, 3 },
|
||||
{ "2.19.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (CODE, 17), CID (OBJECT_POOL, 21), CID (PC_DESCRIPTORS, 22), CID (CODE_SOURCE_MAP, 23), CID (COMPRESSED_STACK_MAPS, 24), CID (EXCEPTION_HANDLERS, 26), CID (CONTEXT, 27), CID (CONTEXT_SCOPE, 28), CID (SENTINEL, 29), CID (SINGLE_TARGET_CACHE, 30), CID (UNLINKED_CALL, 31), CID (MONOMORPHIC_SMIABLE_CALL, 32), CID (CALL_SITE_DATA, 33), CID (ICDATA, 34), CID (MEGAMORPHIC_CACHE, 35), CID (SUBTYPE_TEST_CACHE, 36), CID (LOADING_UNIT, 37), CID (LANGUAGE_ERROR, 40), CID (UNHANDLED_EXCEPTION, 41), CID (INSTANCE, 43), CID (LIBRARY_PREFIX, 44), CID (TYPE_ARGUMENTS, 45), CID (TYPE, 47), CID (FUNCTION_TYPE, 48), CID (RECORD_TYPE, 49), CID (TYPE_REF, 50), CID (TYPE_PARAMETER, 51), CID (CLOSURE, 56), CID (MINT, 60), CID (DOUBLE, 61), CID (FLOAT32X4, 63), CID (INT32X4, 64), CID (FLOAT64X2, 65), CID (RECORD, 66), CID (TYPED_DATA, 68), CID (EXTERNAL_TYPED_DATA, 69), CID (TYPED_DATA_VIEW, 70), CID (CAPABILITY, 73), CID (RECEIVE_PORT, 74), CID (SEND_PORT, 75), CID (STACK_TRACE, 76), CID (SUSPEND_STATE, 77), CID (REGEXP, 78), CID (WEAK_PROPERTY, 79), CID (WEAK_REFERENCE, 80), CID (FUTURE_OR, 82), CID (USER_TAG, 83), CID (TRANSFERABLE_TYPED_DATA, 84), CID (MAP, 85), CID (CONST_MAP, 86), CID (SET, 87), CID (CONST_SET, 88), CID (ARRAY, 89), CID (IMMUTABLE_ARRAY, 90), CID (GROWABLE_OBJECT_ARRAY, 91), CID (STRING, 92), CID (ONE_BYTE_STRING, 93), CID (TWO_BYTE_STRING, 94), CID (NUM_PREDEFINED_CIDS, 176) }, 113, 169, 4 },
|
||||
{ "3.0.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (WEAK_ARRAY, 17), CID (CODE, 18), CID (OBJECT_POOL, 22), CID (PC_DESCRIPTORS, 23), CID (CODE_SOURCE_MAP, 24), CID (COMPRESSED_STACK_MAPS, 25), CID (EXCEPTION_HANDLERS, 27), CID (CONTEXT, 28), CID (CONTEXT_SCOPE, 29), CID (SENTINEL, 30), CID (SINGLE_TARGET_CACHE, 31), CID (UNLINKED_CALL, 32), CID (MONOMORPHIC_SMIABLE_CALL, 33), CID (CALL_SITE_DATA, 34), CID (ICDATA, 35), CID (MEGAMORPHIC_CACHE, 36), CID (SUBTYPE_TEST_CACHE, 37), CID (LOADING_UNIT, 38), CID (LANGUAGE_ERROR, 41), CID (UNHANDLED_EXCEPTION, 42), CID (INSTANCE, 44), CID (LIBRARY_PREFIX, 45), CID (TYPE_ARGUMENTS, 46), CID (TYPE, 48), CID (FUNCTION_TYPE, 49), CID (RECORD_TYPE, 50), CID (TYPE_REF, 51), CID (TYPE_PARAMETER, 52), CID (CLOSURE, 57), CID (MINT, 61), CID (DOUBLE, 62), CID (FLOAT32X4, 64), CID (INT32X4, 65), CID (FLOAT64X2, 66), CID (RECORD, 67), CID (TYPED_DATA, 69), CID (EXTERNAL_TYPED_DATA, 70), CID (TYPED_DATA_VIEW, 71), CID (CAPABILITY, 74), CID (RECEIVE_PORT, 75), CID (SEND_PORT, 76), CID (STACK_TRACE, 77), CID (SUSPEND_STATE, 78), CID (REGEXP, 79), CID (WEAK_PROPERTY, 80), CID (WEAK_REFERENCE, 81), CID (FUTURE_OR, 83), CID (USER_TAG, 84), CID (TRANSFERABLE_TYPED_DATA, 85), CID (MAP, 86), CID (CONST_MAP, 87), CID (SET, 88), CID (CONST_SET, 89), CID (ARRAY, 90), CID (IMMUTABLE_ARRAY, 91), CID (GROWABLE_OBJECT_ARRAY, 92), CID (STRING, 93), CID (ONE_BYTE_STRING, 94), CID (TWO_BYTE_STRING, 95), CID (NUM_PREDEFINED_CIDS, 177) }, 114, 170, 4 },
|
||||
{ "3.1.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (WEAK_ARRAY, 17), CID (CODE, 18), CID (OBJECT_POOL, 22), CID (PC_DESCRIPTORS, 23), CID (CODE_SOURCE_MAP, 24), CID (COMPRESSED_STACK_MAPS, 25), CID (EXCEPTION_HANDLERS, 27), CID (CONTEXT, 28), CID (CONTEXT_SCOPE, 29), CID (SENTINEL, 30), CID (SINGLE_TARGET_CACHE, 31), CID (UNLINKED_CALL, 32), CID (MONOMORPHIC_SMIABLE_CALL, 33), CID (CALL_SITE_DATA, 34), CID (ICDATA, 35), CID (MEGAMORPHIC_CACHE, 36), CID (SUBTYPE_TEST_CACHE, 37), CID (LOADING_UNIT, 38), CID (LANGUAGE_ERROR, 41), CID (UNHANDLED_EXCEPTION, 42), CID (INSTANCE, 44), CID (LIBRARY_PREFIX, 45), CID (TYPE_ARGUMENTS, 46), CID (TYPE, 48), CID (FUNCTION_TYPE, 49), CID (RECORD_TYPE, 50), CID (TYPE_PARAMETER, 51), CID (CLOSURE, 56), CID (MINT, 60), CID (DOUBLE, 61), CID (FLOAT32X4, 63), CID (INT32X4, 64), CID (FLOAT64X2, 65), CID (RECORD, 66), CID (TYPED_DATA, 68), CID (EXTERNAL_TYPED_DATA, 69), CID (TYPED_DATA_VIEW, 70), CID (CAPABILITY, 73), CID (RECEIVE_PORT, 74), CID (SEND_PORT, 75), CID (STACK_TRACE, 76), CID (SUSPEND_STATE, 77), CID (REGEXP, 78), CID (WEAK_PROPERTY, 79), CID (WEAK_REFERENCE, 80), CID (FUTURE_OR, 82), CID (USER_TAG, 83), CID (TRANSFERABLE_TYPED_DATA, 84), CID (MAP, 85), CID (CONST_MAP, 86), CID (SET, 87), CID (CONST_SET, 88), CID (ARRAY, 89), CID (IMMUTABLE_ARRAY, 90), CID (GROWABLE_OBJECT_ARRAY, 91), CID (STRING, 92), CID (ONE_BYTE_STRING, 93), CID (TWO_BYTE_STRING, 94), CID (NUM_PREDEFINED_CIDS, 176) }, 113, 169, 4 },
|
||||
{ "3.2.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (WEAK_ARRAY, 17), CID (CODE, 18), CID (OBJECT_POOL, 22), CID (PC_DESCRIPTORS, 23), CID (CODE_SOURCE_MAP, 24), CID (COMPRESSED_STACK_MAPS, 25), CID (EXCEPTION_HANDLERS, 27), CID (CONTEXT, 28), CID (CONTEXT_SCOPE, 29), CID (SENTINEL, 30), CID (SINGLE_TARGET_CACHE, 31), CID (UNLINKED_CALL, 32), CID (MONOMORPHIC_SMIABLE_CALL, 33), CID (CALL_SITE_DATA, 34), CID (ICDATA, 35), CID (MEGAMORPHIC_CACHE, 36), CID (SUBTYPE_TEST_CACHE, 37), CID (LOADING_UNIT, 38), CID (LANGUAGE_ERROR, 41), CID (UNHANDLED_EXCEPTION, 42), CID (INSTANCE, 44), CID (LIBRARY_PREFIX, 45), CID (TYPE_ARGUMENTS, 46), CID (TYPE, 48), CID (FUNCTION_TYPE, 49), CID (RECORD_TYPE, 50), CID (TYPE_PARAMETER, 51), CID (CLOSURE, 56), CID (MINT, 60), CID (DOUBLE, 61), CID (FLOAT32X4, 63), CID (INT32X4, 64), CID (FLOAT64X2, 65), CID (RECORD, 66), CID (TYPED_DATA, 68), CID (EXTERNAL_TYPED_DATA, 69), CID (TYPED_DATA_VIEW, 70), CID (CAPABILITY, 73), CID (RECEIVE_PORT, 74), CID (SEND_PORT, 75), CID (STACK_TRACE, 76), CID (SUSPEND_STATE, 77), CID (REGEXP, 78), CID (WEAK_PROPERTY, 79), CID (WEAK_REFERENCE, 80), CID (FUTURE_OR, 82), CID (USER_TAG, 83), CID (TRANSFERABLE_TYPED_DATA, 84), CID (MAP, 85), CID (CONST_MAP, 86), CID (SET, 87), CID (CONST_SET, 88), CID (ARRAY, 89), CID (IMMUTABLE_ARRAY, 90), CID (GROWABLE_OBJECT_ARRAY, 91), CID (STRING, 92), CID (ONE_BYTE_STRING, 93), CID (TWO_BYTE_STRING, 94), CID (NUM_PREDEFINED_CIDS, 176) }, 113, 169, 4 },
|
||||
{ "3.3.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (WEAK_ARRAY, 17), CID (CODE, 18), CID (OBJECT_POOL, 22), CID (PC_DESCRIPTORS, 23), CID (CODE_SOURCE_MAP, 24), CID (COMPRESSED_STACK_MAPS, 25), CID (EXCEPTION_HANDLERS, 27), CID (CONTEXT, 28), CID (CONTEXT_SCOPE, 29), CID (SENTINEL, 30), CID (SINGLE_TARGET_CACHE, 31), CID (UNLINKED_CALL, 32), CID (MONOMORPHIC_SMIABLE_CALL, 33), CID (CALL_SITE_DATA, 34), CID (ICDATA, 35), CID (MEGAMORPHIC_CACHE, 36), CID (SUBTYPE_TEST_CACHE, 37), CID (LOADING_UNIT, 38), CID (LANGUAGE_ERROR, 41), CID (UNHANDLED_EXCEPTION, 42), CID (INSTANCE, 44), CID (LIBRARY_PREFIX, 45), CID (TYPE_ARGUMENTS, 46), CID (TYPE, 48), CID (FUNCTION_TYPE, 49), CID (RECORD_TYPE, 50), CID (TYPE_PARAMETER, 51), CID (CLOSURE, 56), CID (MINT, 60), CID (DOUBLE, 61), CID (FLOAT32X4, 63), CID (INT32X4, 64), CID (FLOAT64X2, 65), CID (RECORD, 66), CID (TYPED_DATA, 68), CID (EXTERNAL_TYPED_DATA, 69), CID (TYPED_DATA_VIEW, 70), CID (CAPABILITY, 73), CID (RECEIVE_PORT, 74), CID (SEND_PORT, 75), CID (STACK_TRACE, 76), CID (SUSPEND_STATE, 77), CID (REGEXP, 78), CID (WEAK_PROPERTY, 79), CID (WEAK_REFERENCE, 80), CID (FUTURE_OR, 82), CID (USER_TAG, 83), CID (TRANSFERABLE_TYPED_DATA, 84), CID (MAP, 85), CID (CONST_MAP, 86), CID (SET, 87), CID (CONST_SET, 88), CID (ARRAY, 89), CID (IMMUTABLE_ARRAY, 90), CID (GROWABLE_OBJECT_ARRAY, 91), CID (STRING, 92), CID (ONE_BYTE_STRING, 93), CID (TWO_BYTE_STRING, 94), CID (NUM_PREDEFINED_CIDS, 176) }, 113, 169, 4 },
|
||||
{ "3.4.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (WEAK_ARRAY, 17), CID (CODE, 18), CID (OBJECT_POOL, 22), CID (PC_DESCRIPTORS, 23), CID (CODE_SOURCE_MAP, 24), CID (COMPRESSED_STACK_MAPS, 25), CID (EXCEPTION_HANDLERS, 27), CID (CONTEXT, 28), CID (CONTEXT_SCOPE, 29), CID (SENTINEL, 30), CID (SINGLE_TARGET_CACHE, 31), CID (UNLINKED_CALL, 32), CID (MONOMORPHIC_SMIABLE_CALL, 33), CID (CALL_SITE_DATA, 34), CID (ICDATA, 35), CID (MEGAMORPHIC_CACHE, 36), CID (SUBTYPE_TEST_CACHE, 37), CID (LOADING_UNIT, 38), CID (LANGUAGE_ERROR, 41), CID (UNHANDLED_EXCEPTION, 42), CID (INSTANCE, 44), CID (LIBRARY_PREFIX, 45), CID (TYPE_ARGUMENTS, 46), CID (TYPE, 48), CID (FUNCTION_TYPE, 49), CID (RECORD_TYPE, 50), CID (TYPE_PARAMETER, 51), CID (CLOSURE, 56), CID (MINT, 60), CID (DOUBLE, 61), CID (FLOAT32X4, 63), CID (INT32X4, 64), CID (FLOAT64X2, 65), CID (RECORD, 66), CID (TYPED_DATA, 68), CID (EXTERNAL_TYPED_DATA, 69), CID (TYPED_DATA_VIEW, 70), CID (CAPABILITY, 73), CID (RECEIVE_PORT, 74), CID (SEND_PORT, 75), CID (STACK_TRACE, 76), CID (SUSPEND_STATE, 77), CID (REGEXP, 78), CID (WEAK_PROPERTY, 79), CID (WEAK_REFERENCE, 80), CID (FUTURE_OR, 82), CID (USER_TAG, 83), CID (TRANSFERABLE_TYPED_DATA, 84), CID (MAP, 85), CID (CONST_MAP, 86), CID (SET, 87), CID (CONST_SET, 88), CID (ARRAY, 89), CID (IMMUTABLE_ARRAY, 90), CID (GROWABLE_OBJECT_ARRAY, 91), CID (STRING, 92), CID (ONE_BYTE_STRING, 93), CID (TWO_BYTE_STRING, 94), CID (NUM_PREDEFINED_CIDS, 174) }, 111, 167, 4 },
|
||||
{ "3.5.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (WEAK_ARRAY, 17), CID (CODE, 18), CID (OBJECT_POOL, 22), CID (PC_DESCRIPTORS, 23), CID (CODE_SOURCE_MAP, 24), CID (COMPRESSED_STACK_MAPS, 25), CID (EXCEPTION_HANDLERS, 27), CID (CONTEXT, 28), CID (CONTEXT_SCOPE, 29), CID (SENTINEL, 30), CID (SINGLE_TARGET_CACHE, 31), CID (UNLINKED_CALL, 32), CID (MONOMORPHIC_SMIABLE_CALL, 33), CID (CALL_SITE_DATA, 34), CID (ICDATA, 35), CID (MEGAMORPHIC_CACHE, 36), CID (SUBTYPE_TEST_CACHE, 37), CID (LOADING_UNIT, 38), CID (LANGUAGE_ERROR, 41), CID (UNHANDLED_EXCEPTION, 42), CID (INSTANCE, 44), CID (LIBRARY_PREFIX, 45), CID (TYPE_ARGUMENTS, 46), CID (TYPE, 48), CID (FUNCTION_TYPE, 49), CID (RECORD_TYPE, 50), CID (TYPE_PARAMETER, 51), CID (CLOSURE, 56), CID (MINT, 60), CID (DOUBLE, 61), CID (FLOAT32X4, 63), CID (INT32X4, 64), CID (FLOAT64X2, 65), CID (RECORD, 66), CID (TYPED_DATA, 68), CID (EXTERNAL_TYPED_DATA, 69), CID (TYPED_DATA_VIEW, 70), CID (CAPABILITY, 73), CID (RECEIVE_PORT, 74), CID (SEND_PORT, 75), CID (STACK_TRACE, 76), CID (SUSPEND_STATE, 77), CID (REGEXP, 78), CID (WEAK_PROPERTY, 79), CID (WEAK_REFERENCE, 80), CID (FUTURE_OR, 82), CID (USER_TAG, 83), CID (TRANSFERABLE_TYPED_DATA, 84), CID (MAP, 85), CID (CONST_MAP, 86), CID (SET, 87), CID (CONST_SET, 88), CID (ARRAY, 89), CID (IMMUTABLE_ARRAY, 90), CID (GROWABLE_OBJECT_ARRAY, 91), CID (STRING, 92), CID (ONE_BYTE_STRING, 93), CID (TWO_BYTE_STRING, 94), CID (NUM_PREDEFINED_CIDS, 174) }, 111, 167, 4 },
|
||||
{ "3.6.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (WEAK_ARRAY, 17), CID (CODE, 18), CID (OBJECT_POOL, 23), CID (PC_DESCRIPTORS, 24), CID (CODE_SOURCE_MAP, 25), CID (COMPRESSED_STACK_MAPS, 26), CID (EXCEPTION_HANDLERS, 28), CID (CONTEXT, 29), CID (CONTEXT_SCOPE, 30), CID (SENTINEL, 31), CID (SINGLE_TARGET_CACHE, 32), CID (UNLINKED_CALL, 33), CID (MONOMORPHIC_SMIABLE_CALL, 34), CID (CALL_SITE_DATA, 35), CID (ICDATA, 36), CID (MEGAMORPHIC_CACHE, 37), CID (SUBTYPE_TEST_CACHE, 38), CID (LOADING_UNIT, 39), CID (LANGUAGE_ERROR, 42), CID (UNHANDLED_EXCEPTION, 43), CID (INSTANCE, 45), CID (LIBRARY_PREFIX, 46), CID (TYPE_ARGUMENTS, 47), CID (TYPE, 49), CID (FUNCTION_TYPE, 50), CID (RECORD_TYPE, 51), CID (TYPE_PARAMETER, 52), CID (CLOSURE, 57), CID (MINT, 61), CID (DOUBLE, 62), CID (FLOAT32X4, 64), CID (INT32X4, 65), CID (FLOAT64X2, 66), CID (RECORD, 67), CID (TYPED_DATA, 69), CID (EXTERNAL_TYPED_DATA, 70), CID (TYPED_DATA_VIEW, 71), CID (CAPABILITY, 74), CID (RECEIVE_PORT, 75), CID (SEND_PORT, 76), CID (STACK_TRACE, 77), CID (SUSPEND_STATE, 78), CID (REGEXP, 79), CID (WEAK_PROPERTY, 80), CID (WEAK_REFERENCE, 81), CID (FUTURE_OR, 83), CID (USER_TAG, 84), CID (TRANSFERABLE_TYPED_DATA, 85), CID (MAP, 86), CID (CONST_MAP, 87), CID (SET, 88), CID (CONST_SET, 89), CID (ARRAY, 90), CID (IMMUTABLE_ARRAY, 91), CID (GROWABLE_OBJECT_ARRAY, 92), CID (STRING, 93), CID (ONE_BYTE_STRING, 94), CID (TWO_BYTE_STRING, 95), CID (NUM_PREDEFINED_CIDS, 175) }, 112, 168, 4 },
|
||||
{ "3.9.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (WEAK_ARRAY, 17), CID (CODE, 18), CID (OBJECT_POOL, 23), CID (PC_DESCRIPTORS, 24), CID (CODE_SOURCE_MAP, 25), CID (COMPRESSED_STACK_MAPS, 26), CID (EXCEPTION_HANDLERS, 28), CID (CONTEXT, 29), CID (CONTEXT_SCOPE, 30), CID (SENTINEL, 31), CID (SINGLE_TARGET_CACHE, 32), CID (UNLINKED_CALL, 35), CID (MONOMORPHIC_SMIABLE_CALL, 33), CID (CALL_SITE_DATA, 34), CID (ICDATA, 36), CID (MEGAMORPHIC_CACHE, 37), CID (SUBTYPE_TEST_CACHE, 38), CID (LOADING_UNIT, 39), CID (LANGUAGE_ERROR, 42), CID (UNHANDLED_EXCEPTION, 43), CID (INSTANCE, 45), CID (LIBRARY_PREFIX, 46), CID (TYPE_ARGUMENTS, 47), CID (TYPE, 49), CID (FUNCTION_TYPE, 50), CID (RECORD_TYPE, 51), CID (TYPE_PARAMETER, 52), CID (CLOSURE, 57), CID (MINT, 61), CID (DOUBLE, 62), CID (FLOAT32X4, 64), CID (INT32X4, 65), CID (FLOAT64X2, 66), CID (RECORD, 67), CID (TYPED_DATA, 69), CID (EXTERNAL_TYPED_DATA, 70), CID (TYPED_DATA_VIEW, 71), CID (CAPABILITY, 74), CID (RECEIVE_PORT, 75), CID (SEND_PORT, 76), CID (STACK_TRACE, 77), CID (SUSPEND_STATE, 78), CID (REGEXP, 79), CID (WEAK_PROPERTY, 80), CID (WEAK_REFERENCE, 81), CID (FUTURE_OR, 83), CID (USER_TAG, 84), CID (TRANSFERABLE_TYPED_DATA, 85), CID (MAP, 86), CID (CONST_MAP, 87), CID (SET, 88), CID (CONST_SET, 89), CID (ARRAY, 90), CID (IMMUTABLE_ARRAY, 91), CID (GROWABLE_OBJECT_ARRAY, 92), CID (STRING, 93), CID (ONE_BYTE_STRING, 94), CID (TWO_BYTE_STRING, 95), CID (NUM_PREDEFINED_CIDS, 175) }, 112, 168, 4 },
|
||||
{ "2.10.0", { CID (ILLEGAL, 0), CID (CLASS, 4), CID (PATCH_CLASS, 5), CID (FUNCTION, 6), CID (CLOSURE_DATA, 7), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 77), CID (CODE, 16), CID (OBJECT_POOL, 20), CID (PC_DESCRIPTORS, 21), CID (CODE_SOURCE_MAP, 22), CID (COMPRESSED_STACK_MAPS, 23), CID (LOCAL_VAR_DESCRIPTORS, 24), CID (EXCEPTION_HANDLERS, 25), CID (CONTEXT, 26), CID (CONTEXT_SCOPE, 27), CID (SINGLE_TARGET_CACHE, 29), CID (UNLINKED_CALL, 30), CID (MONOMORPHIC_SMIABLE_CALL, 31), CID (CALL_SITE_DATA, 32), CID (ICDATA, 33), CID (MEGAMORPHIC_CACHE, 34), CID (SUBTYPE_TEST_CACHE, 35), CID (LOADING_UNIT, 36), CID (API_ERROR, 38), CID (LANGUAGE_ERROR, 39), CID (UNHANDLED_EXCEPTION, 40), CID (UNWIND_ERROR, 41), CID (INSTANCE, 42), CID (LIBRARY_PREFIX, 43), CID (TYPE_ARGUMENTS, 44), CID (TYPE, 46), CID (TYPE_REF, 47), CID (TYPE_PARAMETER, 48), CID (CLOSURE, 49), CID (MINT, 53), CID (DOUBLE, 54), CID (FLOAT32X4, 57), CID (INT32X4, 58), CID (FLOAT64X2, 59), CID (TYPED_DATA, 61), CID (EXTERNAL_TYPED_DATA, 62), CID (TYPED_DATA_VIEW, 63), CID (CAPABILITY, 66), CID (RECEIVE_PORT, 67), CID (SEND_PORT, 68), CID (STACK_TRACE, 69), CID (REGEXP, 70), CID (WEAK_PROPERTY, 71), CID (FUTURE_OR, 74), CID (USER_TAG, 75), CID (TRANSFERABLE_TYPED_DATA, 76), CID (ARRAY, 78), CID (IMMUTABLE_ARRAY, 79), CID (GROWABLE_OBJECT_ARRAY, 56), CID (STRING, 80), CID (ONE_BYTE_STRING, 81), CID (TWO_BYTE_STRING, 82), CID (NUM_PREDEFINED_CIDS, 156) }, 108, 150, 3 },
|
||||
{ "2.12.0", { CID (ILLEGAL, 0), CID (CLASS, 4), CID (PATCH_CLASS, 5), CID (FUNCTION, 6), CID (CLOSURE_DATA, 7), CID (FFI_TRAMPOLINE_DATA, 8), CID (FIELD, 9), CID (SCRIPT, 10), CID (LIBRARY, 11), CID (NAMESPACE, 12), CID (KERNEL_PROGRAM_INFO, 13), CID (WEAK_SERIALIZATION_REFERENCE, 74), CID (CODE, 14), CID (OBJECT_POOL, 17), CID (PC_DESCRIPTORS, 18), CID (CODE_SOURCE_MAP, 19), CID (COMPRESSED_STACK_MAPS, 20), CID (LOCAL_VAR_DESCRIPTORS, 21), CID (EXCEPTION_HANDLERS, 22), CID (CONTEXT, 23), CID (CONTEXT_SCOPE, 24), CID (SINGLE_TARGET_CACHE, 25), CID (UNLINKED_CALL, 26), CID (MONOMORPHIC_SMIABLE_CALL, 27), CID (CALL_SITE_DATA, 28), CID (ICDATA, 29), CID (MEGAMORPHIC_CACHE, 30), CID (SUBTYPE_TEST_CACHE, 31), CID (LOADING_UNIT, 32), CID (API_ERROR, 34), CID (LANGUAGE_ERROR, 35), CID (UNHANDLED_EXCEPTION, 36), CID (UNWIND_ERROR, 37), CID (INSTANCE, 38), CID (LIBRARY_PREFIX, 39), CID (TYPE_ARGUMENTS, 40), CID (TYPE, 42), CID (FUNCTION_TYPE, 43), CID (TYPE_REF, 44), CID (TYPE_PARAMETER, 45), CID (CLOSURE, 46), CID (MINT, 50), CID (DOUBLE, 51), CID (FLOAT32X4, 54), CID (INT32X4, 55), CID (FLOAT64X2, 56), CID (TYPED_DATA, 58), CID (EXTERNAL_TYPED_DATA, 59), CID (TYPED_DATA_VIEW, 60), CID (CAPABILITY, 63), CID (RECEIVE_PORT, 64), CID (SEND_PORT, 65), CID (STACK_TRACE, 66), CID (REGEXP, 67), CID (WEAK_PROPERTY, 68), CID (FUTURE_OR, 71), CID (USER_TAG, 72), CID (TRANSFERABLE_TYPED_DATA, 73), CID (ARRAY, 75), CID (IMMUTABLE_ARRAY, 76), CID (GROWABLE_OBJECT_ARRAY, 53), CID (STRING, 77), CID (ONE_BYTE_STRING, 78), CID (TWO_BYTE_STRING, 79), CID (NUM_PREDEFINED_CIDS, 148) }, 100, 142, 3 },
|
||||
{ "2.13.0", { CID (ILLEGAL, 0), CID (CLASS, 4), CID (PATCH_CLASS, 5), CID (FUNCTION, 6), CID (CLOSURE_DATA, 7), CID (FFI_TRAMPOLINE_DATA, 8), CID (FIELD, 9), CID (SCRIPT, 10), CID (LIBRARY, 11), CID (NAMESPACE, 12), CID (KERNEL_PROGRAM_INFO, 13), CID (WEAK_SERIALIZATION_REFERENCE, 14), CID (CODE, 15), CID (OBJECT_POOL, 18), CID (PC_DESCRIPTORS, 19), CID (CODE_SOURCE_MAP, 20), CID (COMPRESSED_STACK_MAPS, 21), CID (LOCAL_VAR_DESCRIPTORS, 22), CID (EXCEPTION_HANDLERS, 23), CID (CONTEXT, 24), CID (CONTEXT_SCOPE, 25), CID (SINGLE_TARGET_CACHE, 26), CID (UNLINKED_CALL, 27), CID (MONOMORPHIC_SMIABLE_CALL, 28), CID (CALL_SITE_DATA, 29), CID (ICDATA, 30), CID (MEGAMORPHIC_CACHE, 31), CID (SUBTYPE_TEST_CACHE, 32), CID (LOADING_UNIT, 33), CID (API_ERROR, 35), CID (LANGUAGE_ERROR, 36), CID (UNHANDLED_EXCEPTION, 37), CID (UNWIND_ERROR, 38), CID (INSTANCE, 39), CID (LIBRARY_PREFIX, 40), CID (TYPE_ARGUMENTS, 41), CID (TYPE, 43), CID (FUNCTION_TYPE, 44), CID (TYPE_REF, 45), CID (TYPE_PARAMETER, 46), CID (CLOSURE, 47), CID (MINT, 51), CID (DOUBLE, 52), CID (FLOAT32X4, 55), CID (INT32X4, 56), CID (FLOAT64X2, 57), CID (TYPED_DATA, 59), CID (EXTERNAL_TYPED_DATA, 60), CID (TYPED_DATA_VIEW, 61), CID (CAPABILITY, 64), CID (RECEIVE_PORT, 65), CID (SEND_PORT, 66), CID (STACK_TRACE, 67), CID (REGEXP, 68), CID (WEAK_PROPERTY, 69), CID (FUTURE_OR, 72), CID (USER_TAG, 73), CID (TRANSFERABLE_TYPED_DATA, 74), CID (ARRAY, 75), CID (IMMUTABLE_ARRAY, 76), CID (GROWABLE_OBJECT_ARRAY, 54), CID (STRING, 77), CID (ONE_BYTE_STRING, 78), CID (TWO_BYTE_STRING, 79), CID (NUM_PREDEFINED_CIDS, 148) }, 100, 142, 3 },
|
||||
{ "2.14.0", { CID (ILLEGAL, 0), CID (CLASS, 4), CID (PATCH_CLASS, 5), CID (FUNCTION, 6), CID (TYPE_PARAMETERS, 7), CID (CLOSURE_DATA, 8), CID (FFI_TRAMPOLINE_DATA, 9), CID (FIELD, 10), CID (SCRIPT, 11), CID (LIBRARY, 12), CID (NAMESPACE, 13), CID (KERNEL_PROGRAM_INFO, 14), CID (WEAK_SERIALIZATION_REFERENCE, 15), CID (CODE, 16), CID (OBJECT_POOL, 20), CID (PC_DESCRIPTORS, 21), CID (CODE_SOURCE_MAP, 22), CID (COMPRESSED_STACK_MAPS, 23), CID (LOCAL_VAR_DESCRIPTORS, 24), CID (EXCEPTION_HANDLERS, 25), CID (CONTEXT, 26), CID (CONTEXT_SCOPE, 27), CID (SENTINEL, 28), CID (SINGLE_TARGET_CACHE, 29), CID (UNLINKED_CALL, 30), CID (MONOMORPHIC_SMIABLE_CALL, 31), CID (CALL_SITE_DATA, 32), CID (ICDATA, 33), CID (MEGAMORPHIC_CACHE, 34), CID (SUBTYPE_TEST_CACHE, 35), CID (LOADING_UNIT, 36), CID (API_ERROR, 38), CID (LANGUAGE_ERROR, 39), CID (UNHANDLED_EXCEPTION, 40), CID (UNWIND_ERROR, 41), CID (INSTANCE, 42), CID (LIBRARY_PREFIX, 43), CID (TYPE_ARGUMENTS, 44), CID (TYPE, 46), CID (FUNCTION_TYPE, 47), CID (TYPE_REF, 48), CID (TYPE_PARAMETER, 49), CID (CLOSURE, 50), CID (MINT, 54), CID (DOUBLE, 55), CID (FLOAT32X4, 58), CID (INT32X4, 59), CID (FLOAT64X2, 60), CID (TYPED_DATA, 62), CID (EXTERNAL_TYPED_DATA, 63), CID (TYPED_DATA_VIEW, 64), CID (CAPABILITY, 67), CID (RECEIVE_PORT, 68), CID (SEND_PORT, 69), CID (STACK_TRACE, 70), CID (REGEXP, 71), CID (WEAK_PROPERTY, 72), CID (FUTURE_OR, 74), CID (USER_TAG, 75), CID (TRANSFERABLE_TYPED_DATA, 76), CID (ARRAY, 79), CID (IMMUTABLE_ARRAY, 80), CID (GROWABLE_OBJECT_ARRAY, 57), CID (STRING, 81), CID (ONE_BYTE_STRING, 82), CID (TWO_BYTE_STRING, 83), CID (NUM_PREDEFINED_CIDS, 152) }, 104, 146, 3 },
|
||||
{ "2.15.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (CODE, 17), CID (OBJECT_POOL, 21), CID (PC_DESCRIPTORS, 22), CID (CODE_SOURCE_MAP, 23), CID (COMPRESSED_STACK_MAPS, 24), CID (LOCAL_VAR_DESCRIPTORS, 25), CID (EXCEPTION_HANDLERS, 26), CID (CONTEXT, 27), CID (CONTEXT_SCOPE, 28), CID (SENTINEL, 29), CID (SINGLE_TARGET_CACHE, 30), CID (UNLINKED_CALL, 31), CID (MONOMORPHIC_SMIABLE_CALL, 32), CID (CALL_SITE_DATA, 33), CID (ICDATA, 34), CID (MEGAMORPHIC_CACHE, 35), CID (SUBTYPE_TEST_CACHE, 36), CID (LOADING_UNIT, 37), CID (API_ERROR, 39), CID (LANGUAGE_ERROR, 40), CID (UNHANDLED_EXCEPTION, 41), CID (UNWIND_ERROR, 42), CID (INSTANCE, 43), CID (LIBRARY_PREFIX, 44), CID (TYPE_ARGUMENTS, 45), CID (TYPE, 47), CID (FUNCTION_TYPE, 48), CID (TYPE_REF, 49), CID (TYPE_PARAMETER, 50), CID (CLOSURE, 51), CID (MINT, 55), CID (DOUBLE, 56), CID (FLOAT32X4, 58), CID (INT32X4, 59), CID (FLOAT64X2, 60), CID (TYPED_DATA, 62), CID (EXTERNAL_TYPED_DATA, 63), CID (TYPED_DATA_VIEW, 64), CID (CAPABILITY, 67), CID (RECEIVE_PORT, 68), CID (SEND_PORT, 69), CID (STACK_TRACE, 70), CID (REGEXP, 71), CID (WEAK_PROPERTY, 72), CID (FUTURE_OR, 74), CID (USER_TAG, 75), CID (TRANSFERABLE_TYPED_DATA, 76), CID (ARRAY, 81), CID (IMMUTABLE_ARRAY, 82), CID (GROWABLE_OBJECT_ARRAY, 83), CID (STRING, 84), CID (ONE_BYTE_STRING, 85), CID (TWO_BYTE_STRING, 86), CID (NUM_PREDEFINED_CIDS, 154) }, 106, 148, 3 },
|
||||
{ "2.16.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (CODE, 17), CID (OBJECT_POOL, 21), CID (PC_DESCRIPTORS, 22), CID (CODE_SOURCE_MAP, 23), CID (COMPRESSED_STACK_MAPS, 24), CID (LOCAL_VAR_DESCRIPTORS, 25), CID (EXCEPTION_HANDLERS, 26), CID (CONTEXT, 27), CID (CONTEXT_SCOPE, 28), CID (SENTINEL, 29), CID (SINGLE_TARGET_CACHE, 30), CID (UNLINKED_CALL, 31), CID (MONOMORPHIC_SMIABLE_CALL, 32), CID (CALL_SITE_DATA, 33), CID (ICDATA, 34), CID (MEGAMORPHIC_CACHE, 35), CID (SUBTYPE_TEST_CACHE, 36), CID (LOADING_UNIT, 37), CID (API_ERROR, 39), CID (LANGUAGE_ERROR, 40), CID (UNHANDLED_EXCEPTION, 41), CID (UNWIND_ERROR, 42), CID (INSTANCE, 43), CID (LIBRARY_PREFIX, 44), CID (TYPE_ARGUMENTS, 45), CID (TYPE, 47), CID (FUNCTION_TYPE, 48), CID (TYPE_REF, 49), CID (TYPE_PARAMETER, 50), CID (CLOSURE, 51), CID (MINT, 55), CID (DOUBLE, 56), CID (FLOAT32X4, 58), CID (INT32X4, 59), CID (FLOAT64X2, 60), CID (TYPED_DATA, 62), CID (EXTERNAL_TYPED_DATA, 63), CID (TYPED_DATA_VIEW, 64), CID (CAPABILITY, 67), CID (RECEIVE_PORT, 68), CID (SEND_PORT, 69), CID (STACK_TRACE, 70), CID (REGEXP, 71), CID (WEAK_PROPERTY, 72), CID (FUTURE_OR, 74), CID (USER_TAG, 75), CID (TRANSFERABLE_TYPED_DATA, 76), CID (ARRAY, 81), CID (IMMUTABLE_ARRAY, 82), CID (GROWABLE_OBJECT_ARRAY, 83), CID (STRING, 84), CID (ONE_BYTE_STRING, 85), CID (TWO_BYTE_STRING, 86), CID (NUM_PREDEFINED_CIDS, 154) }, 106, 148, 3 },
|
||||
{ "2.17.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (CODE, 17), CID (OBJECT_POOL, 21), CID (PC_DESCRIPTORS, 22), CID (CODE_SOURCE_MAP, 23), CID (COMPRESSED_STACK_MAPS, 24), CID (LOCAL_VAR_DESCRIPTORS, 25), CID (EXCEPTION_HANDLERS, 26), CID (CONTEXT, 27), CID (CONTEXT_SCOPE, 28), CID (SENTINEL, 29), CID (SINGLE_TARGET_CACHE, 30), CID (UNLINKED_CALL, 31), CID (MONOMORPHIC_SMIABLE_CALL, 32), CID (CALL_SITE_DATA, 33), CID (ICDATA, 34), CID (MEGAMORPHIC_CACHE, 35), CID (SUBTYPE_TEST_CACHE, 36), CID (LOADING_UNIT, 37), CID (API_ERROR, 39), CID (LANGUAGE_ERROR, 40), CID (UNHANDLED_EXCEPTION, 41), CID (UNWIND_ERROR, 42), CID (INSTANCE, 43), CID (LIBRARY_PREFIX, 44), CID (TYPE_ARGUMENTS, 45), CID (TYPE, 47), CID (FUNCTION_TYPE, 52), CID (TYPE_REF, 53), CID (TYPE_PARAMETER, 54), CID (CLOSURE, 55), CID (MINT, 59), CID (DOUBLE, 60), CID (FLOAT32X4, 62), CID (INT32X4, 63), CID (FLOAT64X2, 64), CID (TYPED_DATA, 66), CID (EXTERNAL_TYPED_DATA, 67), CID (TYPED_DATA_VIEW, 68), CID (CAPABILITY, 71), CID (RECEIVE_PORT, 72), CID (SEND_PORT, 73), CID (STACK_TRACE, 74), CID (REGEXP, 75), CID (WEAK_PROPERTY, 76), CID (WEAK_REFERENCE, 77), CID (FUTURE_OR, 79), CID (USER_TAG, 80), CID (TRANSFERABLE_TYPED_DATA, 81), CID (ARRAY, 86), CID (IMMUTABLE_ARRAY, 87), CID (GROWABLE_OBJECT_ARRAY, 88), CID (STRING, 89), CID (ONE_BYTE_STRING, 90), CID (TWO_BYTE_STRING, 91), CID (NUM_PREDEFINED_CIDS, 158) }, 110, 152, 3 },
|
||||
{ "2.18.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (CODE, 17), CID (OBJECT_POOL, 21), CID (PC_DESCRIPTORS, 22), CID (CODE_SOURCE_MAP, 23), CID (COMPRESSED_STACK_MAPS, 24), CID (LOCAL_VAR_DESCRIPTORS, 25), CID (EXCEPTION_HANDLERS, 26), CID (CONTEXT, 27), CID (CONTEXT_SCOPE, 28), CID (SENTINEL, 29), CID (SINGLE_TARGET_CACHE, 30), CID (UNLINKED_CALL, 31), CID (MONOMORPHIC_SMIABLE_CALL, 32), CID (CALL_SITE_DATA, 33), CID (ICDATA, 34), CID (MEGAMORPHIC_CACHE, 35), CID (SUBTYPE_TEST_CACHE, 36), CID (LOADING_UNIT, 37), CID (API_ERROR, 39), CID (LANGUAGE_ERROR, 40), CID (UNHANDLED_EXCEPTION, 41), CID (UNWIND_ERROR, 42), CID (INSTANCE, 43), CID (LIBRARY_PREFIX, 44), CID (TYPE_ARGUMENTS, 45), CID (TYPE, 47), CID (FUNCTION_TYPE, 52), CID (TYPE_REF, 53), CID (TYPE_PARAMETER, 54), CID (CLOSURE, 55), CID (MINT, 59), CID (DOUBLE, 60), CID (FLOAT32X4, 62), CID (INT32X4, 63), CID (FLOAT64X2, 64), CID (TYPED_DATA, 66), CID (EXTERNAL_TYPED_DATA, 67), CID (TYPED_DATA_VIEW, 68), CID (CAPABILITY, 71), CID (RECEIVE_PORT, 72), CID (SEND_PORT, 73), CID (STACK_TRACE, 74), CID (SUSPEND_STATE, 75), CID (REGEXP, 76), CID (WEAK_PROPERTY, 77), CID (WEAK_REFERENCE, 78), CID (FUTURE_OR, 80), CID (USER_TAG, 81), CID (TRANSFERABLE_TYPED_DATA, 82), CID (MAP, 83), CID (CONST_MAP, 84), CID (SET, 85), CID (CONST_SET, 86), CID (ARRAY, 87), CID (IMMUTABLE_ARRAY, 88), CID (GROWABLE_OBJECT_ARRAY, 89), CID (STRING, 90), CID (ONE_BYTE_STRING, 91), CID (TWO_BYTE_STRING, 92), CID (NUM_PREDEFINED_CIDS, 159) }, 111, 153, 3 },
|
||||
{ "2.18.2", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (WEAK_ARRAY, 17), CID (CODE, 18), CID (OBJECT_POOL, 23), CID (PC_DESCRIPTORS, 24), CID (CODE_SOURCE_MAP, 25), CID (COMPRESSED_STACK_MAPS, 26), CID (LOCAL_VAR_DESCRIPTORS, 27), CID (EXCEPTION_HANDLERS, 28), CID (CONTEXT, 29), CID (CONTEXT_SCOPE, 30), CID (SENTINEL, 31), CID (SINGLE_TARGET_CACHE, 32), CID (UNLINKED_CALL, 35), CID (MONOMORPHIC_SMIABLE_CALL, 33), CID (CALL_SITE_DATA, 34), CID (ICDATA, 36), CID (MEGAMORPHIC_CACHE, 37), CID (SUBTYPE_TEST_CACHE, 38), CID (LOADING_UNIT, 39), CID (API_ERROR, 41), CID (LANGUAGE_ERROR, 42), CID (UNHANDLED_EXCEPTION, 43), CID (UNWIND_ERROR, 44), CID (INSTANCE, 45), CID (LIBRARY_PREFIX, 46), CID (TYPE_ARGUMENTS, 47), CID (TYPE, 49), CID (FUNCTION_TYPE, 50), CID (RECORD_TYPE, 51), CID (TYPE_PARAMETER, 52), CID (CLOSURE, 57), CID (MINT, 61), CID (DOUBLE, 62), CID (FLOAT32X4, 64), CID (INT32X4, 65), CID (FLOAT64X2, 66), CID (RECORD, 67), CID (TYPED_DATA, 69), CID (EXTERNAL_TYPED_DATA, 70), CID (TYPED_DATA_VIEW, 71), CID (CAPABILITY, 74), CID (RECEIVE_PORT, 75), CID (SEND_PORT, 76), CID (STACK_TRACE, 77), CID (SUSPEND_STATE, 78), CID (REGEXP, 79), CID (WEAK_PROPERTY, 80), CID (WEAK_REFERENCE, 81), CID (FUTURE_OR, 83), CID (USER_TAG, 84), CID (TRANSFERABLE_TYPED_DATA, 85), CID (MAP, 86), CID (CONST_MAP, 87), CID (SET, 88), CID (CONST_SET, 89), CID (ARRAY, 90), CID (IMMUTABLE_ARRAY, 91), CID (GROWABLE_OBJECT_ARRAY, 92), CID (STRING, 93), CID (ONE_BYTE_STRING, 94), CID (TWO_BYTE_STRING, 95), CID (NUM_PREDEFINED_CIDS, 175) }, 112, 168, 4 },
|
||||
{ "2.18.3", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (CODE, 17), CID (OBJECT_POOL, 21), CID (PC_DESCRIPTORS, 22), CID (CODE_SOURCE_MAP, 23), CID (COMPRESSED_STACK_MAPS, 24), CID (LOCAL_VAR_DESCRIPTORS, 25), CID (EXCEPTION_HANDLERS, 26), CID (CONTEXT, 27), CID (CONTEXT_SCOPE, 28), CID (SENTINEL, 29), CID (SINGLE_TARGET_CACHE, 30), CID (UNLINKED_CALL, 31), CID (MONOMORPHIC_SMIABLE_CALL, 32), CID (CALL_SITE_DATA, 33), CID (ICDATA, 34), CID (MEGAMORPHIC_CACHE, 35), CID (SUBTYPE_TEST_CACHE, 36), CID (LOADING_UNIT, 37), CID (API_ERROR, 39), CID (LANGUAGE_ERROR, 40), CID (UNHANDLED_EXCEPTION, 41), CID (UNWIND_ERROR, 42), CID (INSTANCE, 43), CID (LIBRARY_PREFIX, 44), CID (TYPE_ARGUMENTS, 45), CID (TYPE, 47), CID (FUNCTION_TYPE, 52), CID (TYPE_REF, 53), CID (TYPE_PARAMETER, 54), CID (CLOSURE, 55), CID (MINT, 59), CID (DOUBLE, 60), CID (FLOAT32X4, 62), CID (INT32X4, 63), CID (FLOAT64X2, 64), CID (TYPED_DATA, 66), CID (EXTERNAL_TYPED_DATA, 67), CID (TYPED_DATA_VIEW, 68), CID (CAPABILITY, 71), CID (RECEIVE_PORT, 72), CID (SEND_PORT, 73), CID (STACK_TRACE, 74), CID (SUSPEND_STATE, 75), CID (REGEXP, 76), CID (WEAK_PROPERTY, 77), CID (WEAK_REFERENCE, 78), CID (FUTURE_OR, 80), CID (USER_TAG, 81), CID (TRANSFERABLE_TYPED_DATA, 82), CID (MAP, 83), CID (CONST_MAP, 84), CID (SET, 85), CID (CONST_SET, 86), CID (ARRAY, 87), CID (IMMUTABLE_ARRAY, 88), CID (GROWABLE_OBJECT_ARRAY, 89), CID (STRING, 90), CID (ONE_BYTE_STRING, 91), CID (TWO_BYTE_STRING, 92), CID (NUM_PREDEFINED_CIDS, 159) }, 111, 153, 3 },
|
||||
{ "2.19.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (CODE, 17), CID (OBJECT_POOL, 21), CID (PC_DESCRIPTORS, 22), CID (CODE_SOURCE_MAP, 23), CID (COMPRESSED_STACK_MAPS, 24), CID (LOCAL_VAR_DESCRIPTORS, 25), CID (EXCEPTION_HANDLERS, 26), CID (CONTEXT, 27), CID (CONTEXT_SCOPE, 28), CID (SENTINEL, 29), CID (SINGLE_TARGET_CACHE, 30), CID (UNLINKED_CALL, 31), CID (MONOMORPHIC_SMIABLE_CALL, 32), CID (CALL_SITE_DATA, 33), CID (ICDATA, 34), CID (MEGAMORPHIC_CACHE, 35), CID (SUBTYPE_TEST_CACHE, 36), CID (LOADING_UNIT, 37), CID (API_ERROR, 39), CID (LANGUAGE_ERROR, 40), CID (UNHANDLED_EXCEPTION, 41), CID (UNWIND_ERROR, 42), CID (INSTANCE, 43), CID (LIBRARY_PREFIX, 44), CID (TYPE_ARGUMENTS, 45), CID (TYPE, 47), CID (FUNCTION_TYPE, 48), CID (RECORD_TYPE, 49), CID (TYPE_REF, 50), CID (TYPE_PARAMETER, 51), CID (CLOSURE, 56), CID (MINT, 60), CID (DOUBLE, 61), CID (FLOAT32X4, 63), CID (INT32X4, 64), CID (FLOAT64X2, 65), CID (RECORD, 66), CID (TYPED_DATA, 68), CID (EXTERNAL_TYPED_DATA, 69), CID (TYPED_DATA_VIEW, 70), CID (CAPABILITY, 73), CID (RECEIVE_PORT, 74), CID (SEND_PORT, 75), CID (STACK_TRACE, 76), CID (SUSPEND_STATE, 77), CID (REGEXP, 78), CID (WEAK_PROPERTY, 79), CID (WEAK_REFERENCE, 80), CID (FUTURE_OR, 82), CID (USER_TAG, 83), CID (TRANSFERABLE_TYPED_DATA, 84), CID (MAP, 85), CID (CONST_MAP, 86), CID (SET, 87), CID (CONST_SET, 88), CID (ARRAY, 89), CID (IMMUTABLE_ARRAY, 90), CID (GROWABLE_OBJECT_ARRAY, 91), CID (STRING, 92), CID (ONE_BYTE_STRING, 93), CID (TWO_BYTE_STRING, 94), CID (NUM_PREDEFINED_CIDS, 176) }, 113, 169, 4 },
|
||||
{ "3.0.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (WEAK_ARRAY, 17), CID (CODE, 18), CID (OBJECT_POOL, 22), CID (PC_DESCRIPTORS, 23), CID (CODE_SOURCE_MAP, 24), CID (COMPRESSED_STACK_MAPS, 25), CID (LOCAL_VAR_DESCRIPTORS, 26), CID (EXCEPTION_HANDLERS, 27), CID (CONTEXT, 28), CID (CONTEXT_SCOPE, 29), CID (SENTINEL, 30), CID (SINGLE_TARGET_CACHE, 31), CID (UNLINKED_CALL, 32), CID (MONOMORPHIC_SMIABLE_CALL, 33), CID (CALL_SITE_DATA, 34), CID (ICDATA, 35), CID (MEGAMORPHIC_CACHE, 36), CID (SUBTYPE_TEST_CACHE, 37), CID (LOADING_UNIT, 38), CID (API_ERROR, 40), CID (LANGUAGE_ERROR, 41), CID (UNHANDLED_EXCEPTION, 42), CID (UNWIND_ERROR, 43), CID (INSTANCE, 44), CID (LIBRARY_PREFIX, 45), CID (TYPE_ARGUMENTS, 46), CID (TYPE, 48), CID (FUNCTION_TYPE, 49), CID (RECORD_TYPE, 50), CID (TYPE_REF, 51), CID (TYPE_PARAMETER, 52), CID (CLOSURE, 57), CID (MINT, 61), CID (DOUBLE, 62), CID (FLOAT32X4, 64), CID (INT32X4, 65), CID (FLOAT64X2, 66), CID (RECORD, 67), CID (TYPED_DATA, 69), CID (EXTERNAL_TYPED_DATA, 70), CID (TYPED_DATA_VIEW, 71), CID (CAPABILITY, 74), CID (RECEIVE_PORT, 75), CID (SEND_PORT, 76), CID (STACK_TRACE, 77), CID (SUSPEND_STATE, 78), CID (REGEXP, 79), CID (WEAK_PROPERTY, 80), CID (WEAK_REFERENCE, 81), CID (FUTURE_OR, 83), CID (USER_TAG, 84), CID (TRANSFERABLE_TYPED_DATA, 85), CID (MAP, 86), CID (CONST_MAP, 87), CID (SET, 88), CID (CONST_SET, 89), CID (ARRAY, 90), CID (IMMUTABLE_ARRAY, 91), CID (GROWABLE_OBJECT_ARRAY, 92), CID (STRING, 93), CID (ONE_BYTE_STRING, 94), CID (TWO_BYTE_STRING, 95), CID (NUM_PREDEFINED_CIDS, 177) }, 114, 170, 4 },
|
||||
{ "3.1.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (WEAK_ARRAY, 17), CID (CODE, 18), CID (OBJECT_POOL, 22), CID (PC_DESCRIPTORS, 23), CID (CODE_SOURCE_MAP, 24), CID (COMPRESSED_STACK_MAPS, 25), CID (LOCAL_VAR_DESCRIPTORS, 26), CID (EXCEPTION_HANDLERS, 27), CID (CONTEXT, 28), CID (CONTEXT_SCOPE, 29), CID (SENTINEL, 30), CID (SINGLE_TARGET_CACHE, 31), CID (UNLINKED_CALL, 32), CID (MONOMORPHIC_SMIABLE_CALL, 33), CID (CALL_SITE_DATA, 34), CID (ICDATA, 35), CID (MEGAMORPHIC_CACHE, 36), CID (SUBTYPE_TEST_CACHE, 37), CID (LOADING_UNIT, 38), CID (API_ERROR, 40), CID (LANGUAGE_ERROR, 41), CID (UNHANDLED_EXCEPTION, 42), CID (UNWIND_ERROR, 43), CID (INSTANCE, 44), CID (LIBRARY_PREFIX, 45), CID (TYPE_ARGUMENTS, 46), CID (TYPE, 48), CID (FUNCTION_TYPE, 49), CID (RECORD_TYPE, 50), CID (TYPE_PARAMETER, 51), CID (CLOSURE, 56), CID (MINT, 60), CID (DOUBLE, 61), CID (FLOAT32X4, 63), CID (INT32X4, 64), CID (FLOAT64X2, 65), CID (RECORD, 66), CID (TYPED_DATA, 68), CID (EXTERNAL_TYPED_DATA, 69), CID (TYPED_DATA_VIEW, 70), CID (CAPABILITY, 73), CID (RECEIVE_PORT, 74), CID (SEND_PORT, 75), CID (STACK_TRACE, 76), CID (SUSPEND_STATE, 77), CID (REGEXP, 78), CID (WEAK_PROPERTY, 79), CID (WEAK_REFERENCE, 80), CID (FUTURE_OR, 82), CID (USER_TAG, 83), CID (TRANSFERABLE_TYPED_DATA, 84), CID (MAP, 85), CID (CONST_MAP, 86), CID (SET, 87), CID (CONST_SET, 88), CID (ARRAY, 89), CID (IMMUTABLE_ARRAY, 90), CID (GROWABLE_OBJECT_ARRAY, 91), CID (STRING, 92), CID (ONE_BYTE_STRING, 93), CID (TWO_BYTE_STRING, 94), CID (NUM_PREDEFINED_CIDS, 176) }, 113, 169, 4 },
|
||||
{ "3.2.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (WEAK_ARRAY, 17), CID (CODE, 18), CID (OBJECT_POOL, 22), CID (PC_DESCRIPTORS, 23), CID (CODE_SOURCE_MAP, 24), CID (COMPRESSED_STACK_MAPS, 25), CID (LOCAL_VAR_DESCRIPTORS, 26), CID (EXCEPTION_HANDLERS, 27), CID (CONTEXT, 28), CID (CONTEXT_SCOPE, 29), CID (SENTINEL, 30), CID (SINGLE_TARGET_CACHE, 31), CID (UNLINKED_CALL, 32), CID (MONOMORPHIC_SMIABLE_CALL, 33), CID (CALL_SITE_DATA, 34), CID (ICDATA, 35), CID (MEGAMORPHIC_CACHE, 36), CID (SUBTYPE_TEST_CACHE, 37), CID (LOADING_UNIT, 38), CID (API_ERROR, 40), CID (LANGUAGE_ERROR, 41), CID (UNHANDLED_EXCEPTION, 42), CID (UNWIND_ERROR, 43), CID (INSTANCE, 44), CID (LIBRARY_PREFIX, 45), CID (TYPE_ARGUMENTS, 46), CID (TYPE, 48), CID (FUNCTION_TYPE, 49), CID (RECORD_TYPE, 50), CID (TYPE_PARAMETER, 51), CID (CLOSURE, 56), CID (MINT, 60), CID (DOUBLE, 61), CID (FLOAT32X4, 63), CID (INT32X4, 64), CID (FLOAT64X2, 65), CID (RECORD, 66), CID (TYPED_DATA, 68), CID (EXTERNAL_TYPED_DATA, 69), CID (TYPED_DATA_VIEW, 70), CID (CAPABILITY, 73), CID (RECEIVE_PORT, 74), CID (SEND_PORT, 75), CID (STACK_TRACE, 76), CID (SUSPEND_STATE, 77), CID (REGEXP, 78), CID (WEAK_PROPERTY, 79), CID (WEAK_REFERENCE, 80), CID (FUTURE_OR, 82), CID (USER_TAG, 83), CID (TRANSFERABLE_TYPED_DATA, 84), CID (MAP, 85), CID (CONST_MAP, 86), CID (SET, 87), CID (CONST_SET, 88), CID (ARRAY, 89), CID (IMMUTABLE_ARRAY, 90), CID (GROWABLE_OBJECT_ARRAY, 91), CID (STRING, 92), CID (ONE_BYTE_STRING, 93), CID (TWO_BYTE_STRING, 94), CID (NUM_PREDEFINED_CIDS, 176) }, 113, 169, 4 },
|
||||
{ "3.3.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (WEAK_ARRAY, 17), CID (CODE, 18), CID (OBJECT_POOL, 22), CID (PC_DESCRIPTORS, 23), CID (CODE_SOURCE_MAP, 24), CID (COMPRESSED_STACK_MAPS, 25), CID (LOCAL_VAR_DESCRIPTORS, 26), CID (EXCEPTION_HANDLERS, 27), CID (CONTEXT, 28), CID (CONTEXT_SCOPE, 29), CID (SENTINEL, 30), CID (SINGLE_TARGET_CACHE, 31), CID (UNLINKED_CALL, 32), CID (MONOMORPHIC_SMIABLE_CALL, 33), CID (CALL_SITE_DATA, 34), CID (ICDATA, 35), CID (MEGAMORPHIC_CACHE, 36), CID (SUBTYPE_TEST_CACHE, 37), CID (LOADING_UNIT, 38), CID (API_ERROR, 40), CID (LANGUAGE_ERROR, 41), CID (UNHANDLED_EXCEPTION, 42), CID (UNWIND_ERROR, 43), CID (INSTANCE, 44), CID (LIBRARY_PREFIX, 45), CID (TYPE_ARGUMENTS, 46), CID (TYPE, 48), CID (FUNCTION_TYPE, 49), CID (RECORD_TYPE, 50), CID (TYPE_PARAMETER, 51), CID (CLOSURE, 56), CID (MINT, 60), CID (DOUBLE, 61), CID (FLOAT32X4, 63), CID (INT32X4, 64), CID (FLOAT64X2, 65), CID (RECORD, 66), CID (TYPED_DATA, 68), CID (EXTERNAL_TYPED_DATA, 69), CID (TYPED_DATA_VIEW, 70), CID (CAPABILITY, 73), CID (RECEIVE_PORT, 74), CID (SEND_PORT, 75), CID (STACK_TRACE, 76), CID (SUSPEND_STATE, 77), CID (REGEXP, 78), CID (WEAK_PROPERTY, 79), CID (WEAK_REFERENCE, 80), CID (FUTURE_OR, 82), CID (USER_TAG, 83), CID (TRANSFERABLE_TYPED_DATA, 84), CID (MAP, 85), CID (CONST_MAP, 86), CID (SET, 87), CID (CONST_SET, 88), CID (ARRAY, 89), CID (IMMUTABLE_ARRAY, 90), CID (GROWABLE_OBJECT_ARRAY, 91), CID (STRING, 92), CID (ONE_BYTE_STRING, 93), CID (TWO_BYTE_STRING, 94), CID (NUM_PREDEFINED_CIDS, 176) }, 113, 169, 4 },
|
||||
{ "3.4.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (WEAK_ARRAY, 17), CID (CODE, 18), CID (OBJECT_POOL, 22), CID (PC_DESCRIPTORS, 23), CID (CODE_SOURCE_MAP, 24), CID (COMPRESSED_STACK_MAPS, 25), CID (LOCAL_VAR_DESCRIPTORS, 26), CID (EXCEPTION_HANDLERS, 27), CID (CONTEXT, 28), CID (CONTEXT_SCOPE, 29), CID (SENTINEL, 30), CID (SINGLE_TARGET_CACHE, 31), CID (UNLINKED_CALL, 32), CID (MONOMORPHIC_SMIABLE_CALL, 33), CID (CALL_SITE_DATA, 34), CID (ICDATA, 35), CID (MEGAMORPHIC_CACHE, 36), CID (SUBTYPE_TEST_CACHE, 37), CID (LOADING_UNIT, 38), CID (API_ERROR, 40), CID (LANGUAGE_ERROR, 41), CID (UNHANDLED_EXCEPTION, 42), CID (UNWIND_ERROR, 43), CID (INSTANCE, 44), CID (LIBRARY_PREFIX, 45), CID (TYPE_ARGUMENTS, 46), CID (TYPE, 48), CID (FUNCTION_TYPE, 49), CID (RECORD_TYPE, 50), CID (TYPE_PARAMETER, 51), CID (CLOSURE, 56), CID (MINT, 60), CID (DOUBLE, 61), CID (FLOAT32X4, 63), CID (INT32X4, 64), CID (FLOAT64X2, 65), CID (RECORD, 66), CID (TYPED_DATA, 68), CID (EXTERNAL_TYPED_DATA, 69), CID (TYPED_DATA_VIEW, 70), CID (CAPABILITY, 73), CID (RECEIVE_PORT, 74), CID (SEND_PORT, 75), CID (STACK_TRACE, 76), CID (SUSPEND_STATE, 77), CID (REGEXP, 78), CID (WEAK_PROPERTY, 79), CID (WEAK_REFERENCE, 80), CID (FUTURE_OR, 82), CID (USER_TAG, 83), CID (TRANSFERABLE_TYPED_DATA, 84), CID (MAP, 85), CID (CONST_MAP, 86), CID (SET, 87), CID (CONST_SET, 88), CID (ARRAY, 89), CID (IMMUTABLE_ARRAY, 90), CID (GROWABLE_OBJECT_ARRAY, 91), CID (STRING, 92), CID (ONE_BYTE_STRING, 93), CID (TWO_BYTE_STRING, 94), CID (NUM_PREDEFINED_CIDS, 174) }, 111, 167, 4 },
|
||||
{ "3.5.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (WEAK_ARRAY, 17), CID (CODE, 18), CID (OBJECT_POOL, 22), CID (PC_DESCRIPTORS, 23), CID (CODE_SOURCE_MAP, 24), CID (COMPRESSED_STACK_MAPS, 25), CID (LOCAL_VAR_DESCRIPTORS, 26), CID (EXCEPTION_HANDLERS, 27), CID (CONTEXT, 28), CID (CONTEXT_SCOPE, 29), CID (SENTINEL, 30), CID (SINGLE_TARGET_CACHE, 31), CID (UNLINKED_CALL, 32), CID (MONOMORPHIC_SMIABLE_CALL, 33), CID (CALL_SITE_DATA, 34), CID (ICDATA, 35), CID (MEGAMORPHIC_CACHE, 36), CID (SUBTYPE_TEST_CACHE, 37), CID (LOADING_UNIT, 38), CID (API_ERROR, 40), CID (LANGUAGE_ERROR, 41), CID (UNHANDLED_EXCEPTION, 42), CID (UNWIND_ERROR, 43), CID (INSTANCE, 44), CID (LIBRARY_PREFIX, 45), CID (TYPE_ARGUMENTS, 46), CID (TYPE, 48), CID (FUNCTION_TYPE, 49), CID (RECORD_TYPE, 50), CID (TYPE_PARAMETER, 51), CID (CLOSURE, 56), CID (MINT, 60), CID (DOUBLE, 61), CID (FLOAT32X4, 63), CID (INT32X4, 64), CID (FLOAT64X2, 65), CID (RECORD, 66), CID (TYPED_DATA, 68), CID (EXTERNAL_TYPED_DATA, 69), CID (TYPED_DATA_VIEW, 70), CID (CAPABILITY, 73), CID (RECEIVE_PORT, 74), CID (SEND_PORT, 75), CID (STACK_TRACE, 76), CID (SUSPEND_STATE, 77), CID (REGEXP, 78), CID (WEAK_PROPERTY, 79), CID (WEAK_REFERENCE, 80), CID (FUTURE_OR, 82), CID (USER_TAG, 83), CID (TRANSFERABLE_TYPED_DATA, 84), CID (MAP, 85), CID (CONST_MAP, 86), CID (SET, 87), CID (CONST_SET, 88), CID (ARRAY, 89), CID (IMMUTABLE_ARRAY, 90), CID (GROWABLE_OBJECT_ARRAY, 91), CID (STRING, 92), CID (ONE_BYTE_STRING, 93), CID (TWO_BYTE_STRING, 94), CID (NUM_PREDEFINED_CIDS, 174) }, 111, 167, 4 },
|
||||
{ "3.6.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (WEAK_ARRAY, 17), CID (CODE, 18), CID (OBJECT_POOL, 23), CID (PC_DESCRIPTORS, 24), CID (CODE_SOURCE_MAP, 25), CID (COMPRESSED_STACK_MAPS, 26), CID (LOCAL_VAR_DESCRIPTORS, 27), CID (EXCEPTION_HANDLERS, 28), CID (CONTEXT, 29), CID (CONTEXT_SCOPE, 30), CID (SENTINEL, 31), CID (SINGLE_TARGET_CACHE, 32), CID (UNLINKED_CALL, 33), CID (MONOMORPHIC_SMIABLE_CALL, 34), CID (CALL_SITE_DATA, 35), CID (ICDATA, 36), CID (MEGAMORPHIC_CACHE, 37), CID (SUBTYPE_TEST_CACHE, 38), CID (LOADING_UNIT, 39), CID (API_ERROR, 41), CID (LANGUAGE_ERROR, 42), CID (UNHANDLED_EXCEPTION, 43), CID (UNWIND_ERROR, 44), CID (INSTANCE, 45), CID (LIBRARY_PREFIX, 46), CID (TYPE_ARGUMENTS, 47), CID (TYPE, 49), CID (FUNCTION_TYPE, 50), CID (RECORD_TYPE, 51), CID (TYPE_PARAMETER, 52), CID (CLOSURE, 57), CID (MINT, 61), CID (DOUBLE, 62), CID (FLOAT32X4, 64), CID (INT32X4, 65), CID (FLOAT64X2, 66), CID (RECORD, 67), CID (TYPED_DATA, 69), CID (EXTERNAL_TYPED_DATA, 70), CID (TYPED_DATA_VIEW, 71), CID (CAPABILITY, 74), CID (RECEIVE_PORT, 75), CID (SEND_PORT, 76), CID (STACK_TRACE, 77), CID (SUSPEND_STATE, 78), CID (REGEXP, 79), CID (WEAK_PROPERTY, 80), CID (WEAK_REFERENCE, 81), CID (FUTURE_OR, 83), CID (USER_TAG, 84), CID (TRANSFERABLE_TYPED_DATA, 85), CID (MAP, 86), CID (CONST_MAP, 87), CID (SET, 88), CID (CONST_SET, 89), CID (ARRAY, 90), CID (IMMUTABLE_ARRAY, 91), CID (GROWABLE_OBJECT_ARRAY, 92), CID (STRING, 93), CID (ONE_BYTE_STRING, 94), CID (TWO_BYTE_STRING, 95), CID (NUM_PREDEFINED_CIDS, 175) }, 112, 168, 4 },
|
||||
{ "3.7.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (WEAK_ARRAY, 17), CID (CODE, 18), CID (OBJECT_POOL, 23), CID (PC_DESCRIPTORS, 24), CID (CODE_SOURCE_MAP, 25), CID (COMPRESSED_STACK_MAPS, 26), CID (LOCAL_VAR_DESCRIPTORS, 27), CID (EXCEPTION_HANDLERS, 28), CID (CONTEXT, 29), CID (CONTEXT_SCOPE, 30), CID (SENTINEL, 31), CID (SINGLE_TARGET_CACHE, 32), CID (UNLINKED_CALL, 33), CID (MONOMORPHIC_SMIABLE_CALL, 34), CID (CALL_SITE_DATA, 35), CID (ICDATA, 36), CID (MEGAMORPHIC_CACHE, 37), CID (SUBTYPE_TEST_CACHE, 38), CID (LOADING_UNIT, 39), CID (API_ERROR, 41), CID (LANGUAGE_ERROR, 42), CID (UNHANDLED_EXCEPTION, 43), CID (UNWIND_ERROR, 44), CID (INSTANCE, 45), CID (LIBRARY_PREFIX, 46), CID (TYPE_ARGUMENTS, 47), CID (TYPE, 49), CID (FUNCTION_TYPE, 50), CID (RECORD_TYPE, 51), CID (TYPE_PARAMETER, 52), CID (CLOSURE, 57), CID (MINT, 61), CID (DOUBLE, 62), CID (FLOAT32X4, 64), CID (INT32X4, 65), CID (FLOAT64X2, 66), CID (RECORD, 67), CID (TYPED_DATA, 69), CID (EXTERNAL_TYPED_DATA, 70), CID (TYPED_DATA_VIEW, 71), CID (CAPABILITY, 74), CID (RECEIVE_PORT, 75), CID (SEND_PORT, 76), CID (STACK_TRACE, 77), CID (SUSPEND_STATE, 78), CID (REGEXP, 79), CID (WEAK_PROPERTY, 80), CID (WEAK_REFERENCE, 81), CID (FUTURE_OR, 83), CID (USER_TAG, 84), CID (TRANSFERABLE_TYPED_DATA, 85), CID (MAP, 86), CID (CONST_MAP, 87), CID (SET, 88), CID (CONST_SET, 89), CID (ARRAY, 90), CID (IMMUTABLE_ARRAY, 91), CID (GROWABLE_OBJECT_ARRAY, 92), CID (STRING, 93), CID (ONE_BYTE_STRING, 94), CID (TWO_BYTE_STRING, 95), CID (NUM_PREDEFINED_CIDS, 175) }, 112, 168, 4 },
|
||||
{ "3.8.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (WEAK_ARRAY, 17), CID (CODE, 18), CID (OBJECT_POOL, 23), CID (PC_DESCRIPTORS, 24), CID (CODE_SOURCE_MAP, 25), CID (COMPRESSED_STACK_MAPS, 26), CID (LOCAL_VAR_DESCRIPTORS, 27), CID (EXCEPTION_HANDLERS, 28), CID (CONTEXT, 29), CID (CONTEXT_SCOPE, 30), CID (SENTINEL, 31), CID (SINGLE_TARGET_CACHE, 32), CID (UNLINKED_CALL, 33), CID (MONOMORPHIC_SMIABLE_CALL, 34), CID (CALL_SITE_DATA, 35), CID (ICDATA, 36), CID (MEGAMORPHIC_CACHE, 37), CID (SUBTYPE_TEST_CACHE, 38), CID (LOADING_UNIT, 39), CID (API_ERROR, 41), CID (LANGUAGE_ERROR, 42), CID (UNHANDLED_EXCEPTION, 43), CID (UNWIND_ERROR, 44), CID (INSTANCE, 45), CID (LIBRARY_PREFIX, 46), CID (TYPE_ARGUMENTS, 47), CID (TYPE, 49), CID (FUNCTION_TYPE, 50), CID (RECORD_TYPE, 51), CID (TYPE_PARAMETER, 52), CID (CLOSURE, 57), CID (MINT, 61), CID (DOUBLE, 62), CID (FLOAT32X4, 64), CID (INT32X4, 65), CID (FLOAT64X2, 66), CID (RECORD, 67), CID (TYPED_DATA, 69), CID (EXTERNAL_TYPED_DATA, 70), CID (TYPED_DATA_VIEW, 71), CID (CAPABILITY, 74), CID (RECEIVE_PORT, 75), CID (SEND_PORT, 76), CID (STACK_TRACE, 77), CID (SUSPEND_STATE, 78), CID (REGEXP, 79), CID (WEAK_PROPERTY, 80), CID (WEAK_REFERENCE, 81), CID (FUTURE_OR, 83), CID (USER_TAG, 84), CID (TRANSFERABLE_TYPED_DATA, 85), CID (MAP, 86), CID (CONST_MAP, 87), CID (SET, 88), CID (CONST_SET, 89), CID (ARRAY, 90), CID (IMMUTABLE_ARRAY, 91), CID (GROWABLE_OBJECT_ARRAY, 92), CID (STRING, 93), CID (ONE_BYTE_STRING, 94), CID (TWO_BYTE_STRING, 95), CID (NUM_PREDEFINED_CIDS, 175) }, 112, 168, 4 },
|
||||
{ "3.9.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (WEAK_ARRAY, 17), CID (CODE, 18), CID (OBJECT_POOL, 23), CID (PC_DESCRIPTORS, 24), CID (CODE_SOURCE_MAP, 25), CID (COMPRESSED_STACK_MAPS, 26), CID (LOCAL_VAR_DESCRIPTORS, 27), CID (EXCEPTION_HANDLERS, 28), CID (CONTEXT, 29), CID (CONTEXT_SCOPE, 30), CID (SENTINEL, 31), CID (SINGLE_TARGET_CACHE, 32), CID (UNLINKED_CALL, 35), CID (MONOMORPHIC_SMIABLE_CALL, 33), CID (CALL_SITE_DATA, 34), CID (ICDATA, 36), CID (MEGAMORPHIC_CACHE, 37), CID (SUBTYPE_TEST_CACHE, 38), CID (LOADING_UNIT, 39), CID (API_ERROR, 41), CID (LANGUAGE_ERROR, 42), CID (UNHANDLED_EXCEPTION, 43), CID (UNWIND_ERROR, 44), CID (INSTANCE, 45), CID (LIBRARY_PREFIX, 46), CID (TYPE_ARGUMENTS, 47), CID (TYPE, 49), CID (FUNCTION_TYPE, 50), CID (RECORD_TYPE, 51), CID (TYPE_PARAMETER, 52), CID (CLOSURE, 57), CID (MINT, 61), CID (DOUBLE, 62), CID (FLOAT32X4, 64), CID (INT32X4, 65), CID (FLOAT64X2, 66), CID (RECORD, 67), CID (TYPED_DATA, 69), CID (EXTERNAL_TYPED_DATA, 70), CID (TYPED_DATA_VIEW, 71), CID (CAPABILITY, 74), CID (RECEIVE_PORT, 75), CID (SEND_PORT, 76), CID (STACK_TRACE, 77), CID (SUSPEND_STATE, 78), CID (REGEXP, 79), CID (WEAK_PROPERTY, 80), CID (WEAK_REFERENCE, 81), CID (FUTURE_OR, 83), CID (USER_TAG, 84), CID (TRANSFERABLE_TYPED_DATA, 85), CID (MAP, 86), CID (CONST_MAP, 87), CID (SET, 88), CID (CONST_SET, 89), CID (ARRAY, 90), CID (IMMUTABLE_ARRAY, 91), CID (GROWABLE_OBJECT_ARRAY, 92), CID (STRING, 93), CID (ONE_BYTE_STRING, 94), CID (TWO_BYTE_STRING, 95), CID (NUM_PREDEFINED_CIDS, 175) }, 112, 168, 4 },
|
||||
{ "3.10.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (WEAK_ARRAY, 17), CID (CODE, 18), CID (OBJECT_POOL, 23), CID (PC_DESCRIPTORS, 24), CID (CODE_SOURCE_MAP, 25), CID (COMPRESSED_STACK_MAPS, 26), CID (LOCAL_VAR_DESCRIPTORS, 27), CID (EXCEPTION_HANDLERS, 28), CID (CONTEXT, 29), CID (CONTEXT_SCOPE, 30), CID (SENTINEL, 31), CID (SINGLE_TARGET_CACHE, 32), CID (UNLINKED_CALL, 35), CID (MONOMORPHIC_SMIABLE_CALL, 33), CID (CALL_SITE_DATA, 34), CID (ICDATA, 36), CID (MEGAMORPHIC_CACHE, 37), CID (SUBTYPE_TEST_CACHE, 38), CID (LOADING_UNIT, 39), CID (API_ERROR, 41), CID (LANGUAGE_ERROR, 42), CID (UNHANDLED_EXCEPTION, 43), CID (UNWIND_ERROR, 44), CID (INSTANCE, 45), CID (LIBRARY_PREFIX, 46), CID (TYPE_ARGUMENTS, 47), CID (TYPE, 49), CID (FUNCTION_TYPE, 50), CID (RECORD_TYPE, 51), CID (TYPE_PARAMETER, 52), CID (CLOSURE, 57), CID (MINT, 61), CID (DOUBLE, 62), CID (FLOAT32X4, 64), CID (INT32X4, 65), CID (FLOAT64X2, 66), CID (RECORD, 67), CID (TYPED_DATA, 69), CID (EXTERNAL_TYPED_DATA, 70), CID (TYPED_DATA_VIEW, 71), CID (CAPABILITY, 74), CID (RECEIVE_PORT, 75), CID (SEND_PORT, 76), CID (STACK_TRACE, 77), CID (SUSPEND_STATE, 78), CID (REGEXP, 79), CID (WEAK_PROPERTY, 80), CID (WEAK_REFERENCE, 81), CID (FUTURE_OR, 83), CID (USER_TAG, 84), CID (TRANSFERABLE_TYPED_DATA, 85), CID (MAP, 86), CID (CONST_MAP, 87), CID (SET, 88), CID (CONST_SET, 89), CID (ARRAY, 90), CID (IMMUTABLE_ARRAY, 91), CID (GROWABLE_OBJECT_ARRAY, 92), CID (STRING, 93), CID (ONE_BYTE_STRING, 94), CID (TWO_BYTE_STRING, 95), CID (NUM_PREDEFINED_CIDS, 175) }, 112, 168, 4 },
|
||||
{ "3.11.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (WEAK_ARRAY, 17), CID (CODE, 18), CID (OBJECT_POOL, 23), CID (PC_DESCRIPTORS, 24), CID (CODE_SOURCE_MAP, 25), CID (COMPRESSED_STACK_MAPS, 26), CID (LOCAL_VAR_DESCRIPTORS, 27), CID (EXCEPTION_HANDLERS, 28), CID (CONTEXT, 29), CID (CONTEXT_SCOPE, 30), CID (SENTINEL, 31), CID (SINGLE_TARGET_CACHE, 32), CID (UNLINKED_CALL, 35), CID (MONOMORPHIC_SMIABLE_CALL, 33), CID (CALL_SITE_DATA, 34), CID (ICDATA, 36), CID (MEGAMORPHIC_CACHE, 37), CID (SUBTYPE_TEST_CACHE, 38), CID (LOADING_UNIT, 39), CID (API_ERROR, 41), CID (LANGUAGE_ERROR, 42), CID (UNHANDLED_EXCEPTION, 43), CID (UNWIND_ERROR, 44), CID (INSTANCE, 45), CID (LIBRARY_PREFIX, 46), CID (TYPE_ARGUMENTS, 47), CID (TYPE, 49), CID (FUNCTION_TYPE, 50), CID (RECORD_TYPE, 51), CID (TYPE_PARAMETER, 52), CID (CLOSURE, 57), CID (MINT, 61), CID (DOUBLE, 62), CID (FLOAT32X4, 64), CID (INT32X4, 65), CID (FLOAT64X2, 66), CID (RECORD, 67), CID (TYPED_DATA, 69), CID (EXTERNAL_TYPED_DATA, 70), CID (TYPED_DATA_VIEW, 71), CID (CAPABILITY, 74), CID (RECEIVE_PORT, 75), CID (SEND_PORT, 76), CID (STACK_TRACE, 77), CID (SUSPEND_STATE, 78), CID (REGEXP, 79), CID (WEAK_PROPERTY, 80), CID (WEAK_REFERENCE, 81), CID (FUTURE_OR, 83), CID (USER_TAG, 84), CID (TRANSFERABLE_TYPED_DATA, 85), CID (MAP, 86), CID (CONST_MAP, 87), CID (SET, 88), CID (CONST_SET, 89), CID (ARRAY, 90), CID (IMMUTABLE_ARRAY, 91), CID (GROWABLE_OBJECT_ARRAY, 92), CID (STRING, 93), CID (ONE_BYTE_STRING, 94), CID (TWO_BYTE_STRING, 95), CID (NUM_PREDEFINED_CIDS, 175) }, 112, 168, 4 },
|
||||
{ "3.12.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (WEAK_ARRAY, 17), CID (CODE, 18), CID (OBJECT_POOL, 23), CID (PC_DESCRIPTORS, 24), CID (CODE_SOURCE_MAP, 25), CID (COMPRESSED_STACK_MAPS, 26), CID (LOCAL_VAR_DESCRIPTORS, 27), CID (EXCEPTION_HANDLERS, 28), CID (CONTEXT, 29), CID (CONTEXT_SCOPE, 30), CID (SENTINEL, 31), CID (SINGLE_TARGET_CACHE, 32), CID (UNLINKED_CALL, 35), CID (MONOMORPHIC_SMIABLE_CALL, 33), CID (CALL_SITE_DATA, 34), CID (ICDATA, 36), CID (MEGAMORPHIC_CACHE, 37), CID (SUBTYPE_TEST_CACHE, 38), CID (LOADING_UNIT, 39), CID (API_ERROR, 41), CID (LANGUAGE_ERROR, 42), CID (UNHANDLED_EXCEPTION, 43), CID (UNWIND_ERROR, 44), CID (INSTANCE, 45), CID (LIBRARY_PREFIX, 46), CID (TYPE_ARGUMENTS, 47), CID (TYPE, 49), CID (FUNCTION_TYPE, 50), CID (RECORD_TYPE, 51), CID (TYPE_PARAMETER, 52), CID (CLOSURE, 57), CID (MINT, 61), CID (DOUBLE, 62), CID (FLOAT32X4, 64), CID (INT32X4, 65), CID (FLOAT64X2, 66), CID (RECORD, 67), CID (TYPED_DATA, 69), CID (EXTERNAL_TYPED_DATA, 70), CID (TYPED_DATA_VIEW, 71), CID (CAPABILITY, 74), CID (RECEIVE_PORT, 75), CID (SEND_PORT, 76), CID (STACK_TRACE, 77), CID (SUSPEND_STATE, 78), CID (REGEXP, 79), CID (WEAK_PROPERTY, 80), CID (WEAK_REFERENCE, 81), CID (FUTURE_OR, 83), CID (USER_TAG, 84), CID (TRANSFERABLE_TYPED_DATA, 85), CID (MAP, 86), CID (CONST_MAP, 87), CID (SET, 88), CID (CONST_SET, 89), CID (ARRAY, 90), CID (IMMUTABLE_ARRAY, 91), CID (GROWABLE_OBJECT_ARRAY, 92), CID (STRING, 93), CID (ONE_BYTE_STRING, 94), CID (TWO_BYTE_STRING, 95), CID (NUM_PREDEFINED_CIDS, 175) }, 112, 168, 4 },
|
||||
{ "3.13.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (WEAK_ARRAY, 17), CID (CODE, 18), CID (OBJECT_POOL, 23), CID (PC_DESCRIPTORS, 24), CID (CODE_SOURCE_MAP, 25), CID (COMPRESSED_STACK_MAPS, 26), CID (LOCAL_VAR_DESCRIPTORS, 27), CID (EXCEPTION_HANDLERS, 28), CID (CONTEXT, 29), CID (CONTEXT_SCOPE, 30), CID (SENTINEL, 31), CID (SINGLE_TARGET_CACHE, 32), CID (UNLINKED_CALL, 35), CID (MONOMORPHIC_SMIABLE_CALL, 33), CID (CALL_SITE_DATA, 34), CID (ICDATA, 36), CID (MEGAMORPHIC_CACHE, 37), CID (SUBTYPE_TEST_CACHE, 38), CID (LOADING_UNIT, 39), CID (API_ERROR, 41), CID (LANGUAGE_ERROR, 42), CID (UNHANDLED_EXCEPTION, 43), CID (UNWIND_ERROR, 44), CID (INSTANCE, 45), CID (LIBRARY_PREFIX, 46), CID (TYPE_ARGUMENTS, 47), CID (TYPE, 49), CID (FUNCTION_TYPE, 50), CID (RECORD_TYPE, 51), CID (TYPE_PARAMETER, 52), CID (CLOSURE, 57), CID (MINT, 61), CID (DOUBLE, 62), CID (FLOAT32X4, 64), CID (INT32X4, 65), CID (FLOAT64X2, 66), CID (RECORD, 67), CID (TYPED_DATA, 69), CID (EXTERNAL_TYPED_DATA, 70), CID (TYPED_DATA_VIEW, 71), CID (CAPABILITY, 74), CID (RECEIVE_PORT, 75), CID (SEND_PORT, 76), CID (STACK_TRACE, 77), CID (SUSPEND_STATE, 78), CID (REGEXP, 79), CID (WEAK_PROPERTY, 80), CID (WEAK_REFERENCE, 81), CID (FUTURE_OR, 83), CID (USER_TAG, 84), CID (TRANSFERABLE_TYPED_DATA, 85), CID (MAP, 86), CID (CONST_MAP, 87), CID (SET, 88), CID (CONST_SET, 89), CID (ARRAY, 90), CID (IMMUTABLE_ARRAY, 91), CID (GROWABLE_OBJECT_ARRAY, 92), CID (STRING, 93), CID (ONE_BYTE_STRING, 94), CID (TWO_BYTE_STRING, 95), CID (NUM_PREDEFINED_CIDS, 176) }, 113, 169, 4 },
|
||||
{ "3.14.0", { CID (ILLEGAL, 0), CID (NATIVE_POINTER, 1), CID (CLASS, 5), CID (PATCH_CLASS, 6), CID (FUNCTION, 7), CID (TYPE_PARAMETERS, 8), CID (CLOSURE_DATA, 9), CID (FFI_TRAMPOLINE_DATA, 10), CID (FIELD, 11), CID (SCRIPT, 12), CID (LIBRARY, 13), CID (NAMESPACE, 14), CID (KERNEL_PROGRAM_INFO, 15), CID (WEAK_SERIALIZATION_REFERENCE, 16), CID (WEAK_ARRAY, 17), CID (CODE, 18), CID (OBJECT_POOL, 23), CID (PC_DESCRIPTORS, 24), CID (CODE_SOURCE_MAP, 25), CID (COMPRESSED_STACK_MAPS, 26), CID (LOCAL_VAR_DESCRIPTORS, 27), CID (EXCEPTION_HANDLERS, 28), CID (CONTEXT, 29), CID (CONTEXT_SCOPE, 30), CID (SENTINEL, 31), CID (SINGLE_TARGET_CACHE, 32), CID (UNLINKED_CALL, 35), CID (MONOMORPHIC_SMIABLE_CALL, 33), CID (CALL_SITE_DATA, 34), CID (ICDATA, 36), CID (MEGAMORPHIC_CACHE, 37), CID (SUBTYPE_TEST_CACHE, 38), CID (LOADING_UNIT, 39), CID (API_ERROR, 41), CID (LANGUAGE_ERROR, 42), CID (UNHANDLED_EXCEPTION, 43), CID (UNWIND_ERROR, 44), CID (INSTANCE, 45), CID (LIBRARY_PREFIX, 46), CID (TYPE_ARGUMENTS, 47), CID (TYPE, 49), CID (FUNCTION_TYPE, 50), CID (RECORD_TYPE, 51), CID (TYPE_PARAMETER, 52), CID (CLOSURE, 57), CID (MINT, 61), CID (DOUBLE, 62), CID (FLOAT32X4, 64), CID (INT32X4, 65), CID (FLOAT64X2, 66), CID (RECORD, 67), CID (TYPED_DATA, 69), CID (EXTERNAL_TYPED_DATA, 70), CID (TYPED_DATA_VIEW, 71), CID (CAPABILITY, 74), CID (RECEIVE_PORT, 75), CID (SEND_PORT, 76), CID (STACK_TRACE, 77), CID (SUSPEND_STATE, 78), CID (REGEXP, 79), CID (WEAK_PROPERTY, 80), CID (WEAK_REFERENCE, 81), CID (FUTURE_OR, 83), CID (USER_TAG, 84), CID (TRANSFERABLE_TYPED_DATA, 85), CID (MAP, 86), CID (CONST_MAP, 87), CID (SET, 88), CID (CONST_SET, 89), CID (ARRAY, 90), CID (IMMUTABLE_ARRAY, 91), CID (GROWABLE_OBJECT_ARRAY, 92), CID (STRING, 93), CID (ONE_BYTE_STRING, 94), CID (TWO_BYTE_STRING, 95), CID (NUM_PREDEFINED_CIDS, 176) }, 113, 169, 4 },
|
||||
{ NULL, { 0 }, 0, 0, 0 }
|
||||
// clang-format on
|
||||
};
|
||||
@@ -60,6 +68,7 @@ static const char *const cid_kind_names[DART_CID_KIND_COUNT] = {
|
||||
[DART_CID_PC_DESCRIPTORS] = "PcDescriptors",
|
||||
[DART_CID_CODE_SOURCE_MAP] = "CodeSourceMap",
|
||||
[DART_CID_COMPRESSED_STACK_MAPS] = "CompressedStackMaps",
|
||||
[DART_CID_LOCAL_VAR_DESCRIPTORS] = "LocalVarDescriptors",
|
||||
[DART_CID_EXCEPTION_HANDLERS] = "ExceptionHandlers",
|
||||
[DART_CID_CONTEXT] = "Context",
|
||||
[DART_CID_CONTEXT_SCOPE] = "ContextScope",
|
||||
@@ -72,8 +81,10 @@ static const char *const cid_kind_names[DART_CID_KIND_COUNT] = {
|
||||
[DART_CID_MEGAMORPHIC_CACHE] = "MegamorphicCache",
|
||||
[DART_CID_SUBTYPE_TEST_CACHE] = "SubtypeTestCache",
|
||||
[DART_CID_LOADING_UNIT] = "LoadingUnit",
|
||||
[DART_CID_API_ERROR] = "ApiError",
|
||||
[DART_CID_LANGUAGE_ERROR] = "LanguageError",
|
||||
[DART_CID_UNHANDLED_EXCEPTION] = "UnhandledException",
|
||||
[DART_CID_UNWIND_ERROR] = "UnwindError",
|
||||
[DART_CID_INSTANCE] = "Instance",
|
||||
[DART_CID_LIBRARY_PREFIX] = "LibraryPrefix",
|
||||
[DART_CID_TYPE_ARGUMENTS] = "TypeArguments",
|
||||
@@ -135,11 +146,31 @@ static const DartCidTable *cid_table_for_version(const char *version) {
|
||||
}
|
||||
|
||||
static const DartCidTable *cid_table_for_layout(const DartVerLayout *layout) {
|
||||
const DartCidTable *table = cid_table_for_version (layout? layout->dart_version: NULL);
|
||||
if (table) {
|
||||
return table;
|
||||
// One-entry memo. dart_cid_get is called per decoded object on some paths,
|
||||
// and resolving the table means comparing version strings against every row.
|
||||
// Keyed on a copy of the version text rather than the layout pointer: owned
|
||||
// layouts are freed, and a later one allocated at the same address would
|
||||
// otherwise inherit the previous snapshot's table.
|
||||
static char memo_version[32] = { 0 };
|
||||
static const DartCidTable *memo_table = NULL;
|
||||
const char *version = layout? layout->dart_version: NULL;
|
||||
if (version && memo_table && !strcmp (version, memo_version)) {
|
||||
return memo_table;
|
||||
}
|
||||
return cid_table_for_version ("3.9.2");
|
||||
const DartCidTable *table = cid_table_for_version (version);
|
||||
if (!table) {
|
||||
// Fall back to the same profile the version fingerprinter picks for an
|
||||
// unknown hash, so a layout it guessed cannot then be decoded with CIDs
|
||||
// from a different release. Identical tables today; they diverge the
|
||||
// moment a release shifts the numbering.
|
||||
const DartVerLayout *newest = dart_newest_profile ();
|
||||
table = cid_table_for_version (newest? newest->dart_version: NULL);
|
||||
}
|
||||
if (version && table && strlen (version) < sizeof (memo_version)) {
|
||||
strcpy (memo_version, version);
|
||||
memo_table = table;
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
const char *dart_cid_kind_name(DartCidKind kind) {
|
||||
@@ -166,7 +197,7 @@ bool dart_cid_is(const DartVerLayout *layout, int cid, DartCidKind kind) {
|
||||
return value >= 0 && cid == value;
|
||||
}
|
||||
|
||||
int dart_cid_typed_data_internal_base(const DartVerLayout *layout) {
|
||||
int dart_cid_typed_data_base(const DartVerLayout *layout) {
|
||||
const DartCidTable *table = cid_table_for_layout (layout);
|
||||
if (table && table->typed_data_internal_base > 0) {
|
||||
return table->typed_data_internal_base;
|
||||
@@ -181,12 +212,12 @@ int dart_cid_typed_data_internal_base(const DartVerLayout *layout) {
|
||||
return 112;
|
||||
}
|
||||
|
||||
int dart_cid_typed_data_internal_limit(const DartVerLayout *layout) {
|
||||
int dart_cid_typed_data_limit(const DartVerLayout *layout) {
|
||||
const DartCidTable *table = cid_table_for_layout (layout);
|
||||
if (table && table->typed_data_internal_limit > 0) {
|
||||
return table->typed_data_internal_limit;
|
||||
}
|
||||
const int base = dart_cid_typed_data_internal_base (layout);
|
||||
const int base = dart_cid_typed_data_base (layout);
|
||||
const int stride = dart_cid_typed_data_stride (layout);
|
||||
return base + (14 * stride);
|
||||
}
|
||||
|
||||
+311
-9
@@ -124,8 +124,61 @@ static char *make_method_flagname(const char *name) {
|
||||
return flagname;
|
||||
}
|
||||
|
||||
static void dump_func_r2(RStrBuf *sb, const DartFunction *fn, bool quiet) {
|
||||
char *flagname = make_method_flagname (fn->name);
|
||||
// Flag and function names are unique in r2: setting an existing flag name
|
||||
// moves it, so a later function sharing a Dart name (e.g. several
|
||||
// Native._anon_closure) would steal it from the first one. Suffix repeated
|
||||
// names with the entry address.
|
||||
static char *make_unique_method_flagname(HtPP *seen, const char *name, ut64 addr) {
|
||||
char *flagname = make_method_flagname (name);
|
||||
bool found = false;
|
||||
ut64 owner = (ut64) (size_t)ht_pp_find (seen, flagname, &found);
|
||||
if (found && owner != addr) {
|
||||
char *unique = r_str_newf ("%s.0x%" PFMT64x, flagname, addr);
|
||||
free (flagname);
|
||||
return unique;
|
||||
}
|
||||
ht_pp_insert (seen, flagname, (void *) (size_t)addr);
|
||||
return flagname;
|
||||
}
|
||||
|
||||
static void method_flagname_free(HtUPKv *kv) {
|
||||
free (kv->value);
|
||||
}
|
||||
|
||||
// Map each function address to its unique method.* name, in app order.
|
||||
static HtUP *method_flagnames(DartApp *app) {
|
||||
HtUP *names = ht_up_new (NULL, method_flagname_free, NULL);
|
||||
HtPP *seen = ht_pp_new0 ();
|
||||
DartFunction *fn;
|
||||
R_VEC_FOREACH (app->functions, fn) {
|
||||
if (fn->name && !ht_up_find (names, fn->addr, NULL)) {
|
||||
ht_up_insert (names, fn->addr, make_unique_method_flagname (seen, fn->name, fn->addr));
|
||||
}
|
||||
}
|
||||
ht_pp_free (seen);
|
||||
return names;
|
||||
}
|
||||
|
||||
// Give each analyzed function its Dart name: r2's `af` prefers flags in the
|
||||
// symbols space (e.g. Mach-O sym.func.*) over the method.* flags set here.
|
||||
void dart_dumper_name_functions(DartApp *app) {
|
||||
if (!app || !app->core || !app->functions) {
|
||||
return;
|
||||
}
|
||||
HtUP *names = method_flagnames (app);
|
||||
DartFunction *fn;
|
||||
R_VEC_FOREACH (app->functions, fn) {
|
||||
RAnalFunction *fcn = r_anal_get_function_at (app->core->anal, fn->addr);
|
||||
const char *name = fcn? ht_up_find (names, fn->addr, NULL): NULL;
|
||||
if (name && strcmp (fcn->name, name)) {
|
||||
r_anal_function_rename (fcn, name);
|
||||
}
|
||||
}
|
||||
ht_up_free (names);
|
||||
}
|
||||
|
||||
static void dump_func_r2(RStrBuf *sb, HtPP *seen, const DartFunction *fn, bool quiet) {
|
||||
char *flagname = make_unique_method_flagname (seen, fn->name, fn->addr);
|
||||
r_strbuf_appendf (sb, "f %s = 0x%" PFMT64x "\n", flagname, (uint64_t)fn->addr);
|
||||
if (!quiet) {
|
||||
r_strbuf_appendf (sb, "'@0x%" PFMT64x "'CC %s\n", (uint64_t)fn->addr, fn->name);
|
||||
@@ -133,7 +186,7 @@ static void dump_func_r2(RStrBuf *sb, const DartFunction *fn, bool quiet) {
|
||||
free (flagname);
|
||||
}
|
||||
|
||||
char *dart_dumper_dump4radare2(DartApp *app) {
|
||||
char *dart_dumper_dump_r2(DartApp *app) {
|
||||
RStrBuf *sb = r_strbuf_new ("");
|
||||
const bool quiet = app && app->dctx.quiet;
|
||||
|
||||
@@ -145,13 +198,15 @@ char *dart_dumper_dump4radare2(DartApp *app) {
|
||||
r_strbuf_appendf (sb, "f app.heap_base = 0x%" PFMT64x "\n", (uint64_t)app->heap_base);
|
||||
|
||||
if (app->functions) {
|
||||
HtPP *seen = ht_pp_new0 ();
|
||||
DartFunction *fn;
|
||||
R_VEC_FOREACH (app->functions, fn) {
|
||||
if (!fn->name) {
|
||||
continue;
|
||||
}
|
||||
dump_func_r2 (sb, fn, quiet);
|
||||
dump_func_r2 (sb, seen, fn, quiet);
|
||||
}
|
||||
ht_pp_free (seen);
|
||||
}
|
||||
|
||||
r_strbuf_append (sb, "dr x27=`e anal.gp`\n");
|
||||
@@ -182,6 +237,9 @@ static void dump_func_json(PJ *pj, const DartFunction *fn) {
|
||||
if (fn->size) {
|
||||
pj_kn (pj, "size", fn->size);
|
||||
}
|
||||
if (R_STR_ISNOTEMPTY (fn->signature)) {
|
||||
pj_ks (pj, "signature", fn->signature);
|
||||
}
|
||||
pj_end (pj);
|
||||
}
|
||||
|
||||
@@ -216,6 +274,7 @@ char *dart_dumper_dump_funcs(DartApp *app, int fmt) {
|
||||
}
|
||||
|
||||
RStrBuf *sb = r_strbuf_new ("");
|
||||
HtPP *seen = ht_pp_new0 ();
|
||||
R_VEC_FOREACH (app->functions, fn) {
|
||||
if (!fn->name) {
|
||||
continue;
|
||||
@@ -224,16 +283,251 @@ char *dart_dumper_dump_funcs(DartApp *app, int fmt) {
|
||||
break;
|
||||
}
|
||||
if (fmt == 'r') {
|
||||
dump_func_r2 (sb, fn, app->dctx.quiet);
|
||||
dump_func_r2 (sb, seen, fn, app->dctx.quiet);
|
||||
} else {
|
||||
dump_func_text (sb, fn);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
ht_pp_free (seen);
|
||||
return drain_trimmed_strbuf (sb);
|
||||
}
|
||||
|
||||
void dart_dumper_apply_to_core(DartApp *app) {
|
||||
// Map a single Dart type token to a C-ish spelling radare2's `afs` accepts.
|
||||
static void dart_type_to_c(RStrBuf *sb, const char *tok) {
|
||||
if (R_STR_ISEMPTY (tok) || !strcmp (tok, "dynamic")) {
|
||||
r_strbuf_append (sb, "void *");
|
||||
} else if (!strcmp (tok, "void")) {
|
||||
r_strbuf_append (sb, "void");
|
||||
} else if (!strcmp (tok, "int")) {
|
||||
r_strbuf_append (sb, "int64_t");
|
||||
} else if (!strcmp (tok, "double")) {
|
||||
r_strbuf_append (sb, "double");
|
||||
} else if (!strcmp (tok, "bool")) {
|
||||
r_strbuf_append (sb, "bool");
|
||||
} else if (!strcmp (tok, "String")) {
|
||||
r_strbuf_append (sb, "char *");
|
||||
} else {
|
||||
// Object / user-class type: an opaque pointer named after the class.
|
||||
char *safe = strdup (tok);
|
||||
r_name_filter (safe, 0);
|
||||
r_strbuf_appendf (sb, "%s *", R_STR_ISNOTEMPTY (safe)? safe: "void");
|
||||
free (safe);
|
||||
}
|
||||
}
|
||||
|
||||
// Turn a recovered Dart signature "RET(T,T,...)" into a radare2 `afs` prototype
|
||||
// for the already filtered function name `name` and return it, or NULL if the
|
||||
// signature is not in that canonical form.
|
||||
static char *dart_signature_to_afs_proto(const char *name, const char *sig) {
|
||||
if (R_STR_ISEMPTY (sig) || strchr (sig, ' ') || strchr (sig, '=')) {
|
||||
return NULL; // not our canonical "RET(T,...)" form
|
||||
}
|
||||
const char *lp = strchr (sig, '(');
|
||||
if (!lp) {
|
||||
return NULL;
|
||||
}
|
||||
char *ret = r_str_ndup (sig, (int) (lp - sig));
|
||||
if (R_STR_ISEMPTY (ret)) {
|
||||
free (ret);
|
||||
return NULL;
|
||||
}
|
||||
char *inside = strdup (lp + 1);
|
||||
char *rp = strrchr (inside, ')');
|
||||
if (rp) {
|
||||
*rp = '\0';
|
||||
}
|
||||
RStrBuf *sb = r_strbuf_new ("");
|
||||
dart_type_to_c (sb, ret);
|
||||
r_strbuf_appendf (sb, " %s(", name);
|
||||
int argc = 0;
|
||||
if (R_STR_ISNOTEMPTY (inside)) {
|
||||
RList *args = r_str_split_list (inside, ",", 0);
|
||||
RListIter *it;
|
||||
char *a;
|
||||
r_list_foreach (args, it, a) {
|
||||
if (argc > 0) {
|
||||
r_strbuf_append (sb, ", ");
|
||||
}
|
||||
dart_type_to_c (sb, r_str_trim_head_ro (a));
|
||||
// The first parameter of an instance method is the receiver.
|
||||
if (argc == 0) {
|
||||
r_strbuf_append (sb, " this");
|
||||
} else {
|
||||
r_strbuf_appendf (sb, " arg%d", argc);
|
||||
}
|
||||
argc++;
|
||||
}
|
||||
r_list_free (args);
|
||||
}
|
||||
r_strbuf_append (sb, ")");
|
||||
free (ret);
|
||||
free (inside);
|
||||
return r_strbuf_drain (sb);
|
||||
}
|
||||
|
||||
// Same scan as is_type() in radare2's libr/anal/type.c.
|
||||
static char *dart_parsed_type_kind(char *s) {
|
||||
char *p;
|
||||
if ((p = strstr (s, "=type")) || (p = strstr (s, "=struct")) || (p = strstr (s, "=union")) ||
|
||||
(p = strstr (s, "=enum")) || (p = strstr (s, "=typedef")) || (p = strstr (s, "=func"))) {
|
||||
return p;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// `afs` stores a prototype with r_anal_save_parsed_type, which first calls
|
||||
// r_anal_remove_parsed_type for every type the parsed C declares. When that type
|
||||
// already exists, the removal r_type_del()s it and then sorts and scans the
|
||||
// whole type database for leftover subkeys. For these prototypes r_type_del has
|
||||
// already removed every key, so the scan finds nothing, but it cost
|
||||
// O(types log types) for every repeated name (anonymous closures, and names the
|
||||
// C parser shortens at ':' such as `dyn:call` -> `call`), making the signature
|
||||
// pass quadratic. Deleting those types here first, with the parser `afs` uses,
|
||||
// lets `afs` take its early return and leaves the same type database.
|
||||
static void dart_types_predelete(RAnal *anal, const char *proto) {
|
||||
Sdb *tdb = anal->sdb_types;
|
||||
if (!tdb) {
|
||||
return;
|
||||
}
|
||||
char *code = r_str_newf ("%s;", proto);
|
||||
char *err = NULL;
|
||||
char *parsed = r_anal_cparse (anal, code, &err);
|
||||
free (code);
|
||||
free (err);
|
||||
if (!parsed) {
|
||||
return;
|
||||
}
|
||||
char *cur = parsed;
|
||||
while ((cur = dart_parsed_type_kind (cur))) {
|
||||
char *name = cur++;
|
||||
*name = 0;
|
||||
while (name > parsed && *(name - 1) != '\n') {
|
||||
name--;
|
||||
}
|
||||
if (sdb_const_get (tdb, name, 0)) {
|
||||
r_type_del (tdb, name);
|
||||
}
|
||||
}
|
||||
free (parsed);
|
||||
}
|
||||
|
||||
static bool dart_flag_found_cb(RFlagItem *fi, void *user) {
|
||||
(void)fi;
|
||||
*(bool *)user = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// With anal.trycatch enabled every `af` scans all flags twice looking for
|
||||
// try.<addr>.catch/filter handler flags. -AAA creates flags for every Dart
|
||||
// method, string and pool reference, so across tens of thousands of functions
|
||||
// those scans dominated the pass. When no try.* flag exists the scans cannot
|
||||
// find a handler, so disabling the option for the pass changes nothing else.
|
||||
bool dart_core_trycatch_suspend(RCore *core) {
|
||||
const bool prev = r_config_get_b (core->config, "anal.trycatch");
|
||||
if (prev) {
|
||||
bool found = false;
|
||||
r_flag_foreach_prefix (core->flags, "try.", 4, dart_flag_found_cb, &found);
|
||||
if (!found) {
|
||||
r_config_set_b (core->config, "anal.trycatch", false);
|
||||
}
|
||||
}
|
||||
return prev;
|
||||
}
|
||||
|
||||
void dart_core_trycatch_restore(RCore *core, bool prev) {
|
||||
r_config_set_b (core->config, "anal.trycatch", prev);
|
||||
}
|
||||
|
||||
static int dart_fn_addr_desc(const void *a, const void *b) {
|
||||
const DartFunction *fa = *(const DartFunction *const *)a;
|
||||
const DartFunction *fb = *(const DartFunction *const *)b;
|
||||
if (fa->addr < fb->addr) {
|
||||
return 1;
|
||||
}
|
||||
if (fa->addr > fb->addr) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Define a function at `addr` if needed and set its recovered signature so that
|
||||
// `afsj`/`afcrj` report real parameter and return types. Functions are analysed
|
||||
// in descending address order: a Dart function whose analysis overruns into the
|
||||
// next one then stops at the already-defined higher function instead of merging.
|
||||
void dart_dumper_apply_signatures(DartApp *app) {
|
||||
if (!app || !app->core || !app->functions) {
|
||||
return;
|
||||
}
|
||||
RCore *core = app->core;
|
||||
size_t total = RVecDartFunction_length (app->functions);
|
||||
if (!total) {
|
||||
return;
|
||||
}
|
||||
DartFunction **order = (DartFunction **)malloc (total * sizeof (DartFunction *));
|
||||
if (!order) {
|
||||
return;
|
||||
}
|
||||
size_t n = 0;
|
||||
DartFunction *fn;
|
||||
R_VEC_FOREACH (app->functions, fn) {
|
||||
if (fn->addr && R_STR_ISNOTEMPTY (fn->signature)) {
|
||||
order[n++] = fn;
|
||||
}
|
||||
}
|
||||
qsort (order, n, sizeof (DartFunction *), dart_fn_addr_desc);
|
||||
HtUP *names = method_flagnames (app);
|
||||
// Keep the analysis local to each function start; the surrounding pass owns
|
||||
// broader discovery. String ESIL emulation is very slow and irrelevant to
|
||||
// prototypes, so disable it for the duration of this pass.
|
||||
// Variable recovery, call-following and jump tables are not needed for the
|
||||
// prototypes either, and var recovery alone is about half of this `af` cost.
|
||||
int prev_hasnext = r_config_get_i (core->config, "anal.hasnext");
|
||||
int prev_emustr = r_config_get_i (core->config, "emu.str");
|
||||
bool prev_calls = r_config_get_b (core->config, "anal.calls");
|
||||
bool prev_vars = r_config_get_b (core->config, "anal.vars");
|
||||
bool prev_jmptbl = r_config_get_b (core->config, "anal.jmp.tbl");
|
||||
r_config_set_i (core->config, "anal.hasnext", 0);
|
||||
r_config_set_i (core->config, "emu.str", 0);
|
||||
r_config_set_b (core->config, "anal.calls", false);
|
||||
r_config_set_b (core->config, "anal.vars", false);
|
||||
r_config_set_b (core->config, "anal.jmp.tbl", false);
|
||||
const bool prev_trycatch = dart_core_trycatch_suspend (core);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
fn = order[i];
|
||||
const char *flagname = ht_up_find (names, fn->addr, NULL);
|
||||
char *name = strdup (flagname? flagname: "fcn");
|
||||
r_name_filter (name, 0);
|
||||
if (R_STR_ISEMPTY (name)) {
|
||||
free (name);
|
||||
name = strdup ("fcn");
|
||||
}
|
||||
char *proto = dart_signature_to_afs_proto (name, fn->signature);
|
||||
if (!proto) {
|
||||
free (name);
|
||||
continue;
|
||||
}
|
||||
if (!r_anal_get_function_at (core->anal, fn->addr)) {
|
||||
r_core_cmdf (core, "af @ 0x%" PFMT64x, fn->addr);
|
||||
}
|
||||
if (r_anal_get_function_at (core->anal, fn->addr)) {
|
||||
dart_types_predelete (core->anal, proto);
|
||||
r_core_cmdf (core, "\"afs %s\" @ 0x%" PFMT64x, proto, fn->addr);
|
||||
}
|
||||
free (proto);
|
||||
free (name);
|
||||
}
|
||||
r_config_set_i (core->config, "anal.hasnext", prev_hasnext);
|
||||
r_config_set_i (core->config, "emu.str", prev_emustr);
|
||||
r_config_set_b (core->config, "anal.calls", prev_calls);
|
||||
r_config_set_b (core->config, "anal.vars", prev_vars);
|
||||
r_config_set_b (core->config, "anal.jmp.tbl", prev_jmptbl);
|
||||
dart_core_trycatch_restore (core, prev_trycatch);
|
||||
ht_up_free (names);
|
||||
free (order);
|
||||
}
|
||||
|
||||
void dart_dumper_apply_to_core(DartApp *app, bool apply_signatures) {
|
||||
if (!app || !app->core) {
|
||||
return;
|
||||
}
|
||||
@@ -245,17 +539,25 @@ void dart_dumper_apply_to_core(DartApp *app) {
|
||||
}
|
||||
|
||||
if (app->functions) {
|
||||
HtUP *names = method_flagnames (app);
|
||||
DartFunction *fn;
|
||||
R_VEC_FOREACH (app->functions, fn) {
|
||||
if (!fn->name) {
|
||||
const char *flagname = ht_up_find (names, fn->addr, NULL);
|
||||
if (!fn->name || !flagname) {
|
||||
continue;
|
||||
}
|
||||
char *flagname = make_method_flagname (fn->name);
|
||||
r_flag_set (core->flags, flagname, fn->addr, fn->size);
|
||||
r_meta_set_string (core->anal, R_META_TYPE_COMMENT, fn->addr, fn->name);
|
||||
free (flagname);
|
||||
}
|
||||
ht_up_free (names);
|
||||
}
|
||||
|
||||
r_core_cmd0 (core, "e emu.str=true");
|
||||
// Applying signatures forces analysis (af) of every typed function, which is
|
||||
// slow on a large snapshot; only do it at the deepest level (-AAA). Lighter
|
||||
// consumers (e.g. the reflow decompiler) read per-function signatures from
|
||||
// the fast `-fj` dump instead.
|
||||
if (apply_signatures) {
|
||||
dart_dumper_apply_signatures (app);
|
||||
}
|
||||
}
|
||||
@@ -101,6 +101,9 @@ void dart_obf_fini(DartCtx *ctx) {
|
||||
ht_pp_free (ctx->obf_by_obfuscated);
|
||||
ctx->obf_by_obfuscated = NULL;
|
||||
ctx->obf_map_tried = false;
|
||||
// Release the read_mem window cache (scoped to one command invocation).
|
||||
dart_read_cache_free (ctx->rmem);
|
||||
ctx->rmem = NULL;
|
||||
}
|
||||
|
||||
char *dart_obf_resolve(DartCtx *ctx, const char *name) {
|
||||
|
||||
+77
-33
@@ -50,7 +50,7 @@ typedef struct {
|
||||
ut64 index;
|
||||
ut8 bits;
|
||||
DartPpInfo pp;
|
||||
DartModernPoolSlotInfo slot;
|
||||
ModernPoolSlot slot;
|
||||
} DartPpSlotInfo;
|
||||
|
||||
static void dart_object_info_fini(DartObjectInfo *info) {
|
||||
@@ -76,7 +76,7 @@ static void dart_pp_slot_info_fini(DartPpSlotInfo *info) {
|
||||
return;
|
||||
}
|
||||
dart_pp_info_fini (&info->pp);
|
||||
dart_modern_pool_slot_info_fini (&info->slot);
|
||||
modern_slot_fini (&info->slot);
|
||||
memset (info, 0, sizeof (*info));
|
||||
}
|
||||
|
||||
@@ -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++) {
|
||||
@@ -147,6 +134,17 @@ static bool dart_object_parse_pp_spec(const char *spec, ut64 *out) {
|
||||
return dart_object_parse_u64 (p + 3, out);
|
||||
}
|
||||
|
||||
static bool dart_object_parse_ref_spec(const char *spec, ut64 *out) {
|
||||
if (R_STR_ISEMPTY (spec) || !out) {
|
||||
return false;
|
||||
}
|
||||
const char *p = r_str_trim_head_ro (spec);
|
||||
if (tolower ((ut8)p[0]) != 'r' || tolower ((ut8)p[1]) != 'e' || tolower ((ut8)p[2]) != 'f' || p[3] != ':') {
|
||||
return false;
|
||||
}
|
||||
return dart_object_parse_u64 (p + 4, out);
|
||||
}
|
||||
|
||||
static char *dart_object_bytes_preview(const ut8 *buf, ut64 len) {
|
||||
RStrBuf *sb = r_strbuf_new ("");
|
||||
for (ut64 i = 0; i < len; i++) {
|
||||
@@ -265,7 +263,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_object_header (out->header);
|
||||
if (out->cid <= 0 || out->cid >= 0xfffff) {
|
||||
return false;
|
||||
}
|
||||
@@ -357,14 +355,14 @@ static bool dart_pp_slot_decode(DartCtx *ctx, ut64 offset, DartPpSlotInfo *slot)
|
||||
if (slot->bits_ok) {
|
||||
DartSnapshotHeader sh = { 0 };
|
||||
if (dart_snapshot_header_read (ctx, slot->pp.snapshot_base, &sh) && sh.ok) {
|
||||
const DartModernClusterRequest req = {
|
||||
const ModernReq req = {
|
||||
ctx,
|
||||
sh.cluster_start,
|
||||
slot->pp.snapshot_base + sh.total_len,
|
||||
sh.nc,
|
||||
sh.nb
|
||||
};
|
||||
slot->slot_ok = dart_modern_resolve_pp_slot (&req, offset, &slot->slot);
|
||||
slot->slot_ok = modern_resolve_pp_slot (&req, offset, &slot->slot);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -391,11 +389,13 @@ static void dart_object_json_object_fields(PJ *pj, const DartObjectInfo *obj) {
|
||||
}
|
||||
}
|
||||
|
||||
static void dart_object_json_value(PJ *pj, const DartValueInfo *value, bool decode_raw) {
|
||||
static void dart_object_json_value(PJ *pj, const DartValueInfo *value, bool decode_raw, bool snapshot_ref) {
|
||||
pj_ko (pj, "value");
|
||||
pj_kn (pj, "raw", value->raw);
|
||||
pj_kb (pj, "decoded", decode_raw);
|
||||
if (!decode_raw) {
|
||||
if (snapshot_ref) {
|
||||
pj_ks (pj, "kind", "snapshot_ref");
|
||||
} else if (!decode_raw) {
|
||||
pj_ks (pj, "kind", "object_pool_ref");
|
||||
} else if (value->selected >= 0) {
|
||||
pj_ks (pj, "kind", "object");
|
||||
@@ -443,7 +443,7 @@ static void dart_object_json_pool_slot(PJ *pj, const DartPpSlotInfo *slot) {
|
||||
}
|
||||
pj_kb (pj, "resolved", slot->slot_ok);
|
||||
if (slot->slot_ok) {
|
||||
const DartModernPoolSlotInfo *si = &slot->slot;
|
||||
const ModernPoolSlot *si = &slot->slot;
|
||||
pj_kn (pj, "cluster_index", si->cluster_index);
|
||||
pj_kn (pj, "pool_index", si->pool_index);
|
||||
pj_kn (pj, "pool_ref", si->pool_ref);
|
||||
@@ -478,7 +478,7 @@ static void dart_object_json_pool_slot(PJ *pj, const DartPpSlotInfo *slot) {
|
||||
pj_end (pj);
|
||||
}
|
||||
|
||||
static char *dart_object_dump_json(const char *spec, const DartValueInfo *value, const DartPpSlotInfo *slot, bool decode_raw) {
|
||||
static char *dart_object_dump_json(const char *spec, const DartValueInfo *value, const DartPpSlotInfo *slot, ModernValueGraph *graph, bool decode_raw) {
|
||||
PJ *pj = pj_new ();
|
||||
if (!pj) {
|
||||
return strdup ("{\"error\":\"Failed to create JSON\"}");
|
||||
@@ -495,6 +495,8 @@ static char *dart_object_dump_json(const char *spec, const DartValueInfo *value,
|
||||
pj_ks (pj, "kind", "pp_slot");
|
||||
}
|
||||
dart_object_json_pool_slot (pj, slot);
|
||||
} else if (graph) {
|
||||
pj_ks (pj, "kind", "snapshot_ref");
|
||||
} else if (decode_raw && value->selected >= 0) {
|
||||
pj_ks (pj, "kind", "object");
|
||||
} else if (value->is_smi) {
|
||||
@@ -502,7 +504,8 @@ static char *dart_object_dump_json(const char *spec, const DartValueInfo *value,
|
||||
} else {
|
||||
pj_ks (pj, "kind", "unknown");
|
||||
}
|
||||
dart_object_json_value (pj, value, decode_raw);
|
||||
dart_object_json_value (pj, value, decode_raw, graph && (!slot || !slot->present));
|
||||
modern_value_graph_json (pj, graph);
|
||||
pj_end (pj);
|
||||
return pj_drain (pj);
|
||||
}
|
||||
@@ -565,7 +568,7 @@ static void dart_object_text_pool_slot(RStrBuf *sb, const DartPpSlotInfo *slot)
|
||||
r_strbuf_appendf (sb, "pp.bits: 0x%02x\n", (unsigned int)slot->bits);
|
||||
}
|
||||
if (slot->slot_ok) {
|
||||
const DartModernPoolSlotInfo *si = &slot->slot;
|
||||
const ModernPoolSlot *si = &slot->slot;
|
||||
r_strbuf_appendf (sb, "pool.ref: %" PRIu64 "\n", (uint64_t)si->pool_ref);
|
||||
r_strbuf_appendf (sb, "pool.index: %" PRIu64 "\n", (uint64_t)si->pool_index);
|
||||
r_strbuf_appendf (sb, "entry.index: %" PRIu64 "\n", (uint64_t)si->index);
|
||||
@@ -594,7 +597,7 @@ static void dart_object_text_pool_slot(RStrBuf *sb, const DartPpSlotInfo *slot)
|
||||
}
|
||||
}
|
||||
|
||||
static char *dart_object_dump_text(const char *spec, const DartValueInfo *value, const DartPpSlotInfo *slot, bool decode_raw, bool quiet) {
|
||||
static char *dart_object_dump_text(const char *spec, const DartValueInfo *value, const DartPpSlotInfo *slot, ModernValueGraph *graph, bool decode_raw, bool quiet) {
|
||||
RStrBuf *sb = r_strbuf_new ("");
|
||||
if (quiet) {
|
||||
if (slot && slot->present && slot->slot_ok && R_STR_ISNOTEMPTY (slot->slot.resolved_name)) {
|
||||
@@ -608,6 +611,10 @@ static char *dart_object_dump_text(const char *spec, const DartValueInfo *value,
|
||||
return r_strbuf_drain (sb);
|
||||
}
|
||||
}
|
||||
if (graph) {
|
||||
r_strbuf_append (sb, modern_value_graph_root_kind (graph));
|
||||
return r_strbuf_drain (sb);
|
||||
}
|
||||
r_strbuf_appendf (sb, "raw=0x%" PFMT64x, value->raw);
|
||||
return r_strbuf_drain (sb);
|
||||
}
|
||||
@@ -615,16 +622,19 @@ static char *dart_object_dump_text(const char *spec, const DartValueInfo *value,
|
||||
if (slot && slot->present) {
|
||||
dart_object_text_pool_slot (sb, slot);
|
||||
}
|
||||
dart_object_text_value (sb, value, decode_raw);
|
||||
if (!graph || (slot && slot->present)) {
|
||||
dart_object_text_value (sb, value, decode_raw);
|
||||
}
|
||||
modern_value_graph_text (sb, graph);
|
||||
return r_strbuf_drain (sb);
|
||||
}
|
||||
|
||||
static char *dart_object_dump_r2(const DartValueInfo *value, const DartPpSlotInfo *slot, bool decode_raw) {
|
||||
static char *dart_object_dump_r2(const DartValueInfo *value, const DartPpSlotInfo *slot, ModernValueGraph *graph, bool decode_raw) {
|
||||
RStrBuf *sb = r_strbuf_new ("");
|
||||
if (slot && slot->present && slot->read_ok) {
|
||||
r_strbuf_appendf (sb, "'@0x%" PFMT64x "'CC Dart PP slot offset=0x%" PFMT64x " raw=0x%" PFMT64x, slot->addr, slot->offset, slot->raw);
|
||||
if (slot->slot_ok) {
|
||||
const DartModernPoolSlotInfo *si = &slot->slot;
|
||||
const ModernPoolSlot *si = &slot->slot;
|
||||
r_strbuf_appendf (sb, " entry=%" PRIu64 " type=%s behavior=%s", (uint64_t)si->index, si->type_name? si->type_name: "unknown", si->behavior_name? si->behavior_name: "unknown");
|
||||
if (R_STR_ISNOTEMPTY (si->resolved_kind)) {
|
||||
r_strbuf_appendf (sb, " resolved=%s", si->resolved_kind);
|
||||
@@ -650,6 +660,9 @@ static char *dart_object_dump_r2(const DartValueInfo *value, const DartPpSlotInf
|
||||
} else if (decode_raw && value->is_smi) {
|
||||
r_strbuf_appendf (sb, "'CC Dart Smi raw=0x%" PFMT64x " value=%" PFMT64d "\n", value->raw, value->smi);
|
||||
}
|
||||
if (graph) {
|
||||
r_strbuf_appendf (sb, "'CC Dart snapshot value kind=%s\n", modern_value_graph_root_kind (graph));
|
||||
}
|
||||
return r_strbuf_drain (sb);
|
||||
}
|
||||
|
||||
@@ -670,8 +683,10 @@ char *dart_pool_dump_object(DartCtx *ctx, const char *spec, int fmt) {
|
||||
DartVerLayout *layout_owned = ctx->layout? NULL: dart_ctx_init_layout (ctx, &layout_tmp);
|
||||
ut64 raw = 0;
|
||||
ut64 pp_offset = 0;
|
||||
ut64 snapshot_ref = 0;
|
||||
bool is_pp = dart_object_parse_pp_spec (spec, &pp_offset);
|
||||
if (!is_pp) {
|
||||
bool is_ref = dart_object_parse_ref_spec (spec, &snapshot_ref);
|
||||
if (!is_pp && !is_ref) {
|
||||
if (!dart_object_parse_u64 (spec, &raw)) {
|
||||
if (layout_owned) {
|
||||
dart_ctx_fini_layout (ctx, layout_owned);
|
||||
@@ -684,6 +699,15 @@ char *dart_pool_dump_object(DartCtx *ctx, const char *spec, int fmt) {
|
||||
if (raw >= DART_SYNTHETIC_PP_BASE) {
|
||||
pp_offset = raw - DART_SYNTHETIC_PP_BASE;
|
||||
is_pp = true;
|
||||
} else {
|
||||
DartPpInfo pp_info = { 0 };
|
||||
if (dart_resolve_pp_info (ctx, &pp_info)) {
|
||||
if (raw >= pp_info.base && raw - pp_info.base < pp_info.size) {
|
||||
pp_offset = raw - pp_info.base;
|
||||
is_pp = true;
|
||||
}
|
||||
dart_pp_info_fini (&pp_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
DartValueInfo value;
|
||||
@@ -698,15 +722,35 @@ char *dart_pool_dump_object(DartCtx *ctx, const char *spec, int fmt) {
|
||||
decode_raw = ! (slot.slot_ok && slot.slot.behavior == 0 && slot.slot.type == 1);
|
||||
}
|
||||
}
|
||||
dart_value_decode (ctx, raw, &value);
|
||||
if (!is_ref) {
|
||||
dart_value_decode (ctx, raw, &value);
|
||||
} else {
|
||||
decode_raw = false;
|
||||
}
|
||||
ModernValueGraph *graph = NULL;
|
||||
if (is_ref || (slot.slot_ok && slot.slot.behavior == 0 && slot.slot.type == 1)) {
|
||||
const ut64 snapshot_base = is_pp? slot.pp.snapshot_base: ctx->iso_data;
|
||||
DartSnapshotHeader sh = { 0 };
|
||||
if (snapshot_base && dart_snapshot_header_read (ctx, snapshot_base, &sh) && sh.ok) {
|
||||
const ModernReq req = {
|
||||
ctx,
|
||||
sh.cluster_start,
|
||||
snapshot_base + sh.total_len,
|
||||
sh.nc,
|
||||
sh.nb
|
||||
};
|
||||
graph = modern_value_graph_new (&req, is_ref? snapshot_ref: slot.slot.ref);
|
||||
}
|
||||
}
|
||||
char *out = NULL;
|
||||
if (fmt == 'j') {
|
||||
out = dart_object_dump_json (spec, &value, is_pp? &slot: NULL, decode_raw);
|
||||
out = dart_object_dump_json (spec, &value, is_pp? &slot: NULL, graph, decode_raw);
|
||||
} else if (fmt == 'r') {
|
||||
out = dart_object_dump_r2 (&value, is_pp? &slot: NULL, decode_raw);
|
||||
out = dart_object_dump_r2 (&value, is_pp? &slot: NULL, graph, decode_raw);
|
||||
} else {
|
||||
out = dart_object_dump_text (spec, &value, is_pp? &slot: NULL, decode_raw, ctx->quiet);
|
||||
out = dart_object_dump_text (spec, &value, is_pp? &slot: NULL, graph, decode_raw, ctx->quiet);
|
||||
}
|
||||
modern_value_graph_free (graph);
|
||||
dart_value_info_fini (&value);
|
||||
dart_pp_slot_info_fini (&slot);
|
||||
if (layout_owned) {
|
||||
|
||||
+327
-89
@@ -2,6 +2,10 @@
|
||||
|
||||
#include "dart_pool_parse_priv.h"
|
||||
|
||||
#ifdef R_BIN_ATTR_MIXIN
|
||||
#define R2_HAS_BIN_ATTR 1
|
||||
#endif
|
||||
|
||||
static DartFieldInfo *dart_field_info_clone(const DartFieldInfo *fi) {
|
||||
if (!fi) {
|
||||
return NULL;
|
||||
@@ -47,10 +51,58 @@ static void dart_interface_info_free(void *p) {
|
||||
}
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
kFieldCid_extract = 10,
|
||||
kLibraryCid_extract = 12,
|
||||
} ExtraCids;
|
||||
// Cluster classes the legacy (non-OBJECT_HEADER) parser knows how to decode,
|
||||
// resolved for the snapshot's own layout. Absent classes resolve to -1 and then
|
||||
// match nothing, which is the point: comparing against a hardcoded id silently
|
||||
// decoded the wrong cluster on every release that shifted the numbering.
|
||||
typedef struct {
|
||||
int string;
|
||||
int one_byte_string;
|
||||
int two_byte_string;
|
||||
int class_;
|
||||
int field;
|
||||
int function;
|
||||
int type;
|
||||
int function_type;
|
||||
int type_parameter;
|
||||
int type_arguments;
|
||||
int array;
|
||||
int immutable_array;
|
||||
int library;
|
||||
} LegacyClusterCids;
|
||||
|
||||
static LegacyClusterCids legacy_cluster_cids(const DartVerLayout *layout) {
|
||||
const LegacyClusterCids lc = {
|
||||
.string = dart_cid_get (layout, DART_CID_STRING),
|
||||
.one_byte_string = dart_cid_get (layout, DART_CID_ONE_BYTE_STRING),
|
||||
.two_byte_string = dart_cid_get (layout, DART_CID_TWO_BYTE_STRING),
|
||||
.class_ = dart_cid_get (layout, DART_CID_CLASS),
|
||||
.field = dart_cid_get (layout, DART_CID_FIELD),
|
||||
.function = dart_cid_get (layout, DART_CID_FUNCTION),
|
||||
.type = dart_cid_get (layout, DART_CID_TYPE),
|
||||
.function_type = dart_cid_get (layout, DART_CID_FUNCTION_TYPE),
|
||||
.type_parameter = dart_cid_get (layout, DART_CID_TYPE_PARAMETER),
|
||||
.type_arguments = dart_cid_get (layout, DART_CID_TYPE_ARGUMENTS),
|
||||
.array = dart_cid_get (layout, DART_CID_ARRAY),
|
||||
.immutable_array = dart_cid_get (layout, DART_CID_IMMUTABLE_ARRAY),
|
||||
.library = dart_cid_get (layout, DART_CID_LIBRARY),
|
||||
};
|
||||
return lc;
|
||||
}
|
||||
|
||||
static inline bool legacy_cid_is(ut64 cid, int resolved) {
|
||||
return resolved >= 0 && cid == (ut64)resolved;
|
||||
}
|
||||
|
||||
// Clusters whose decoders may legitimately give up part-way (variable-length
|
||||
// payloads we do not fully model); the rest are treated as stream corruption.
|
||||
static bool legacy_cid_recoverable(ut64 cid, const LegacyClusterCids *lc) {
|
||||
return legacy_cid_is (cid, lc->function) ||
|
||||
legacy_cid_is (cid, lc->function_type) ||
|
||||
legacy_cid_is (cid, lc->type_arguments) ||
|
||||
legacy_cid_is (cid, lc->array) ||
|
||||
legacy_cid_is (cid, lc->immutable_array);
|
||||
}
|
||||
|
||||
#define DART_TYPE_CLASS_ID_SHIFT 3
|
||||
#define DART_TYPE_CLASS_ID_MASK ((1U << 20) - 1)
|
||||
@@ -415,7 +467,7 @@ static int decode_type_cluster_ext(ClusterStream *s, DartCtx *ctx, RList *type_l
|
||||
for (ut64 i = 0; i < count; i++) {
|
||||
DartTypeInfo *ti = R_NEW0 (DartTypeInfo);
|
||||
ti->ref_id = (*ref_counter)++;
|
||||
ti->kind = kTypeCid;
|
||||
ti->kind = DART_TYPE_KIND_TYPE;
|
||||
ut64 type_test_stub_ref = 0;
|
||||
ut64 hash_ref = 0;
|
||||
ut64 args_ref = 0;
|
||||
@@ -451,7 +503,7 @@ static int decode_type_parameter_cluster_ext(ClusterStream *s, DartCtx *ctx, RLi
|
||||
for (ut64 i = 0; i < count; i++) {
|
||||
DartTypeInfo *ti = R_NEW0 (DartTypeInfo);
|
||||
ti->ref_id = (*ref_counter)++;
|
||||
ti->kind = kTypeParameterCid;
|
||||
ti->kind = DART_TYPE_KIND_TYPE_PARAMETER;
|
||||
ut64 type_test_stub_ref = 0;
|
||||
ut64 hash_ref = 0;
|
||||
ut64 owner_ref = 0;
|
||||
@@ -518,7 +570,7 @@ static int decode_function_type_cluster_ext(ClusterStream *s, DartCtx *ctx, RLis
|
||||
for (ut64 i = 0; i < count; i++) {
|
||||
DartTypeInfo *ti = R_NEW0 (DartTypeInfo);
|
||||
ti->ref_id = (*ref_counter)++;
|
||||
ti->kind = kFunctionTypeCid;
|
||||
ti->kind = DART_TYPE_KIND_FUNCTION_TYPE;
|
||||
ut64 type_test_stub_ref = 0;
|
||||
ut64 hash_ref = 0;
|
||||
if (!cs_read_ref_id (s, &type_test_stub_ref) || !cs_read_ref_id (s, &hash_ref) || !cs_read_ref_id (s, &ti->type_parameters_ref) || !cs_read_ref_id (s, &ti->result_type_ref) || !cs_read_ref_id (s, &ti->parameter_types_ref) || !cs_read_ref_id (s, &ti->named_parameter_names_ref)) {
|
||||
@@ -658,23 +710,27 @@ static char *dup_ref_string_obf(DartCtx *ctx, ut64 ref) {
|
||||
return value;
|
||||
}
|
||||
|
||||
static const char *builtin_type_name(ut64 cid) {
|
||||
switch (cid) {
|
||||
case kStringCid:
|
||||
case kOneByteStringCid:
|
||||
case kTwoByteStringCid:
|
||||
// `Type.type_class_id` is a class id packed into the type's flags, not a ref, so
|
||||
// it is compared against this layout's real ids.
|
||||
static const char *builtin_type_name(DartCtx *ctx, ut64 cid) {
|
||||
const DartVerLayout *layout = ctx? ctx->layout: NULL;
|
||||
if (legacy_cid_is (cid, dart_cid_get (layout, DART_CID_STRING)) ||
|
||||
legacy_cid_is (cid, dart_cid_get (layout, DART_CID_ONE_BYTE_STRING)) ||
|
||||
legacy_cid_is (cid, dart_cid_get (layout, DART_CID_TWO_BYTE_STRING))) {
|
||||
return "String";
|
||||
case kArrayCid:
|
||||
case kImmutableArrayCid:
|
||||
case kGrowableObjectArrayCid:
|
||||
return "List";
|
||||
case kMintCid:
|
||||
return "int";
|
||||
case kDoubleCid:
|
||||
return "double";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
if (legacy_cid_is (cid, dart_cid_get (layout, DART_CID_ARRAY)) ||
|
||||
legacy_cid_is (cid, dart_cid_get (layout, DART_CID_IMMUTABLE_ARRAY)) ||
|
||||
legacy_cid_is (cid, dart_cid_get (layout, DART_CID_GROWABLE_OBJECT_ARRAY))) {
|
||||
return "List";
|
||||
}
|
||||
if (legacy_cid_is (cid, dart_cid_get (layout, DART_CID_MINT))) {
|
||||
return "int";
|
||||
}
|
||||
if (legacy_cid_is (cid, dart_cid_get (layout, DART_CID_DOUBLE))) {
|
||||
return "double";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static DartTypeInfo *find_type_by_ref(RList *type_list, ut64 ref) {
|
||||
@@ -855,7 +911,7 @@ static char *resolve_type_name_depth(DartCtx *ctx, RList *class_list, RList *typ
|
||||
if (R_STR_ISNOTEMPTY (ti->name)) {
|
||||
return strdup (ti->name);
|
||||
}
|
||||
if (ti->kind == kTypeParameterCid) {
|
||||
if (ti->kind == DART_TYPE_KIND_TYPE_PARAMETER) {
|
||||
bool is_function_param = (ti->flags & DART_TYPE_PARAMETER_FUNCTION_FLAG) != 0;
|
||||
const char *base_prefix = is_function_param? "F": "C";
|
||||
const char *index_prefix = is_function_param? "Y": "X";
|
||||
@@ -868,7 +924,7 @@ static char *resolve_type_name_depth(DartCtx *ctx, RList *class_list, RList *typ
|
||||
}
|
||||
return name;
|
||||
}
|
||||
if (ti->kind == kFunctionTypeCid) {
|
||||
if (ti->kind == DART_TYPE_KIND_FUNCTION_TYPE) {
|
||||
char *name = resolve_function_type_name (ctx, class_list, type_list, type_args_list, array_list, ti, depth);
|
||||
if (name) {
|
||||
ti->name = strdup (name);
|
||||
@@ -876,7 +932,7 @@ static char *resolve_type_name_depth(DartCtx *ctx, RList *class_list, RList *typ
|
||||
return name;
|
||||
}
|
||||
char *name = NULL;
|
||||
const char *builtin = builtin_type_name (ti->type_class_ref);
|
||||
const char *builtin = builtin_type_name (ctx, ti->type_class_ref);
|
||||
if (builtin) {
|
||||
name = strdup (builtin);
|
||||
} else {
|
||||
@@ -1319,7 +1375,7 @@ static void resolve_class_interfaces(DartCtx *ctx, RList *class_list, RList *typ
|
||||
char *name = resolve_type_name (ctx, class_list, type_list, type_args_list, array_list, type_ref);
|
||||
ut64 class_ref = 0;
|
||||
DartTypeInfo *ti = find_type_by_ref (type_list, type_ref);
|
||||
if (ti && ti->kind == kTypeCid) {
|
||||
if (ti && ti->kind == DART_TYPE_KIND_TYPE) {
|
||||
DartClassInfo *target_ci = find_class_by_ref (class_list, ti->type_class_ref);
|
||||
if (target_ci) {
|
||||
class_ref = target_ci->ref_id;
|
||||
@@ -1432,6 +1488,46 @@ static void resolve_class_and_field_names(DartCtx *ctx, RList *class_list, RList
|
||||
}
|
||||
}
|
||||
|
||||
// Function fills only carry an InstructionTable slot; decode the table once
|
||||
// and turn each method's slot into its entry address.
|
||||
static void resolve_method_entry_points(DartCtx *ctx, RList *class_list) {
|
||||
const int dump_snapshot_json = ctx->dump_snapshot_json;
|
||||
const int dump_fns_limit = ctx->dump_fns_limit;
|
||||
ctx->dump_snapshot_json = 0;
|
||||
ctx->dump_fns_limit = 0;
|
||||
RVecDartInstructionTableEntry *it = dart_pool_extract_instruction_table (ctx);
|
||||
ctx->dump_snapshot_json = dump_snapshot_json;
|
||||
ctx->dump_fns_limit = dump_fns_limit;
|
||||
if (!it) {
|
||||
return;
|
||||
}
|
||||
ut64 count = 0;
|
||||
DartInstructionTableEntry *ie;
|
||||
R_VEC_FOREACH (it, ie) {
|
||||
count = R_MAX (count, ie->index + 1);
|
||||
}
|
||||
// Function::code_index is the raw InstructionTable slot, not the
|
||||
// first_entry_with_code-relative index kept in ie->code_index. Slots below
|
||||
// first_entry_with_code lost their Code object (discarded in AOT) but
|
||||
// still own real instructions, so map them too.
|
||||
ut64 *addr_by_code_index = calloc (count + 1, sizeof (ut64));
|
||||
R_VEC_FOREACH (it, ie) {
|
||||
addr_by_code_index[ie->index] = ie->address;
|
||||
}
|
||||
RListIter *iter, *miter;
|
||||
DartClassInfo *ci;
|
||||
DartMethodInfo *mi;
|
||||
r_list_foreach (class_list, iter, ci) {
|
||||
r_list_foreach (ci->methods, miter, mi) {
|
||||
if (!mi->entry_point && mi->code_index < count) {
|
||||
mi->entry_point = addr_by_code_index[mi->code_index];
|
||||
}
|
||||
}
|
||||
}
|
||||
free (addr_by_code_index);
|
||||
dart_instruction_table_list_free (it);
|
||||
}
|
||||
|
||||
RList *dart_pool_extract_classes(DartCtx *ctx) {
|
||||
if (!ctx || !ctx->core) {
|
||||
return NULL;
|
||||
@@ -1464,8 +1560,8 @@ RList *dart_pool_extract_classes(DartCtx *ctx) {
|
||||
ut64 total_refs = sh.nb + sh.no + 16;
|
||||
ctx->refs_count = total_refs;
|
||||
ctx->refs = (void **)calloc (total_refs, sizeof (void *));
|
||||
const DartModernClusterRequest req = { ctx, sh.cluster_start, snapshot_base + sh.total_len, sh.nc, sh.nb };
|
||||
modern_classes = dart_modern_extract_classes_from_clusters (&req, class_list);
|
||||
const ModernReq req = { ctx, sh.cluster_start, snapshot_base + sh.total_len, sh.nc, sh.nb };
|
||||
modern_classes = modern_extract_classes (&req, class_list);
|
||||
if (modern_classes) {
|
||||
if (ctx->verbose > 0) {
|
||||
int modern_fields = 0;
|
||||
@@ -1504,71 +1600,50 @@ RList *dart_pool_extract_classes(DartCtx *ctx) {
|
||||
.end = snapshot_base + sh.total_len
|
||||
};
|
||||
ut64 ref_counter = sh.nb + 1;
|
||||
const LegacyClusterCids lc = legacy_cluster_cids (ctx->layout);
|
||||
for (ut64 ci2 = 0; ci2 < sh.nc && stream.cursor < stream.end; ci2++) {
|
||||
uint32_t tags = 0;
|
||||
if (!cs_read_u32 (&stream, &tags)) {
|
||||
ut32 tags = 0;
|
||||
bool tags_read = synthetic_legacy_parse
|
||||
? cs_read_u32 (&stream, &tags)
|
||||
: cs_read_tagged32 (&stream, &tags);
|
||||
if (!tags_read) {
|
||||
break;
|
||||
}
|
||||
uint32_t cid = (tags >> 12) & 0xFFFFF;
|
||||
const ut32 cid = dart_cid_from_tags (ctx, tags);
|
||||
bool is_canonical = ((tags >> 1) & 1) != 0;
|
||||
(void)is_canonical;
|
||||
if (ctx->verbose > 1) {
|
||||
fprintf (stderr, "[r2flutter] Cluster %" PRIu64 ": cid=%u (Class=%d,Field=%d,String=%d)\n", ci2, cid, kClassCid, kFieldCid_extract, kOneByteStringCid);
|
||||
fprintf (stderr, "[r2flutter] Cluster %" PRIu64 ": cid=%u (Class=%d,Field=%d,String=%d)\n", ci2, cid, lc.class_, lc.field, lc.one_byte_string);
|
||||
}
|
||||
int rc = 0;
|
||||
switch (cid) {
|
||||
case kOneByteStringCid:
|
||||
case kTwoByteStringCid:
|
||||
case kStringCid:
|
||||
if (legacy_cid_is (cid, lc.one_byte_string) || legacy_cid_is (cid, lc.two_byte_string) || legacy_cid_is (cid, lc.string)) {
|
||||
rc = decode_string_cluster (&stream, ctx, &ref_counter, false);
|
||||
break;
|
||||
case kClassCid:
|
||||
} else if (legacy_cid_is (cid, lc.class_)) {
|
||||
rc = decode_class_cluster_ext (&stream, ctx, class_list, &ref_counter);
|
||||
break;
|
||||
case kFieldCid_extract:
|
||||
} else if (legacy_cid_is (cid, lc.field)) {
|
||||
rc = decode_field_cluster_ext (&stream, ctx, field_list, &ref_counter);
|
||||
break;
|
||||
case kFunctionCid:
|
||||
} else if (legacy_cid_is (cid, lc.function)) {
|
||||
rc = decode_function_cluster_ext (&stream, ctx, method_list, &ref_counter);
|
||||
if (rc < 0) {
|
||||
rc = 0;
|
||||
skip_generic_cluster (&stream);
|
||||
}
|
||||
break;
|
||||
case kTypeCid:
|
||||
} else if (legacy_cid_is (cid, lc.type)) {
|
||||
rc = decode_type_cluster_ext (&stream, ctx, type_list, &ref_counter);
|
||||
break;
|
||||
case kFunctionTypeCid:
|
||||
} else if (legacy_cid_is (cid, lc.function_type)) {
|
||||
rc = decode_function_type_cluster_ext (&stream, ctx, type_list, &ref_counter);
|
||||
if (rc < 0) {
|
||||
rc = 0;
|
||||
skip_generic_cluster (&stream);
|
||||
}
|
||||
break;
|
||||
case kTypeParameterCid:
|
||||
} else if (legacy_cid_is (cid, lc.type_parameter)) {
|
||||
rc = decode_type_parameter_cluster_ext (&stream, ctx, type_list, &ref_counter);
|
||||
break;
|
||||
case kTypeArgumentsCid:
|
||||
} else if (legacy_cid_is (cid, lc.type_arguments)) {
|
||||
rc = decode_type_arguments_cluster_ext (&stream, ctx, type_args_list, &ref_counter);
|
||||
if (rc < 0) {
|
||||
rc = 0;
|
||||
skip_generic_cluster (&stream);
|
||||
}
|
||||
break;
|
||||
case kArrayCid:
|
||||
case kImmutableArrayCid:
|
||||
} else if (legacy_cid_is (cid, lc.array) || legacy_cid_is (cid, lc.immutable_array)) {
|
||||
rc = decode_array_cluster_ext (&stream, ctx, array_list, &ref_counter);
|
||||
if (rc < 0) {
|
||||
rc = 0;
|
||||
skip_generic_cluster (&stream);
|
||||
}
|
||||
break;
|
||||
case kLibraryCid_extract:
|
||||
} else if (legacy_cid_is (cid, lc.library)) {
|
||||
rc = decode_library_cluster_ext (&stream, ctx, libraries, &ref_counter);
|
||||
break;
|
||||
default:
|
||||
} else {
|
||||
skip_generic_cluster (&stream);
|
||||
}
|
||||
// A cluster we recognise but cannot decode is not fatal: skip its
|
||||
// bytes and carry on rather than abandoning the whole snapshot.
|
||||
if (rc < 0 && legacy_cid_recoverable (cid, &lc)) {
|
||||
rc = 0;
|
||||
skip_generic_cluster (&stream);
|
||||
break;
|
||||
}
|
||||
if (rc < 0) {
|
||||
break;
|
||||
@@ -1643,15 +1718,14 @@ RList *dart_pool_extract_classes(DartCtx *ctx) {
|
||||
r_list_free (method_list);
|
||||
r_list_free (field_list);
|
||||
if (ctx->dump_fields && r_list_length (class_list) > 0) {
|
||||
ut64 kAlign = ctx->layout && ctx->layout->max_alignment? (ut64)ctx->layout->max_alignment: 16;
|
||||
ut64 data_image_base = snapshot_base + ((sh.total_len + (kAlign - 1)) & ~ (kAlign - 1));
|
||||
ut64 data_image_base = dart_snapshot_data_image_base (snapshot_base, sh.total_len);
|
||||
ut64 data_image_end = ctx->iso_instr? ctx->iso_instr: (data_image_base + (4ULL << 20));
|
||||
scan_fields_from_data_image (ctx, class_list, data_image_base, data_image_end);
|
||||
scan_methods_from_data_image (ctx, class_list, data_image_base, data_image_end);
|
||||
if (ctx->vm_data) {
|
||||
DartSnapshotHeader vm_sh;
|
||||
if (dart_snapshot_header_read (ctx, ctx->vm_data, &vm_sh)) {
|
||||
ut64 vm_data_base = ctx->vm_data + ((vm_sh.total_len + (kAlign - 1)) & ~ (kAlign - 1));
|
||||
ut64 vm_data_base = dart_snapshot_data_image_base (ctx->vm_data, vm_sh.total_len);
|
||||
ut64 vm_data_end = ctx->vm_instr? ctx->vm_instr: (vm_data_base + (4ULL << 20));
|
||||
scan_fields_from_data_image (ctx, class_list, vm_data_base, vm_data_end);
|
||||
scan_methods_from_data_image (ctx, class_list, vm_data_base, vm_data_end);
|
||||
@@ -1659,12 +1733,18 @@ RList *dart_pool_extract_classes(DartCtx *ctx) {
|
||||
}
|
||||
}
|
||||
dart_ctx_fini_layout (ctx, layout_owned);
|
||||
if (modern_classes) {
|
||||
resolve_method_entry_points (ctx, class_list);
|
||||
}
|
||||
return class_list;
|
||||
}
|
||||
|
||||
static void dump_class_json(PJ *pj, const DartClassInfo *ci) {
|
||||
pj_o (pj);
|
||||
pj_kn (pj, "ref", ci->ref_id);
|
||||
if (ci->class_id) {
|
||||
pj_ki (pj, "class_id", ci->class_id);
|
||||
}
|
||||
if (ci->name) {
|
||||
pj_ks (pj, "name", ci->name);
|
||||
}
|
||||
@@ -1677,10 +1757,15 @@ static void dump_class_json(PJ *pj, const DartClassInfo *ci) {
|
||||
}
|
||||
pj_end (pj);
|
||||
}
|
||||
if (ci->super_class_name || ci->super_class_ref) {
|
||||
if (ci->super_class_name || ci->super_class_ref || ci->super_type_ref) {
|
||||
pj_k (pj, "super");
|
||||
pj_o (pj);
|
||||
pj_kn (pj, "ref", ci->super_class_ref);
|
||||
if (ci->super_class_ref) {
|
||||
pj_kn (pj, "ref", ci->super_class_ref);
|
||||
}
|
||||
if (ci->super_type_ref) {
|
||||
pj_kn (pj, "type_ref", ci->super_type_ref);
|
||||
}
|
||||
if (ci->super_class_name) {
|
||||
pj_ks (pj, "name", ci->super_class_name);
|
||||
}
|
||||
@@ -1711,8 +1796,14 @@ static void dump_class_json(PJ *pj, const DartClassInfo *ci) {
|
||||
pj_k (pj, "layout");
|
||||
pj_o (pj);
|
||||
pj_ki (pj, "instance_size", ci->instance_size);
|
||||
if (ci->next_field_offset) {
|
||||
pj_ki (pj, "next_field_offset", ci->next_field_offset);
|
||||
}
|
||||
pj_ki (pj, "type_params", ci->num_type_parameters);
|
||||
pj_ki (pj, "type_arg_offset", ci->type_argument_offset);
|
||||
if (ci->field_bitmap_present) {
|
||||
pj_kn (pj, "field_bitmap", ci->field_bitmap);
|
||||
}
|
||||
pj_end (pj);
|
||||
pj_k (pj, "flags");
|
||||
pj_o (pj);
|
||||
@@ -1968,6 +2059,104 @@ static void append_comment_cmd(RStrBuf *sb, ut64 addr, const char *msg) {
|
||||
free (b64);
|
||||
}
|
||||
|
||||
#ifdef R2_HAS_BIN_ATTR
|
||||
static ut64 dart_class_attrbits(const DartClassInfo *ci) {
|
||||
ut64 a = 0;
|
||||
if (ci->flags & DART_CLASS_ABSTRACT) {
|
||||
a |= R_BIN_ATTR_ABSTRACT;
|
||||
}
|
||||
if (ci->flags & DART_CLASS_ENUM) {
|
||||
a |= R_BIN_ATTR_ENUM;
|
||||
}
|
||||
if (ci->flags & DART_CLASS_MIXIN) {
|
||||
a |= R_BIN_ATTR_MIXIN;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
static ut64 dart_field_attrbits(const DartFieldInfo *fi) {
|
||||
ut64 a = 0;
|
||||
if (fi->flags & DART_FIELD_STATIC) {
|
||||
a |= R_BIN_ATTR_STATIC;
|
||||
}
|
||||
if (fi->flags & DART_FIELD_FINAL) {
|
||||
a |= R_BIN_ATTR_FINAL;
|
||||
}
|
||||
if (fi->flags & DART_FIELD_CONST) {
|
||||
a |= R_BIN_ATTR_CONST;
|
||||
}
|
||||
if (fi->flags & DART_FIELD_LATE) {
|
||||
a |= R_BIN_ATTR_LATE;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
static ut64 dart_method_attrbits(const DartMethodInfo *mi) {
|
||||
const char *kind = method_kind_name (mi->kind_tag);
|
||||
ut64 a = method_is_static (mi)? R_BIN_ATTR_STATIC: 0;
|
||||
if (strstr (kind, "Getter")) {
|
||||
a |= R_BIN_ATTR_GETTER;
|
||||
} else if (strstr (kind, "Setter")) {
|
||||
a |= R_BIN_ATTR_SETTER;
|
||||
} else if (!strcmp (kind, "Constructor")) {
|
||||
a |= R_BIN_ATTR_CONSTRUCTOR;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
#endif
|
||||
|
||||
// the dart_*_ic_args helpers build the ic+ argument string (leading space, may be empty)
|
||||
// using the attribute names that r_bin_attr_forclass/symbol/field parse back
|
||||
|
||||
static char *dart_class_ic_args(const DartClassInfo *ci) {
|
||||
#ifdef R2_HAS_BIN_ATTR
|
||||
RStrBuf *sb = r_strbuf_new ("");
|
||||
if (ci->instance_size > 0) {
|
||||
r_strbuf_appendf (sb, " size=%u", ci->instance_size);
|
||||
}
|
||||
const char *ns = ci->library_name;
|
||||
if (R_STR_ISNOTEMPTY (ns) && !ns[strspn (ns, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.:/-")]) {
|
||||
r_strbuf_appendf (sb, " ns=%s", ns);
|
||||
}
|
||||
char *attrs = r_bin_attr_tostring (dart_class_attrbits (ci), false);
|
||||
if (R_STR_ISNOTEMPTY (attrs)) {
|
||||
r_strbuf_appendf (sb, " %s", attrs);
|
||||
}
|
||||
free (attrs);
|
||||
return r_strbuf_drain (sb);
|
||||
#else
|
||||
(void)ci;
|
||||
return strdup ("");
|
||||
#endif
|
||||
}
|
||||
|
||||
static char *dart_field_ic_args(const DartFieldInfo *fi) {
|
||||
#ifdef R2_HAS_BIN_ATTR
|
||||
char *attrs = r_bin_attr_tostring (dart_field_attrbits (fi), false);
|
||||
char *res = r_str_newf (" %s%s%s", field_c_type (fi), R_STR_ISNOTEMPTY (attrs)? " ": "", r_str_get (attrs));
|
||||
free (attrs);
|
||||
return res;
|
||||
#else
|
||||
return r_str_newf (" %s", field_c_type (fi));
|
||||
#endif
|
||||
}
|
||||
|
||||
static char *dart_method_ic_args(const DartMethodInfo *mi) {
|
||||
#ifdef R2_HAS_BIN_ATTR
|
||||
char *attrs = r_bin_attr_tostring (dart_method_attrbits (mi), false);
|
||||
if (R_STR_ISEMPTY (attrs)) {
|
||||
free (attrs);
|
||||
return strdup ("");
|
||||
}
|
||||
char *res = r_str_newf (" %s", attrs);
|
||||
free (attrs);
|
||||
return res;
|
||||
#else
|
||||
(void)mi;
|
||||
return strdup ("");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void append_ic_inheritance_cmd(RStrBuf *sb, const char *class_name, const char *base_name) {
|
||||
if (!sb || R_STR_ISEMPTY (class_name) || R_STR_ISEMPTY (base_name)) {
|
||||
return;
|
||||
@@ -1981,15 +2170,19 @@ static void append_ic_field_cmd(RStrBuf *sb, const char *class_name, const DartF
|
||||
return;
|
||||
}
|
||||
char *field_name = member_export_name (fi->name, "field");
|
||||
r_strbuf_appendf (sb, "ic+%s..%s %s @ 0x%x\n", class_name, field_name, field_c_type (fi), fi->offset);
|
||||
char *args = dart_field_ic_args (fi);
|
||||
r_strbuf_appendf (sb, "'@0x%x'ic+%s..%s%s\n", fi->offset, class_name, field_name, args);
|
||||
free (args);
|
||||
free (field_name);
|
||||
}
|
||||
|
||||
static void append_ic_method_cmd(RStrBuf *sb, const char *class_name, const char *method_name, ut64 addr) {
|
||||
static void append_ic_method_cmd(RStrBuf *sb, const char *class_name, const char *method_name, ut64 addr, const DartMethodInfo *mi) {
|
||||
if (!sb || R_STR_ISEMPTY (class_name) || R_STR_ISEMPTY (method_name)) {
|
||||
return;
|
||||
}
|
||||
r_strbuf_appendf (sb, "ic+%s.%s @ 0x%" PFMT64x "\n", class_name, method_name, addr);
|
||||
char *args = dart_method_ic_args (mi);
|
||||
r_strbuf_appendf (sb, "ic+%s.%s%s @ 0x%" PFMT64x "\n", class_name, method_name, args, addr);
|
||||
free (args);
|
||||
}
|
||||
|
||||
static void dump_class_r2_metadata(RStrBuf *sb, const DartClassInfo *ci, bool quiet) {
|
||||
@@ -2001,7 +2194,9 @@ static void dump_class_r2_metadata(RStrBuf *sb, const DartClassInfo *ci, bool qu
|
||||
if (!quiet) {
|
||||
r_strbuf_appendf (sb, "# r2 class %s original=%s\n", class_ref, ci->name);
|
||||
}
|
||||
r_strbuf_appendf (sb, "ic+%s @ 0\n", class_ref);
|
||||
char *cargs = dart_class_ic_args (ci);
|
||||
r_strbuf_appendf (sb, "ic+%s%s @ 0\n", class_ref, cargs);
|
||||
free (cargs);
|
||||
r_strbuf_appendf (sb, "ac %s\n", class_ref);
|
||||
#if 0
|
||||
// TOO SLOW
|
||||
@@ -2058,7 +2253,7 @@ static void dump_class_r2_metadata(RStrBuf *sb, const DartClassInfo *ci, bool qu
|
||||
const char *method_ref = R_STR_ISNOTEMPTY (method_name)? method_name: "method";
|
||||
char *cc = method_dyncc (ci, mi);
|
||||
ut64 addr = method_export_addr (mi);
|
||||
append_ic_method_cmd (sb, class_ref, method_ref, addr);
|
||||
append_ic_method_cmd (sb, class_ref, method_ref, addr, mi);
|
||||
if (!quiet) {
|
||||
r_strbuf_appendf (sb, "# method %s.%s kind=%s mode=%s cc=%s", class_ref, method_ref, method_kind_name (mi->kind_tag), method_mode_name (mi), cc);
|
||||
if (R_STR_ISNOTEMPTY (mi->signature)) {
|
||||
@@ -2248,6 +2443,7 @@ char *dart_pool_dump_classes(DartCtx *ctx, int fmt) {
|
||||
dump_class_text (sb, ci, fmt, type_view, quiet);
|
||||
}
|
||||
if (fmt == 'r') {
|
||||
r_strbuf_append (sb, "e bin.lang=dart\n");
|
||||
if (!quiet) {
|
||||
r_strbuf_append (sb, "# Dart class symbols and analysis metadata\n");
|
||||
}
|
||||
@@ -2296,8 +2492,19 @@ static RBinClass *core_bin_get_or_add_class(RCore *core, const char *class_name,
|
||||
}
|
||||
klass->origin = R_BIN_CLASS_ORIGIN_USER;
|
||||
klass->addr = 0;
|
||||
klass->instance_size = ci? ci->instance_size: 0;
|
||||
klass->index = r_list_length (klasses);
|
||||
#ifdef R2_HAS_BIN_ATTR
|
||||
if (ci) {
|
||||
klass->attr.size = ci->instance_size;
|
||||
klass->attr.flags = dart_class_attrbits (ci);
|
||||
klass->attr.lang = R_BIN_LANG_DART;
|
||||
if (R_STR_ISNOTEMPTY (ci->library_name)) {
|
||||
klass->attr.ns = strdup (ci->library_name);
|
||||
}
|
||||
}
|
||||
#else
|
||||
klass->instance_size = ci? ci->instance_size: 0;
|
||||
#endif
|
||||
r_list_append (klasses, klass);
|
||||
return klass;
|
||||
}
|
||||
@@ -2361,9 +2568,16 @@ static void core_apply_bin_field(RBinClass *klass, const DartFieldInfo *fi, int
|
||||
field->vaddr = fi->offset;
|
||||
field->paddr = fi->offset;
|
||||
field->value = fi->offset;
|
||||
#ifdef R2_HAS_BIN_ATTR
|
||||
field->attr.size = 8;
|
||||
field->attr.offset = fi->offset;
|
||||
field->attr.kind = R_BIN_FIELD_KIND_FIELD;
|
||||
field->attr.flags = dart_field_attrbits (fi);
|
||||
#else
|
||||
field->size = 8;
|
||||
field->offset = fi->offset;
|
||||
field->kind = R_BIN_FIELD_KIND_FIELD;
|
||||
#endif
|
||||
field->type = r_bin_name_new (R_STR_ISNOTEMPTY (fi->type_name)? fi->type_name: "dynamic");
|
||||
(*count)++;
|
||||
}
|
||||
@@ -2389,7 +2603,12 @@ static void core_apply_bin_method(RBinClass *klass, const DartMethodInfo *mi, in
|
||||
sym->type = "method";
|
||||
sym->paddr = addr;
|
||||
sym->vaddr = addr;
|
||||
#ifdef R2_HAS_BIN_ATTR
|
||||
sym->attr.flags = dart_method_attrbits (mi);
|
||||
sym->attr.lang = R_BIN_LANG_DART;
|
||||
#else
|
||||
sym->size = 0;
|
||||
#endif
|
||||
(*count)++;
|
||||
}
|
||||
free (method_name);
|
||||
@@ -2484,14 +2703,26 @@ static void core_apply_type(RCore *core, const DartClassInfo *ci, const char *cl
|
||||
.name = field_name,
|
||||
.type = strdup (field_c_type (fi)),
|
||||
.offset = fi->offset,
|
||||
#if R2_ABIVERSION >= 125
|
||||
.bitsize = 64,
|
||||
#else
|
||||
.size = 64,
|
||||
};
|
||||
#endif
|
||||
};
|
||||
#if R2_ABIVERSION >= 125
|
||||
RAnalStructMember *slot = RVecAnalTypeMember_emplace_back (&type->struct_data.members);
|
||||
#else
|
||||
RAnalStructMember *slot = RVecAnalStructMember_emplace_back (&type->struct_data.members);
|
||||
#endif
|
||||
if (slot) {
|
||||
*slot = member;
|
||||
(*field_count)++;
|
||||
} else {
|
||||
#if R2_ABIVERSION >= 125
|
||||
anal_type_member_fini (&member);
|
||||
#else
|
||||
anal_struct_member_fini (&member);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2547,7 +2778,9 @@ static void core_apply_ic_field(RCore *core, const char *class_name, const DartF
|
||||
return;
|
||||
}
|
||||
char *field_name = member_export_name (fi->name, "field");
|
||||
core_apply_ic_command (core, r_str_newf ("ic+%s..%s %s @ 0x%x", class_name, field_name, field_c_type (fi), fi->offset));
|
||||
char *args = dart_field_ic_args (fi);
|
||||
core_apply_ic_command (core, r_str_newf ("'@0x%x'ic+%s..%s%s", fi->offset, class_name, field_name, args));
|
||||
free (args);
|
||||
free (field_name);
|
||||
}
|
||||
|
||||
@@ -2557,7 +2790,9 @@ static void core_apply_ic_method(RCore *core, const char *class_name, const Dart
|
||||
}
|
||||
char *method_name = member_export_name (mi->name, "method");
|
||||
ut64 addr = method_export_addr (mi);
|
||||
core_apply_ic_command (core, r_str_newf ("ic+%s.%s @ 0x%" PFMT64x, class_name, method_name, addr));
|
||||
char *args = dart_method_ic_args (mi);
|
||||
core_apply_ic_command (core, r_str_newf ("ic+%s.%s%s @ 0x%" PFMT64x, class_name, method_name, args, addr));
|
||||
free (args);
|
||||
free (method_name);
|
||||
}
|
||||
|
||||
@@ -2565,7 +2800,9 @@ static void core_apply_ic_class(RCore *core, const DartClassInfo *ci, const char
|
||||
if (!core || !ci || R_STR_ISEMPTY (class_name)) {
|
||||
return;
|
||||
}
|
||||
core_apply_ic_command (core, r_str_newf ("ic+%s @ 0", class_name));
|
||||
char *cargs = dart_class_ic_args (ci);
|
||||
core_apply_ic_command (core, r_str_newf ("ic+%s%s @ 0", class_name, cargs));
|
||||
free (cargs);
|
||||
if (R_STR_ISNOTEMPTY (ci->super_class_name)) {
|
||||
char *super_name = class_export_name (ci->super_class_name);
|
||||
core_apply_ic_inheritance (core, class_name, super_name);
|
||||
@@ -2612,6 +2849,7 @@ int dart_pool_apply_classes_to_core(DartCtx *ctx) {
|
||||
int method_count = 0;
|
||||
int type_field_count = 0;
|
||||
int anal_method_count = 0;
|
||||
r_core_cmd0 (ctx->core, "e bin.lang=dart");
|
||||
RListIter *it;
|
||||
DartClassInfo *ci;
|
||||
r_list_foreach (classes, it, ci) {
|
||||
|
||||
@@ -16,22 +16,6 @@ void free_dart_string(void *p) {
|
||||
}
|
||||
}
|
||||
|
||||
static void free_dart_class(void *p) {
|
||||
DartClass *dc = (DartClass *)p;
|
||||
if (dc) {
|
||||
free (dc->name);
|
||||
free (dc);
|
||||
}
|
||||
}
|
||||
|
||||
static void free_dart_func(void *p) {
|
||||
DartPoolFunction *df = (DartPoolFunction *)p;
|
||||
if (df) {
|
||||
free (df->name);
|
||||
free (df);
|
||||
}
|
||||
}
|
||||
|
||||
int decode_string_cluster(ClusterStream *s, DartCtx *ctx, ut64 *ref_counter, bool is_canonical) {
|
||||
(void)is_canonical;
|
||||
ut64 count = 0;
|
||||
@@ -98,88 +82,6 @@ int decode_string_cluster(ClusterStream *s, DartCtx *ctx, ut64 *ref_counter, boo
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int decode_class_cluster(ClusterStream *s, DartCtx *ctx, ut64 *ref_counter, bool is_canonical) {
|
||||
(void)is_canonical;
|
||||
ut64 count = 0;
|
||||
if (!cs_read_unsigned (s, &count)) {
|
||||
return -1;
|
||||
}
|
||||
if (count == 0 || count > 50000) {
|
||||
return 0;
|
||||
}
|
||||
if (ctx->verbose > 1) {
|
||||
fprintf (stderr, "[r2flutter] Class cluster: count=%" PRIu64 "\n", count);
|
||||
}
|
||||
for (ut64 i = 0; i < count; i++) {
|
||||
DartClass *dc = R_NEW0 (DartClass);
|
||||
dc->ref_id = (*ref_counter)++;
|
||||
uint32_t instance_size = 0;
|
||||
cs_read_u32 (s, &instance_size);
|
||||
dc->instance_size = (int)instance_size;
|
||||
ut64 name_ref = 0;
|
||||
cs_read_ref_id (s, &name_ref);
|
||||
dc->name_ref = name_ref;
|
||||
ut64 library_ref = 0;
|
||||
cs_read_ref_id (s, &library_ref);
|
||||
dc->library_ref = library_ref;
|
||||
ut64 skip_count = 6;
|
||||
for (ut64 j = 0; j < skip_count; j++) {
|
||||
ut64 dummy = 0;
|
||||
cs_read_ref_id (s, &dummy);
|
||||
}
|
||||
if (ctx->classes) {
|
||||
r_list_append (ctx->classes, dc);
|
||||
}
|
||||
if (ctx->refs && dc->ref_id < ctx->refs_count) {
|
||||
ctx->refs[dc->ref_id] = dc;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int decode_function_cluster(ClusterStream *s, DartCtx *ctx, ut64 *ref_counter, ut64 iso_instr, bool is_canonical) {
|
||||
(void)is_canonical;
|
||||
ut64 count = 0;
|
||||
if (!cs_read_unsigned (s, &count)) {
|
||||
return -1;
|
||||
}
|
||||
if (count == 0 || count > 100000) {
|
||||
return 0;
|
||||
}
|
||||
if (ctx->verbose > 1) {
|
||||
fprintf (stderr, "[r2flutter] Function cluster: count=%" PRIu64 "\n", count);
|
||||
}
|
||||
for (ut64 i = 0; i < count; i++) {
|
||||
DartPoolFunction *df = R_NEW0 (DartPoolFunction);
|
||||
df->ref_id = (*ref_counter)++;
|
||||
ut64 name_ref = 0;
|
||||
cs_read_ref_id (s, &name_ref);
|
||||
df->name_ref = name_ref;
|
||||
ut64 owner_ref = 0;
|
||||
cs_read_ref_id (s, &owner_ref);
|
||||
df->owner_ref = owner_ref;
|
||||
ut64 code_idx = 0;
|
||||
cs_read_unsigned (s, &code_idx);
|
||||
if (code_idx > 0 && iso_instr > 0) {
|
||||
df->entry_point = iso_instr + (code_idx - 1) * 4;
|
||||
}
|
||||
ut64 skip_refs = 4;
|
||||
for (ut64 j = 0; j < skip_refs; j++) {
|
||||
ut64 dummy = 0;
|
||||
cs_read_ref_id (s, &dummy);
|
||||
}
|
||||
uint32_t kind_tag = 0;
|
||||
cs_read_u32 (s, &kind_tag);
|
||||
if (ctx->functions) {
|
||||
r_list_append (ctx->functions, df);
|
||||
}
|
||||
if (ctx->refs && df->ref_id < ctx->refs_count) {
|
||||
ctx->refs[df->ref_id] = df;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void skip_generic_cluster(ClusterStream *stream) {
|
||||
ut64 count = 0;
|
||||
if (cs_read_unsigned (stream, &count)) {
|
||||
@@ -198,99 +100,3 @@ void skip_generic_cluster(ClusterStream *stream) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int deserialize_clusters(DartCtx *ctx, ut64 cluster_start, ut64 cluster_end, ut64 num_clusters, ut64 iso_instr) {
|
||||
if (!ctx || !ctx->core || cluster_start >= cluster_end) {
|
||||
return -1;
|
||||
}
|
||||
ctx->strings = r_list_newf (free_dart_string);
|
||||
ctx->classes = r_list_newf (free_dart_class);
|
||||
ctx->functions = r_list_newf (free_dart_func);
|
||||
ut64 total_refs = ctx->num_base_objects + ctx->num_objects + 16;
|
||||
ctx->refs_count = total_refs;
|
||||
ctx->refs = (void **)calloc (total_refs, sizeof (void *));
|
||||
ClusterStream stream = {
|
||||
.ctx = ctx,
|
||||
.cursor = cluster_start,
|
||||
.end = cluster_end
|
||||
};
|
||||
ut64 ref_counter = ctx->num_base_objects + 1;
|
||||
for (ut64 ci = 0; ci < num_clusters && stream.cursor < stream.end; ci++) {
|
||||
uint32_t tags = 0;
|
||||
if (!cs_read_u32 (&stream, &tags)) {
|
||||
if (ctx->verbose > 0) {
|
||||
fprintf (stderr, "[r2flutter] Failed to read cluster tag at %" PRIu64 "\n", ci);
|
||||
}
|
||||
break;
|
||||
}
|
||||
uint32_t cid = (tags >> 12) & 0xFFFFF;
|
||||
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);
|
||||
}
|
||||
int rc = 0;
|
||||
switch (cid) {
|
||||
case kOneByteStringCid:
|
||||
case kTwoByteStringCid:
|
||||
case kStringCid:
|
||||
rc = decode_string_cluster (&stream, ctx, &ref_counter, is_canonical);
|
||||
break;
|
||||
case kClassCid:
|
||||
rc = decode_class_cluster (&stream, ctx, &ref_counter, is_canonical);
|
||||
break;
|
||||
case kFunctionCid:
|
||||
rc = decode_function_cluster (&stream, ctx, &ref_counter, iso_instr, is_canonical);
|
||||
break;
|
||||
default:
|
||||
skip_generic_cluster (&stream);
|
||||
break;
|
||||
}
|
||||
if (rc < 0) {
|
||||
if (ctx->verbose > 0) {
|
||||
fprintf (stderr, "[r2flutter] Error decoding cluster cid=%u\n", cid);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ctx->verbose > 0) {
|
||||
fprintf (stderr, "[r2flutter] Decoded: strings=%d classes=%d functions=%d\n", ctx->strings? r_list_length (ctx->strings): 0, ctx->classes? r_list_length (ctx->classes): 0, ctx->functions? r_list_length (ctx->functions): 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void resolve_names(DartCtx *ctx) {
|
||||
if (!ctx || !ctx->refs) {
|
||||
return;
|
||||
}
|
||||
if (ctx->classes) {
|
||||
RListIter *it;
|
||||
DartClass *dc;
|
||||
r_list_foreach (ctx->classes, it, dc) {
|
||||
if (!dc || dc->name) {
|
||||
continue;
|
||||
}
|
||||
if (dc->name_ref > 0 && dc->name_ref < ctx->refs_count) {
|
||||
DartString *ds = (DartString *)ctx->refs[dc->name_ref];
|
||||
if (ds && ds->value) {
|
||||
dc->name = strdup (ds->value);
|
||||
dart_obf_apply (ctx, &dc->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ctx->functions) {
|
||||
RListIter *it;
|
||||
DartPoolFunction *df;
|
||||
r_list_foreach (ctx->functions, it, df) {
|
||||
if (!df || df->name) {
|
||||
continue;
|
||||
}
|
||||
if (df->name_ref > 0 && df->name_ref < ctx->refs_count) {
|
||||
DartString *ds = (DartString *)ctx->refs[df->name_ref];
|
||||
if (ds && ds->value) {
|
||||
df->name = strdup (ds->value);
|
||||
dart_obf_apply (ctx, &df->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,8 @@ 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) {
|
||||
const int cid_field = dart_cid_get (ctx->layout, DART_CID_FIELD);
|
||||
if (cid_field < 0 || (int)dart_cid_from_object_header (header) != cid_field) {
|
||||
return false;
|
||||
}
|
||||
bool use_compressed = ctx->compressed_word_size == 4;
|
||||
@@ -172,9 +158,10 @@ bool read_data_image_method(DartCtx *ctx, ut64 pos, ut64 data_start, ut64 data_e
|
||||
if (!read_mem (ctx, pos, buf, sizeof (buf))) {
|
||||
return false;
|
||||
}
|
||||
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) {
|
||||
// No Function id for this layout means we cannot tell a Function object from
|
||||
// anything else; report nothing rather than match against a guessed id.
|
||||
const int cid_function = dart_cid_get (ctx->layout, DART_CID_FUNCTION);
|
||||
if (cid_function < 0 || (int)dart_cid_from_object_header (r_read_le64 (buf)) != cid_function) {
|
||||
return false;
|
||||
}
|
||||
ut64 entry = r_read_le64 (buf + fl->entry_off);
|
||||
|
||||
+190
-32
@@ -3,6 +3,21 @@
|
||||
#include "dart_pool_parse_priv.h"
|
||||
|
||||
static bool looks_like_data_snapshot(DartCtx *ctx, ut64 base, ut64 *out_total_len) {
|
||||
DartSnapshotHeader outer;
|
||||
if (!dart_snapshot_fingerprint_read (ctx, base, &outer)) {
|
||||
return false;
|
||||
}
|
||||
if (!outer.kind || outer.kind > 4) {
|
||||
return false;
|
||||
}
|
||||
const bool known_layout = dart_version_from_hash (outer.hash) != NULL ||
|
||||
R_STR_ISNOTEMPTY (ctx? ctx->dart_version_override: NULL);
|
||||
if (!known_layout) {
|
||||
if (out_total_len) {
|
||||
*out_total_len = outer.total_len;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
DartSnapshotHeader hdr;
|
||||
if (!dart_snapshot_header_read (ctx, base, &hdr)) {
|
||||
return false;
|
||||
@@ -22,6 +37,16 @@ static bool looks_like_data_snapshot(DartCtx *ctx, ut64 base, ut64 *out_total_le
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool dart_snapshot_is_single(DartCtx *ctx, ut64 base) {
|
||||
DartSnapshotHeader outer;
|
||||
if (!base || !dart_snapshot_fingerprint_read (ctx, base, &outer)) {
|
||||
return false;
|
||||
}
|
||||
const char *version = dart_version_from_hash (outer.hash);
|
||||
const DartVerLayout *layout = version? dart_profile_from_version (version): NULL;
|
||||
return layout && layout->single_snapshot;
|
||||
}
|
||||
|
||||
static void pick_vm_iso_by_size(const ut64 *addrs, const ut64 *lens, int count, ut64 *vm_out, ut64 *iso_out) {
|
||||
if (count < 1) {
|
||||
return;
|
||||
@@ -48,6 +73,18 @@ static void pick_vm_iso_by_size(const ut64 *addrs, const ut64 *lens, int count,
|
||||
}
|
||||
}
|
||||
|
||||
static ut64 rebase_bin_addr(DartCtx *ctx, ut64 addr) {
|
||||
if (!ctx || !ctx->core || !ctx->core->bin || !ctx->core->io || !addr) {
|
||||
return addr;
|
||||
}
|
||||
ut64 baddr = r_bin_get_baddr (ctx->core->bin);
|
||||
if (!baddr || addr >= baddr || baddr > UT64_MAX - addr) {
|
||||
return addr;
|
||||
}
|
||||
ut64 rebased = baddr + addr;
|
||||
return r_io_v2p (ctx->core->io, rebased) != UT64_MAX? rebased: addr;
|
||||
}
|
||||
|
||||
static int collect_snapshot_magics_in_range(DartCtx *ctx, ut64 start, ut64 size, ut64 *found_addrs, int found_cap, int found_cnt) {
|
||||
if (!ctx || !found_addrs || found_cap <= 0 || found_cnt >= found_cap || size < 4) {
|
||||
return found_cnt;
|
||||
@@ -61,8 +98,22 @@ static int collect_snapshot_magics_in_range(DartCtx *ctx, ut64 start, ut64 size,
|
||||
}
|
||||
for (int j = 0; j <= toread - 4; j += 4) {
|
||||
uint32_t val = r_read_le32 (buf + j);
|
||||
if (val == DART_SNAPSHOT_MAGIC) {
|
||||
found_addrs[found_cnt++] = addr + j;
|
||||
if (val != DART_SNAPSHOT_MAGIC) {
|
||||
continue;
|
||||
}
|
||||
const ut64 hit = addr + j;
|
||||
// The read window overlaps the previous chunk by 16 bytes so a magic
|
||||
// straddling a boundary is not missed; dedup so those overlap bytes
|
||||
// (and Mach-O segment/section double-coverage) don't count twice.
|
||||
bool dup = false;
|
||||
for (int d = 0; d < found_cnt; d++) {
|
||||
if (found_addrs[d] == hit) {
|
||||
dup = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!dup) {
|
||||
found_addrs[found_cnt++] = hit;
|
||||
if (found_cnt >= found_cap) {
|
||||
return found_cnt;
|
||||
}
|
||||
@@ -72,6 +123,89 @@ static int collect_snapshot_magics_in_range(DartCtx *ctx, ut64 start, ut64 size,
|
||||
return found_cnt;
|
||||
}
|
||||
|
||||
// A Dart Image starts with a kObjectStartAlignment (64)-byte header whose first
|
||||
// host word is ImageSize (header + payload) and second is
|
||||
// InstructionsSectionOffset. Bare-instructions AOT lays the two instruction
|
||||
// images consecutively at the start of the first executable section:
|
||||
// [VmInstructions][IsolateInstructions], each padded up to this alignment.
|
||||
#define DART_IMAGE_ALIGN 64
|
||||
#define DART_IMAGE_HEADER_MIN 64
|
||||
|
||||
static ut64 dart_align_up(ut64 v, ut64 a) {
|
||||
return (v + (a - 1)) & ~ (a - 1);
|
||||
}
|
||||
|
||||
// Read an Image header at addr and return its ImageSize in *size_out, true only
|
||||
// if it is a plausible instructions image: it fits before the first data image
|
||||
// and its InstructionsSection offset (word 1) is a small nonzero aligned value
|
||||
// inside the image.
|
||||
static bool dart_image_at(DartCtx *ctx, ut64 addr, ut64 data_lo, ut64 *size_out) {
|
||||
ut8 hdr[16];
|
||||
if (!read_mem (ctx, addr, hdr, sizeof (hdr))) {
|
||||
return false;
|
||||
}
|
||||
ut64 size = r_read_le64 (hdr), isoff = r_read_le64 (hdr + 8);
|
||||
*size_out = size;
|
||||
return size >= DART_IMAGE_HEADER_MIN && addr + size <= data_lo &&
|
||||
isoff >= 8 && (isoff & 7) == 0 && isoff < size && isoff < 0x10000;
|
||||
}
|
||||
|
||||
// When the four kDart* symbols are stripped, the magic scan can only find the
|
||||
// data snapshots (instruction images carry no magic). Recover vm_instr/iso_instr
|
||||
// structurally: vm_instr is the start of the first executable section, and the
|
||||
// isolate instructions image follows the VM one and ends where the first data
|
||||
// image begins. Every step is cross-checked; if anything is off we leave the
|
||||
// addresses at 0, so a wrong/unknown layout degrades to "no IT names" rather
|
||||
// than junk addresses. No-op when symbols already resolved the instructions.
|
||||
static void locate_instr_images_structural(DartCtx *ctx) {
|
||||
if (!ctx || !ctx->core || !ctx->core->bin || ctx->vm_instr || ctx->iso_instr) {
|
||||
return;
|
||||
}
|
||||
ut64 data_lo = ctx->vm_data && ctx->iso_data
|
||||
? R_MIN (ctx->vm_data, ctx->iso_data)
|
||||
: (ctx->vm_data? ctx->vm_data: ctx->iso_data);
|
||||
if (!data_lo) {
|
||||
return;
|
||||
}
|
||||
RVecRBinSection *sections = r_bin_get_sections_vec (ctx->core->bin);
|
||||
if (!sections) {
|
||||
return;
|
||||
}
|
||||
ut64 vm_instr = 0, sz1 = 0, sz2 = 0;
|
||||
RBinSection *sec;
|
||||
R_VEC_FOREACH (sections, sec) {
|
||||
if (sec && !sec->is_segment && sec->vaddr && sec->vsize && (sec->perm & R_PERM_X)) {
|
||||
vm_instr = rebase_bin_addr (ctx, sec->vaddr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!vm_instr || vm_instr >= data_lo || !dart_image_at (ctx, vm_instr, data_lo, &sz1)) {
|
||||
return;
|
||||
}
|
||||
if (dart_snapshot_is_single (ctx, ctx->iso_data? ctx->iso_data: ctx->vm_data)) {
|
||||
ctx->iso_instr = vm_instr;
|
||||
if (ctx->verbose > 0) {
|
||||
fprintf (stderr, "[r2flutter] located single instruction image structurally: snapshot_instr=0x%" PFMT64x "\n", (ut64)vm_instr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
ut64 iso_instr = dart_align_up (vm_instr + sz1, DART_IMAGE_ALIGN);
|
||||
if (iso_instr <= vm_instr || iso_instr >= data_lo || !dart_image_at (ctx, iso_instr, data_lo, &sz2)) {
|
||||
return;
|
||||
}
|
||||
// The isolate instructions image is the last text image: it must end right
|
||||
// where the first data image begins (within a page of alignment slack).
|
||||
ut64 iso_end = dart_align_up (iso_instr + sz2, DART_IMAGE_ALIGN);
|
||||
if (iso_end > data_lo || data_lo - iso_end > 0x4000) {
|
||||
return;
|
||||
}
|
||||
ctx->vm_instr = vm_instr;
|
||||
ctx->iso_instr = iso_instr;
|
||||
if (ctx->verbose > 0) {
|
||||
fprintf (stderr, "[r2flutter] located instruction images structurally: vm_instr=0x%" PFMT64x " iso_instr=0x%" PFMT64x "\n", (ut64)vm_instr, (ut64)iso_instr);
|
||||
}
|
||||
}
|
||||
|
||||
int find_snapshots(DartCtx *ctx) {
|
||||
if (!ctx || !ctx->core) {
|
||||
return -1;
|
||||
@@ -92,6 +226,8 @@ int find_snapshots(DartCtx *ctx) {
|
||||
"_kDartIsolateSnapshotInstructions",
|
||||
"DartIsolateSnapshotInstructions",
|
||||
};
|
||||
const char *single_data_names[2] = { "_kDartSnapshotData", "DartSnapshotData" };
|
||||
const char *single_text_names[2] = { "_kDartSnapshotText", "DartSnapshotText" };
|
||||
ut64 *outs[4] = { &ctx->vm_data, &ctx->vm_instr, &ctx->iso_data, &ctx->iso_instr };
|
||||
if (core->bin) {
|
||||
RVecRBinSymbol *v = r_bin_get_symbols_vec (core->bin);
|
||||
@@ -106,9 +242,20 @@ int find_snapshots(DartCtx *ctx) {
|
||||
continue;
|
||||
}
|
||||
for (int k = 0; k < 8; k++) {
|
||||
if (!strcmp (nm, names[k])) {
|
||||
// Only assign when this symbol carries an address: Mach-O
|
||||
// often lists a name twice and a later vaddr==0 duplicate
|
||||
// must not erase the good value we already resolved.
|
||||
if (!strcmp (nm, names[k]) && sym->vaddr) {
|
||||
int idx = k / 2;
|
||||
*outs[idx] = sym->vaddr? sym->vaddr: 0;
|
||||
*outs[idx] = rebase_bin_addr (ctx, sym->vaddr);
|
||||
}
|
||||
}
|
||||
for (int k = 0; k < 2; k++) {
|
||||
if (!strcmp (nm, single_data_names[k]) && sym->vaddr) {
|
||||
ctx->iso_data = rebase_bin_addr (ctx, sym->vaddr);
|
||||
}
|
||||
if (!strcmp (nm, single_text_names[k]) && sym->vaddr) {
|
||||
ctx->iso_instr = rebase_bin_addr (ctx, sym->vaddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,6 +264,9 @@ int find_snapshots(DartCtx *ctx) {
|
||||
if (ctx->vm_data && ctx->vm_instr && ctx->iso_data && ctx->iso_instr) {
|
||||
return 0;
|
||||
}
|
||||
if (ctx->iso_data && ctx->iso_instr && dart_snapshot_is_single (ctx, ctx->iso_data)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
RVecRBinSection *sections = r_bin_get_sections_vec (core->bin);
|
||||
ut64 found_addrs[32];
|
||||
@@ -127,7 +277,12 @@ int find_snapshots(DartCtx *ctx) {
|
||||
if (!sec || !sec->vaddr || !sec->vsize) {
|
||||
continue;
|
||||
}
|
||||
ut64 vaddr = sec->vaddr;
|
||||
// r_bin_get_sections_vec returns both segments and their child
|
||||
// sections on Mach-O; scanning both covers the same bytes twice.
|
||||
if (sec->is_segment) {
|
||||
continue;
|
||||
}
|
||||
ut64 vaddr = rebase_bin_addr (ctx, sec->vaddr);
|
||||
ut64 size = sec->vsize;
|
||||
if (ctx->verbose > 0) {
|
||||
fprintf (stderr, "[r2flutter] scanning section '%s' vaddr=0x%" PFMT64x " size=0x%" PFMT64x "\n", sec->name? sec->name: "(null)", (ut64)vaddr, (ut64)size);
|
||||
@@ -148,40 +303,43 @@ int find_snapshots(DartCtx *ctx) {
|
||||
}
|
||||
}
|
||||
if (found_cnt >= 1) {
|
||||
// Only data snapshots carry the snapshot magic; instruction images do
|
||||
// not. The old code classified every non-data magic hit as an
|
||||
// "instructions" snapshot and fed its bogus total_len into the vm/iso
|
||||
// picker, so a stray f5f5dcdc byte run could poison vm_instr/iso_instr.
|
||||
// Derive only the data snapshots here; instruction images come from
|
||||
// symbols (and, once implemented, structural section location).
|
||||
ut64 data_addrs[4];
|
||||
ut64 data_lens[4];
|
||||
int data_cnt = 0;
|
||||
ut64 instr_addrs[4];
|
||||
ut64 instr_lens[4];
|
||||
int instr_cnt = 0;
|
||||
for (int i = 0; i < found_cnt; i++) {
|
||||
ut8 hdr2[16];
|
||||
if (!read_mem (ctx, found_addrs[i] + 4, hdr2, sizeof (hdr2))) {
|
||||
continue;
|
||||
}
|
||||
ut64 total_len = r_read_le64 (hdr2) + 4;
|
||||
ut64 classified_len = 0;
|
||||
bool is_data = looks_like_data_snapshot (ctx, found_addrs[i], &classified_len);
|
||||
if (is_data) {
|
||||
if (data_cnt < 4) {
|
||||
data_addrs[data_cnt] = found_addrs[i];
|
||||
data_lens[data_cnt] = total_len;
|
||||
data_cnt++;
|
||||
}
|
||||
} else {
|
||||
if (instr_cnt < 4) {
|
||||
instr_addrs[instr_cnt] = found_addrs[i];
|
||||
instr_lens[instr_cnt] = total_len;
|
||||
instr_cnt++;
|
||||
}
|
||||
for (int i = 0; i < found_cnt && data_cnt < 4; i++) {
|
||||
ut64 total_len = 0;
|
||||
if (looks_like_data_snapshot (ctx, found_addrs[i], &total_len)) {
|
||||
data_addrs[data_cnt] = found_addrs[i];
|
||||
data_lens[data_cnt] = total_len;
|
||||
data_cnt++;
|
||||
}
|
||||
}
|
||||
if (data_cnt >= 1) {
|
||||
pick_vm_iso_by_size (data_addrs, data_lens, data_cnt, &ctx->vm_data, &ctx->iso_data);
|
||||
}
|
||||
if (instr_cnt >= 1) {
|
||||
pick_vm_iso_by_size (instr_addrs, instr_lens, instr_cnt, &ctx->vm_instr, &ctx->iso_instr);
|
||||
// Preserve any addresses already resolved from symbols; only fill
|
||||
// the ones still unset so scan guesses cannot overwrite good values.
|
||||
ut64 scan_vm_data = 0;
|
||||
ut64 scan_iso_data = 0;
|
||||
if (data_cnt == 1 && dart_snapshot_is_single (ctx, data_addrs[0])) {
|
||||
scan_iso_data = data_addrs[0];
|
||||
} else {
|
||||
pick_vm_iso_by_size (data_addrs, data_lens, data_cnt, &scan_vm_data, &scan_iso_data);
|
||||
}
|
||||
if (!ctx->vm_data) {
|
||||
ctx->vm_data = scan_vm_data;
|
||||
}
|
||||
if (!ctx->iso_data) {
|
||||
ctx->iso_data = scan_iso_data;
|
||||
}
|
||||
}
|
||||
// Symbols are absent (we reached the scan): try to recover the
|
||||
// instruction images from the section layout so IT name recovery works.
|
||||
locate_instr_images_structural (ctx);
|
||||
if (ctx->vm_data || ctx->iso_data || ctx->vm_instr || ctx->iso_instr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
+21
-17
@@ -23,6 +23,16 @@ static char *read_it_name(DartCtx *ctx, ut64 addr) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *resolve_it_entry_signature(DartCtx *ctx, const DartInstructionTableEntry *entry) {
|
||||
if (ctx && ctx->signature_by_code_index && entry->has_code && entry->code_index < ctx->signature_by_code_index_count) {
|
||||
const char *sig = ctx->signature_by_code_index[entry->code_index];
|
||||
if (R_STR_ISNOTEMPTY (sig)) {
|
||||
return strdup (sig);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *resolve_it_entry_name(DartCtx *ctx, HtUP *sym_by_addr, ut64 data_image_base, const DartInstructionTableEntry *entry) {
|
||||
char *out = NULL;
|
||||
if (ctx && ctx->name_by_code_index && entry->has_code && entry->code_index < ctx->name_by_code_index_count) {
|
||||
@@ -31,7 +41,10 @@ static char *resolve_it_entry_name(DartCtx *ctx, HtUP *sym_by_addr, ut64 data_im
|
||||
out = strdup (ns);
|
||||
}
|
||||
}
|
||||
if (sym_by_addr) {
|
||||
// A stripped Mach-O/ELF often contributes synthetic func.<address>
|
||||
// symbols for every function start. Keep a Dart-derived name when the
|
||||
// cluster graph supplied one; RBin is only a fallback for unmapped slots.
|
||||
if (!out && sym_by_addr) {
|
||||
RBinSymbol *bs = (RBinSymbol *)ht_up_find (sym_by_addr, entry->address, NULL);
|
||||
if (bs && bs->name) {
|
||||
const char *resolved = r_bin_name_tostring (bs->name);
|
||||
@@ -147,22 +160,9 @@ 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 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) {
|
||||
const ut32 cid = dart_cid_from_object_header (tags);
|
||||
const int string_cid = dart_cid_get (ctx->layout, DART_CID_ONE_BYTE_STRING);
|
||||
if (string_cid < 0 || (int)cid != string_cid) {
|
||||
return false;
|
||||
}
|
||||
ut64 len_smi = r_read_le64 (objhdr + 8);
|
||||
@@ -402,8 +402,10 @@ int dart_it_emit_fixed(const DartItEmitRequest *req) {
|
||||
.has_code = has_code,
|
||||
};
|
||||
entry.name = resolve_it_entry_name (ctx, req->sym_by_addr, req->data_image_base, &entry);
|
||||
entry.signature = resolve_it_entry_signature (ctx, &entry);
|
||||
emit_it_entry_record (&entry, req);
|
||||
free (entry.name);
|
||||
free (entry.signature);
|
||||
emitted++;
|
||||
}
|
||||
return 0;
|
||||
@@ -462,8 +464,10 @@ int dart_it_emit_varint(const DartItEmitRequest *req) {
|
||||
.has_code = has_code,
|
||||
};
|
||||
entry.name = resolve_it_entry_name (ctx, req->sym_by_addr, req->data_image_base, &entry);
|
||||
entry.signature = resolve_it_entry_signature (ctx, &entry);
|
||||
emit_it_entry_record (&entry, req);
|
||||
free (entry.name);
|
||||
free (entry.signature);
|
||||
emitted++;
|
||||
}
|
||||
return 0;
|
||||
|
||||
+2695
-600
File diff suppressed because it is too large.
Load diff
@@ -60,10 +60,88 @@ static bool read_heap_ptr(DartCtx *ctx, ut64 addr, ut64 data_image_base, ut64 *o
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns 1 for a decoded text string, 0 when addr is not a recognised 64-bit
|
||||
// string object, and -1 for a recognised string whose payload is not text.
|
||||
static int try_read_dart_string_object64(DartCtx *ctx, ut64 addr, char *out, int outsz) {
|
||||
if (!ctx || !ctx->layout || ctx->compressed_word_size != 8) {
|
||||
return false;
|
||||
}
|
||||
ut8 hdr[16];
|
||||
if (!read_mem (ctx, addr, hdr, sizeof (hdr))) {
|
||||
return false;
|
||||
}
|
||||
const ut32 cid = dart_cid_from_object_header (r_read_le64 (hdr));
|
||||
const int one_byte_cid = dart_cid_get (ctx->layout, DART_CID_ONE_BYTE_STRING);
|
||||
const int two_byte_cid = dart_cid_get (ctx->layout, DART_CID_TWO_BYTE_STRING);
|
||||
const bool is_two_byte = two_byte_cid >= 0 && cid == (ut32)two_byte_cid;
|
||||
if (!is_two_byte && (one_byte_cid < 0 || cid != (ut32)one_byte_cid)) {
|
||||
return false;
|
||||
}
|
||||
const ut64 length_smi = r_read_le64 (hdr + 8);
|
||||
if ((length_smi & 1) != 0) {
|
||||
return -1;
|
||||
}
|
||||
const ut64 length = length_smi >> 1;
|
||||
if (!length || length > 1024) {
|
||||
return -1;
|
||||
}
|
||||
const ut64 bytes_len = is_two_byte? length * 2: length;
|
||||
ut8 *bytes = (ut8 *)malloc ((size_t)bytes_len);
|
||||
if (!bytes || !read_mem (ctx, addr + 16, bytes, (int)bytes_len)) {
|
||||
free (bytes);
|
||||
return -1;
|
||||
}
|
||||
if (is_two_byte) {
|
||||
for (ut64 i = 0; i < length; i++) {
|
||||
const ut16 code = r_read_le16 (bytes + (i * 2));
|
||||
if (code < 0x20 || (code >= 0x7f && code < 0xa0)) {
|
||||
free (bytes);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
char *utf8 = dart_utf16le_to_utf8 (bytes, bytes_len);
|
||||
free (bytes);
|
||||
if (R_STR_ISEMPTY (utf8)) {
|
||||
free (utf8);
|
||||
return -1;
|
||||
}
|
||||
r_str_ncpy (out, utf8, outsz);
|
||||
free (utf8);
|
||||
return true;
|
||||
}
|
||||
int written = 0;
|
||||
for (ut64 i = 0; i < length; i++) {
|
||||
const ut8 ch = bytes[i];
|
||||
if (ch < 0x20 || (ch >= 0x7f && ch < 0xa0)) {
|
||||
free (bytes);
|
||||
return -1;
|
||||
}
|
||||
if (ch < 0x80) {
|
||||
if (written + 1 >= outsz) {
|
||||
break;
|
||||
}
|
||||
out[written++] = (char)ch;
|
||||
} else {
|
||||
if (written + 2 >= outsz) {
|
||||
break;
|
||||
}
|
||||
out[written++] = (char) (0xc0 | (ch >> 6));
|
||||
out[written++] = (char) (0x80 | (ch & 0x3f));
|
||||
}
|
||||
}
|
||||
out[written] = '\0';
|
||||
free (bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool try_read_dart_string(DartCtx *ctx, ut64 addr, char *out, int outsz) {
|
||||
if (!ctx || !out || outsz <= 1) {
|
||||
return false;
|
||||
}
|
||||
const int object_string = try_read_dart_string_object64 (ctx, addr, out, outsz);
|
||||
if (object_string) {
|
||||
return object_string > 0;
|
||||
}
|
||||
ut8 hdr[32];
|
||||
if (!read_mem (ctx, addr, hdr, sizeof (hdr))) {
|
||||
return false;
|
||||
@@ -299,7 +377,7 @@ void collect_data_names_with_r2(DartCtx *ctx, ut64 data_image_base, ut64 data_im
|
||||
continue;
|
||||
}
|
||||
char *line, *saveptr = NULL;
|
||||
for (line = strtok_r (out, "\n", &saveptr); line; line = strtok_r (NULL, "\n", &saveptr)) {
|
||||
for (line = r_str_tok_r (out, "\n", &saveptr); line; line = r_str_tok_r (NULL, "\n", &saveptr)) {
|
||||
if (!r_str_startswith (line, "0x")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
+442
-124
@@ -46,7 +46,7 @@ bool dart_ctx_set_profile_override(DartCtx *ctx, const char *spec) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *dart_ctx_effective_hash(DartCtx *ctx) {
|
||||
const char *dart_ctx_hash(DartCtx *ctx) {
|
||||
if (!ctx) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -59,14 +59,50 @@ const char *dart_ctx_effective_hash(DartCtx *ctx) {
|
||||
return R_STR_ISNOTEMPTY (ctx->snapshot_hash_actual)? ctx->snapshot_hash_actual: NULL;
|
||||
}
|
||||
|
||||
const char *dart_ctx_effective_version(DartCtx *ctx) {
|
||||
const char *dart_ctx_version(DartCtx *ctx) {
|
||||
if (!ctx) {
|
||||
return NULL;
|
||||
}
|
||||
if (R_STR_ISNOTEMPTY (ctx->dart_version_override)) {
|
||||
return ctx->dart_version_override;
|
||||
}
|
||||
return dart_version_from_hash (dart_ctx_effective_hash (ctx));
|
||||
return dart_version_from_hash (dart_ctx_hash (ctx));
|
||||
}
|
||||
|
||||
// Classify how the layout was determined, for honest reporting: exact hash,
|
||||
// user override, structural probe, conservative fingerprint, or no snapshot.
|
||||
static void dart_ctx_set_version_source(DartCtx *ctx) {
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
if (R_STR_ISNOTEMPTY (ctx->dart_version_override)) {
|
||||
ctx->version_source = DART_VERSION_SOURCE_OVERRIDE;
|
||||
} else if (dart_version_from_hash (dart_ctx_hash (ctx))) {
|
||||
ctx->version_source = DART_VERSION_SOURCE_EXACT;
|
||||
} else if (ctx->layout_probed) {
|
||||
ctx->version_source = DART_VERSION_SOURCE_PROBE;
|
||||
} else if (R_STR_ISNOTEMPTY (dart_ctx_hash (ctx))) {
|
||||
ctx->version_source = DART_VERSION_SOURCE_FINGERPRINT;
|
||||
} else {
|
||||
ctx->version_source = DART_VERSION_SOURCE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
// Effective version label including the evidence used for an unknown hash.
|
||||
static const char *dart_ctx_version_label(DartCtx *ctx, char *buf, size_t bufsz) {
|
||||
const char *v = dart_ctx_version (ctx);
|
||||
if (v) {
|
||||
return v;
|
||||
}
|
||||
if (ctx && ctx->layout && ctx->layout->dart_version && ctx->version_source == DART_VERSION_SOURCE_PROBE) {
|
||||
snprintf (buf, bufsz, "~%s+ (structural-probe)", ctx->layout->dart_version);
|
||||
return buf;
|
||||
}
|
||||
if (ctx && ctx->layout && ctx->layout->dart_version && ctx->version_source == DART_VERSION_SOURCE_FINGERPRINT) {
|
||||
snprintf (buf, bufsz, "~%s+ (fingerprint)", ctx->layout->dart_version);
|
||||
return buf;
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
static const char *dart_tag_style_names[] = {
|
||||
@@ -103,12 +139,12 @@ static const DartVerLayout *load_layout_from_json(const char *hash, DartVerLayou
|
||||
}
|
||||
const char *version = dart_version_from_hash (hash);
|
||||
const DartVerLayout *base_profile = version? dart_profile_from_version (version): NULL;
|
||||
if (base_profile) {
|
||||
memcpy (out, base_profile, sizeof (*out));
|
||||
} else {
|
||||
memset (out, 0, sizeof (*out));
|
||||
out->tag_style = DART_TAG_STYLE_OBJECT_HEADER;
|
||||
if (!base_profile) {
|
||||
// Keep discovery and decoding on the same conservative profile. A newer
|
||||
// single-snapshot layout requires an exact hash or structural probing.
|
||||
base_profile = dart_fingerprint_profile ();
|
||||
}
|
||||
memcpy (out, base_profile, sizeof (*out));
|
||||
r_str_ncpy (out->hash, hash, sizeof (out->hash));
|
||||
for (int i = 0; i < DART_HASH_ENTRIES_COUNT; i++) {
|
||||
if (!strcmp (dart_hash_entries[i].hash, hash)) {
|
||||
@@ -125,7 +161,7 @@ static DartVerLayout *dart_layout_new_from_profile(DartCtx *ctx, const DartVerLa
|
||||
}
|
||||
DartVerLayout *dvl = R_NEW0 (DartVerLayout);
|
||||
memcpy (dvl, profile, sizeof (DartVerLayout));
|
||||
const char *hash = dart_ctx_effective_hash (ctx);
|
||||
const char *hash = dart_ctx_hash (ctx);
|
||||
if (R_STR_ISNOTEMPTY (hash)) {
|
||||
r_str_ncpy (dvl->hash, hash, sizeof (dvl->hash));
|
||||
}
|
||||
@@ -139,7 +175,11 @@ static DartVerLayout *dart_pick_layout_owned_for_ctx(DartCtx *ctx) {
|
||||
if (R_STR_ISNOTEMPTY (ctx->dart_version_override)) {
|
||||
return dart_layout_new_from_profile (ctx, dart_profile_from_version (ctx->dart_version_override));
|
||||
}
|
||||
return dart_pick_layout_by_hash (dart_ctx_effective_hash (ctx));
|
||||
return dart_layout_from_hash (dart_ctx_hash (ctx));
|
||||
}
|
||||
|
||||
static ut64 dart_ctx_primary_data(const DartCtx *ctx) {
|
||||
return ctx? (ctx->vm_data? ctx->vm_data: ctx->iso_data): 0;
|
||||
}
|
||||
|
||||
static void extract_snapshot_hash_flags(DartCtx *ctx, ut64 vm_data) {
|
||||
@@ -149,7 +189,7 @@ static void extract_snapshot_hash_flags(DartCtx *ctx, ut64 vm_data) {
|
||||
ctx->snapshot_hash[0] = '\0';
|
||||
ctx->snapshot_hash_actual[0] = '\0';
|
||||
DartSnapshotHeader hdr;
|
||||
if (!dart_snapshot_header_read (ctx, vm_data, &hdr)) {
|
||||
if (!dart_snapshot_fingerprint_read (ctx, vm_data, &hdr)) {
|
||||
return;
|
||||
}
|
||||
memcpy (ctx->snapshot_hash_actual, hdr.hash, DART_SNAPSHOT_HASH_SIZE);
|
||||
@@ -164,27 +204,106 @@ static void extract_snapshot_hash_flags(DartCtx *ctx, ut64 vm_data) {
|
||||
}
|
||||
}
|
||||
|
||||
static int dart_word_size_from_flags(const char *flags) {
|
||||
const bool has_compressed = flags && strstr (flags, "compressed-pointer") != NULL;
|
||||
const bool has_no_compressed = flags && (strstr (flags, "no-compressed-pointer") != NULL || strstr (flags, "no-compressed") != NULL);
|
||||
return has_compressed && !has_no_compressed? 4: 8;
|
||||
}
|
||||
|
||||
static bool dart_probe_snapshot_with_profile(DartCtx *ctx, const DartVerLayout *profile, ut64 base, ut64 *score) {
|
||||
ctx->layout = profile;
|
||||
DartSnapshotHeader sh = { 0 };
|
||||
if (!dart_snapshot_header_read (ctx, base, &sh) || !sh.ok || !sh.nb || sh.no < sh.nb || !sh.nc ||
|
||||
sh.no > 100000000 || sh.nc > 100000 || sh.cluster_start >= base + sh.total_len) {
|
||||
return false;
|
||||
}
|
||||
const ModernReq req = {
|
||||
ctx,
|
||||
sh.cluster_start,
|
||||
base + sh.total_len,
|
||||
sh.nc,
|
||||
sh.nb
|
||||
};
|
||||
return modern_probe_snapshot (&req, sh.no, score);
|
||||
}
|
||||
|
||||
static bool dart_ctx_probe_layout(DartCtx *ctx) {
|
||||
if (!ctx || !ctx->layout || R_STR_ISNOTEMPTY (ctx->dart_version_override) ||
|
||||
R_STR_ISNOTEMPTY (ctx->snapshot_hash_override) || R_STR_ISEMPTY (ctx->snapshot_hash_actual) ||
|
||||
dart_version_from_hash (ctx->snapshot_hash_actual)) {
|
||||
return false;
|
||||
}
|
||||
const ut64 base = ctx->iso_data? ctx->iso_data: ctx->vm_data;
|
||||
DartSnapshotHeader outer = { 0 };
|
||||
if (!base || !dart_snapshot_fingerprint_read (ctx, base, &outer) || !outer.kind || outer.kind > 4) {
|
||||
return false;
|
||||
}
|
||||
const bool one_data_snapshot = !ctx->vm_data || !ctx->iso_data || ctx->vm_data == ctx->iso_data;
|
||||
const bool want_single = one_data_snapshot && outer.kind == 2;
|
||||
const DartVerLayout *target = ctx->layout;
|
||||
const int old_word_size = ctx->compressed_word_size;
|
||||
const DartVerLayout *best = NULL;
|
||||
ut64 best_score = 0;
|
||||
int candidates = 0;
|
||||
ctx->compressed_word_size = dart_word_size_from_flags (outer.flags);
|
||||
for (int i = 0; i < dart_profile_count (); i++) {
|
||||
const DartVerLayout *profile = dart_profile_at (i);
|
||||
if (!profile || profile->single_snapshot != want_single) {
|
||||
continue;
|
||||
}
|
||||
ut64 score = 0;
|
||||
const bool valid = dart_probe_snapshot_with_profile (ctx, profile, base, &score);
|
||||
if (ctx->verbose > 1) {
|
||||
fprintf (stderr, "[r2flutter] layout probe profile=%s valid=%s semantic_score=%" PRIu64 "\n", profile->dart_version, r_str_bool (valid), (uint64_t)score);
|
||||
}
|
||||
if (!valid) {
|
||||
continue;
|
||||
}
|
||||
candidates++;
|
||||
if (!best || score > best_score) {
|
||||
best = profile;
|
||||
best_score = score;
|
||||
}
|
||||
}
|
||||
ctx->layout = target;
|
||||
ctx->compressed_word_size = old_word_size;
|
||||
if (!best) {
|
||||
if (ctx->verbose > 0) {
|
||||
fprintf (stderr, "[r2flutter] layout probe found no structurally valid profile; keeping fingerprint fallback %s\n", target->dart_version);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
char hash[DART_SNAPSHOT_HASH_SIZE + 1];
|
||||
r_str_ncpy (hash, target->hash, sizeof (hash));
|
||||
memcpy ((DartVerLayout *)target, best, sizeof (*best));
|
||||
r_str_ncpy (((DartVerLayout *)target)->hash, hash, sizeof (((DartVerLayout *)target)->hash));
|
||||
ctx->layout_probed = true;
|
||||
if (best->single_snapshot && ctx->vm_data == ctx->iso_data) {
|
||||
ctx->vm_data = 0;
|
||||
if (ctx->vm_instr == ctx->iso_instr) {
|
||||
ctx->vm_instr = 0;
|
||||
}
|
||||
}
|
||||
if (ctx->verbose > 0) {
|
||||
fprintf (stderr, "[r2flutter] layout probe selected profile %s from %d valid candidate%s (semantic_score=%" PRIu64 ")\n", best->dart_version, candidates, candidates == 1? "": "s", (uint64_t)best_score);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void derive_layout_from_flags(DartCtx *ctx) {
|
||||
if (!ctx || !ctx->vm_data) {
|
||||
const ut64 data = dart_ctx_primary_data (ctx);
|
||||
if (!ctx || !data) {
|
||||
return;
|
||||
}
|
||||
DartSnapshotHeader hdr;
|
||||
if (!dart_snapshot_header_read (ctx, ctx->vm_data, &hdr)) {
|
||||
if (!dart_snapshot_header_read (ctx, data, &hdr)) {
|
||||
return;
|
||||
}
|
||||
const char *flags = hdr.flags;
|
||||
bool has_compressed = strstr (flags, "compressed-pointer") != NULL;
|
||||
bool has_no_compressed = strstr (flags, "no-compressed-pointer") != NULL ||
|
||||
strstr (flags, "no-compressed") != NULL;
|
||||
if (has_compressed && !has_no_compressed) {
|
||||
ctx->compressed_word_size = 4;
|
||||
} else {
|
||||
ctx->compressed_word_size = 8;
|
||||
}
|
||||
ctx->compressed_word_size = dart_word_size_from_flags (hdr.flags);
|
||||
if (ctx->layout && ctx->compressed_word_size == 4) {
|
||||
int major = 0;
|
||||
int minor = 0;
|
||||
const char *version = dart_ctx_effective_version (ctx);
|
||||
const char *version = dart_ctx_version (ctx);
|
||||
bool use_64_alignment = ctx->layout->tag_style == DART_TAG_STYLE_OBJECT_HEADER;
|
||||
if (!use_64_alignment && version && sscanf (version, "%d.%d", &major, &minor) == 2) {
|
||||
use_64_alignment = major > 2 || (major == 2 && minor >= 19);
|
||||
@@ -198,20 +317,24 @@ static void derive_layout_from_flags(DartCtx *ctx) {
|
||||
}
|
||||
|
||||
DartVerLayout *dart_ctx_init_layout(DartCtx *ctx, DartVerLayout *tmp) {
|
||||
extract_snapshot_hash_flags (ctx, ctx->vm_data);
|
||||
ctx->layout = R_STR_ISEMPTY (ctx->dart_version_override)? load_layout_from_json (dart_ctx_effective_hash (ctx), tmp): NULL;
|
||||
ctx->layout_probed = false;
|
||||
extract_snapshot_hash_flags (ctx, dart_ctx_primary_data (ctx));
|
||||
ctx->layout = R_STR_ISEMPTY (ctx->dart_version_override)? load_layout_from_json (dart_ctx_hash (ctx), tmp): NULL;
|
||||
DartVerLayout *owned = NULL;
|
||||
if (!ctx->layout) {
|
||||
owned = dart_pick_layout_owned_for_ctx (ctx);
|
||||
ctx->layout = owned;
|
||||
}
|
||||
(void)dart_ctx_probe_layout (ctx);
|
||||
derive_layout_from_flags (ctx);
|
||||
dart_ctx_set_version_source (ctx);
|
||||
return owned;
|
||||
}
|
||||
|
||||
void dart_ctx_fini_layout(DartCtx *ctx, DartVerLayout *owned) {
|
||||
dart_ver_layout_free (owned);
|
||||
ctx->layout = NULL;
|
||||
ctx->layout_probed = false;
|
||||
}
|
||||
|
||||
static void dart_pool_print_snapshot_json(DartCtx *ctx) {
|
||||
@@ -286,36 +409,6 @@ static int decode_pool_and_emit(DartItEmitRequest *req) {
|
||||
|
||||
const ut64 cluster_start = sh.cluster_start;
|
||||
const ut64 cluster_end = base + total_len;
|
||||
if (nc > 0 && nc < 5000 && no < 500000 && cluster_start < cluster_end) {
|
||||
const int deser_rc = deserialize_clusters (ctx, cluster_start, cluster_end, nc, ctx->iso_instr);
|
||||
if (deser_rc == 0) {
|
||||
resolve_names (ctx);
|
||||
if (ctx->functions && req->on_fn) {
|
||||
RListIter *fit;
|
||||
DartPoolFunction *df;
|
||||
r_list_foreach (ctx->functions, fit, df) {
|
||||
if (!df || df->entry_point == 0) {
|
||||
continue;
|
||||
}
|
||||
char *clean_name = r_name_filter_dup (R_STR_ISEMPTY (df->name)? "method.unknown": df->name);
|
||||
req->on_fn (clean_name, df->entry_point, 0, req->fn_user);
|
||||
free (clean_name);
|
||||
}
|
||||
}
|
||||
if (ctx->verbose > 1 && ctx->strings) {
|
||||
RListIter *sit;
|
||||
DartString *ds;
|
||||
int str_count = 0;
|
||||
r_list_foreach (ctx->strings, sit, ds) {
|
||||
if (ds && ds->value && str_count < 50) {
|
||||
fprintf (stderr, "[r2flutter] String[%" PRIu64 "]: %s\n", ds->ref_id, ds->value);
|
||||
str_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HtUP *sym_by_addr = ht_up_new0 ();
|
||||
if (ctx->core && ctx->core->bin && sym_by_addr) {
|
||||
RVecRBinSymbol *v = r_bin_get_symbols_vec (ctx->core->bin);
|
||||
@@ -340,8 +433,7 @@ static int decode_pool_and_emit(DartItEmitRequest *req) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
const ut64 kAlign = ctx->layout && ctx->layout->max_alignment? (ut64)ctx->layout->max_alignment: 16;
|
||||
const ut64 data_image_base = base + ((total_len + (kAlign - 1)) & ~ (kAlign - 1));
|
||||
const ut64 data_image_base = dart_snapshot_data_image_base (base, total_len);
|
||||
ut64 data_image_end = ctx->iso_instr? ctx->iso_instr: (data_image_base + (1ULL << 22));
|
||||
if (data_image_end < data_image_base) {
|
||||
data_image_end = data_image_base + (1ULL << 22);
|
||||
@@ -349,8 +441,8 @@ static int decode_pool_and_emit(DartItEmitRequest *req) {
|
||||
if (ctx->verbose > 0) {
|
||||
fprintf (stderr, "[r2flutter] data_image_base=0x%" PFMT64x " data_image_end=0x%" PFMT64x "\n", (ut64)data_image_base, (ut64)data_image_end);
|
||||
}
|
||||
if (dart_modern_is_supported_snapshot (ctx)) {
|
||||
dart_modern_scan_names_from_clusters (ctx, cluster_start, cluster_end, nc, itlen);
|
||||
if (modern_supported (ctx)) {
|
||||
modern_scan_names (ctx, cluster_start, cluster_end, nc, itlen);
|
||||
}
|
||||
ctx->name_by_ep = scan_code_names (ctx, data_image_base, data_image_end);
|
||||
ctx->name_pool = collect_data_names (ctx, data_image_base, data_image_end);
|
||||
@@ -368,7 +460,9 @@ static int decode_pool_and_emit(DartItEmitRequest *req) {
|
||||
goto cleanup;
|
||||
}
|
||||
const ut64 layout_cap = ctx->layout && ctx->layout->it_cap? ctx->layout->it_cap: 20000;
|
||||
const ut64 max_entries = req->max_entries > 0? req->max_entries: (req->on_it? itlen: layout_cap);
|
||||
// itlen counts retained Code objects, not instructions whose Code metadata
|
||||
// was discarded. Let the table header bound an unlimited IT extraction.
|
||||
const ut64 max_entries = req->max_entries > 0? req->max_entries: (req->on_it? 0: layout_cap);
|
||||
req->data_image_base = data_image_base;
|
||||
req->data_image_end = data_image_end;
|
||||
req->itlen = itlen;
|
||||
@@ -402,6 +496,14 @@ cleanup:
|
||||
ctx->name_by_code_index = NULL;
|
||||
ctx->name_by_code_index_count = 0;
|
||||
}
|
||||
if (ctx->signature_by_code_index) {
|
||||
for (ut64 i = 0; i < ctx->signature_by_code_index_count; i++) {
|
||||
free (ctx->signature_by_code_index[i]);
|
||||
}
|
||||
free (ctx->signature_by_code_index);
|
||||
ctx->signature_by_code_index = NULL;
|
||||
ctx->signature_by_code_index_count = 0;
|
||||
}
|
||||
if (ctx->owner_kind_by_code_index) {
|
||||
free (ctx->owner_kind_by_code_index);
|
||||
ctx->owner_kind_by_code_index = NULL;
|
||||
@@ -441,7 +543,11 @@ static void emit_stub_symbols(DartCtx *ctx,
|
||||
if (!addr) {
|
||||
continue;
|
||||
}
|
||||
#ifdef R_BIN_ATTR_MIXIN
|
||||
ut64 size = sym->attr.size;
|
||||
#else
|
||||
ut64 size = sym->size;
|
||||
#endif
|
||||
const char *nm = sym->name? r_bin_name_tostring2 (sym->name, 'o'): NULL;
|
||||
if (!nm) {
|
||||
nm = "sym.func";
|
||||
@@ -452,19 +558,12 @@ static void emit_stub_symbols(DartCtx *ctx,
|
||||
}
|
||||
}
|
||||
|
||||
int dart_pool_enumerate(DartCtx *ctx, const char *libapp_path, DartPoolFunctionCallback on_fn, void *user, ut64 *out_base, ut64 *out_heap_base) {
|
||||
(void)libapp_path;
|
||||
int dart_pool_scan(DartCtx *ctx, DartPoolFunctionCallback on_fn, void *user) {
|
||||
if (!ctx || !ctx->core) {
|
||||
return -1;
|
||||
}
|
||||
int ok = find_snapshots (ctx);
|
||||
if (ok == 0) {
|
||||
if (out_base) {
|
||||
*out_base = r_bin_get_baddr (ctx->core->bin);
|
||||
}
|
||||
if (out_heap_base) {
|
||||
*out_heap_base = 0;
|
||||
}
|
||||
if (ctx->verbose > 0) {
|
||||
eprintf ("[r2flutter] Found Dart snapshots: vm_data=0x%" PFMT64x " vm_instr=0x%" PFMT64x " iso_data=0x%" PFMT64x " iso_instr=0x%" PFMT64x "\n",
|
||||
(ut64)ctx->vm_data,
|
||||
@@ -497,13 +596,7 @@ int dart_pool_enumerate(DartCtx *ctx, const char *libapp_path, DartPoolFunctionC
|
||||
dart_ctx_fini_layout (ctx, layout_owned);
|
||||
return rc;
|
||||
}
|
||||
if (out_base) {
|
||||
*out_base = 0;
|
||||
}
|
||||
if (out_heap_base) {
|
||||
*out_heap_base = 0;
|
||||
}
|
||||
eprintf ("[r2flutter] Dart snapshots not found in symbols (file=%s).\n", libapp_path? libapp_path: "(null)");
|
||||
eprintf ("[r2flutter] Dart snapshots not found in symbols.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -554,6 +647,7 @@ void dart_instruction_table_entry_fini(DartInstructionTableEntry *ie) {
|
||||
return;
|
||||
}
|
||||
R_FREE (ie->name);
|
||||
R_FREE (ie->signature);
|
||||
}
|
||||
|
||||
void dart_instruction_table_entry_free(DartInstructionTableEntry *ie) {
|
||||
@@ -580,9 +674,12 @@ static int prepare_header_data(DartCtx *ctx) {
|
||||
if (ok != 0) {
|
||||
return -1;
|
||||
}
|
||||
extract_snapshot_hash_flags (ctx, ctx->vm_data);
|
||||
ctx->layout_probed = false;
|
||||
extract_snapshot_hash_flags (ctx, dart_ctx_primary_data (ctx));
|
||||
ctx->layout = dart_pick_layout_owned_for_ctx (ctx);
|
||||
(void)dart_ctx_probe_layout (ctx);
|
||||
derive_layout_from_flags (ctx);
|
||||
dart_ctx_set_version_source (ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -594,7 +691,9 @@ char *dart_pool_dump_header(DartCtx *ctx, int fmt) {
|
||||
return fmt == 'r'? strdup ("# Error: Dart snapshots not found\n"): strdup ("Error: Dart snapshots not found\n");
|
||||
}
|
||||
const bool quiet = ctx && ctx->quiet;
|
||||
const char *version = dart_ctx_effective_version (ctx);
|
||||
char vbuf[48];
|
||||
const char *version = dart_ctx_version_label (ctx, vbuf, sizeof (vbuf));
|
||||
const char *version_source = dart_version_source_str (ctx? ctx->version_source: DART_VERSION_SOURCE_UNKNOWN);
|
||||
if (fmt == 'j') {
|
||||
ut64 header_addr = ctx->iso_data? ctx->iso_data: ctx->vm_data;
|
||||
DartSnapshotHeader sh = { 0 };
|
||||
@@ -610,6 +709,9 @@ char *dart_pool_dump_header(DartCtx *ctx, int fmt) {
|
||||
pj_kn (pj, "vm_instr", ctx->vm_instr);
|
||||
pj_kn (pj, "iso_data", ctx->iso_data);
|
||||
pj_kn (pj, "iso_instr", ctx->iso_instr);
|
||||
if (ctx->layout && ctx->layout->single_snapshot) {
|
||||
pj_kb (pj, "single_snapshot", true);
|
||||
}
|
||||
if (ctx->container_kind[0]) {
|
||||
pj_k (pj, "container");
|
||||
pj_o (pj);
|
||||
@@ -630,7 +732,8 @@ char *dart_pool_dump_header(DartCtx *ctx, int fmt) {
|
||||
pj_kn (pj, "total", sh.total_len);
|
||||
pj_end (pj);
|
||||
pj_ki (pj, "cws", ctx->compressed_word_size);
|
||||
pj_ks (pj, "dart_version", version? version: "unknown");
|
||||
pj_ks (pj, "dart_version", version);
|
||||
pj_ks (pj, "version_source", version_source);
|
||||
if (ctx->layout) {
|
||||
const DartVerLayout *l = ctx->layout;
|
||||
pj_ks (pj, "tag_style", dart_tag_style_to_string (l->tag_style));
|
||||
@@ -656,24 +759,33 @@ char *dart_pool_dump_header(DartCtx *ctx, int fmt) {
|
||||
}
|
||||
if (fmt == 'r') {
|
||||
RStrBuf *sb = r_strbuf_new (quiet? "": "'# Dart AOT Snapshot Info\n");
|
||||
const bool single = ctx->layout && ctx->layout->single_snapshot;
|
||||
const ut64 data = dart_ctx_primary_data (ctx);
|
||||
// Create flags for snapshot addresses
|
||||
r_strbuf_appendf (sb, "'fs dart\n");
|
||||
r_strbuf_appendf (sb, "'f dart.vm_data = 0x%" PFMT64x "\n", (ut64)ctx->vm_data);
|
||||
r_strbuf_appendf (sb, "'f dart.vm_instr = 0x%" PFMT64x "\n", (ut64)ctx->vm_instr);
|
||||
r_strbuf_appendf (sb, "'f dart.iso_data = 0x%" PFMT64x "\n", (ut64)ctx->iso_data);
|
||||
r_strbuf_appendf (sb, "'f dart.iso_instr = 0x%" PFMT64x "\n", (ut64)ctx->iso_instr);
|
||||
if (single) {
|
||||
r_strbuf_appendf (sb, "'f dart.snapshot_data = 0x%" PFMT64x "\n", (ut64)ctx->iso_data);
|
||||
r_strbuf_appendf (sb, "'f dart.snapshot_text = 0x%" PFMT64x "\n", (ut64)ctx->iso_instr);
|
||||
r_strbuf_appendf (sb, "'f dart.iso_data = 0x%" PFMT64x "\n", (ut64)ctx->iso_data);
|
||||
r_strbuf_appendf (sb, "'f dart.iso_instr = 0x%" PFMT64x "\n", (ut64)ctx->iso_instr);
|
||||
} else {
|
||||
r_strbuf_appendf (sb, "'f dart.vm_data = 0x%" PFMT64x "\n", (ut64)ctx->vm_data);
|
||||
r_strbuf_appendf (sb, "'f dart.vm_instr = 0x%" PFMT64x "\n", (ut64)ctx->vm_instr);
|
||||
r_strbuf_appendf (sb, "'f dart.iso_data = 0x%" PFMT64x "\n", (ut64)ctx->iso_data);
|
||||
r_strbuf_appendf (sb, "'f dart.iso_instr = 0x%" PFMT64x "\n", (ut64)ctx->iso_instr);
|
||||
}
|
||||
if (ctx->container_kind[0]) {
|
||||
r_strbuf_appendf (sb, "'f dart.container_payload = 0x%" PFMT64x "\n", (ut64)ctx->container_payload_offset);
|
||||
r_strbuf_appendf (sb, "'f dart.container_payload_size = 0x%" PFMT64x "\n", (ut64)ctx->container_payload_size);
|
||||
}
|
||||
if (!quiet) {
|
||||
// Add comments with metadata
|
||||
r_strbuf_appendf (sb, "'@0x%" PFMT64x "'CC Dart snapshot hash: %s\n", (ut64)ctx->vm_data, ctx->snapshot_hash[0]? ctx->snapshot_hash: "(unknown)");
|
||||
r_strbuf_appendf (sb, "'@0x%" PFMT64x "'CC Dart version: %s\n", (ut64)ctx->vm_data, version? version: "unknown");
|
||||
r_strbuf_appendf (sb, "'@0x%" PFMT64x "'CC Dart snapshot hash: %s\n", data, ctx->snapshot_hash[0]? ctx->snapshot_hash: "(unknown)");
|
||||
r_strbuf_appendf (sb, "'@0x%" PFMT64x "'CC Dart version: %s (%s)\n", data, version, version_source);
|
||||
if (ctx->layout) {
|
||||
const DartVerLayout *l = ctx->layout;
|
||||
r_strbuf_appendf (sb, "'@0x%" PFMT64x "'CC Tag style: %s\n", (ut64)ctx->vm_data, dart_tag_style_to_string (l->tag_style));
|
||||
r_strbuf_appendf (sb, "'@0x%" PFMT64x "'CC Alignment: %d, CWS: %d\n", (ut64)ctx->vm_data, l->max_alignment, ctx->compressed_word_size);
|
||||
r_strbuf_appendf (sb, "'@0x%" PFMT64x "'CC Tag style: %s\n", data, dart_tag_style_to_string (l->tag_style));
|
||||
r_strbuf_appendf (sb, "'@0x%" PFMT64x "'CC Alignment: %d, CWS: %d\n", data, l->max_alignment, ctx->compressed_word_size);
|
||||
}
|
||||
}
|
||||
return r_strbuf_drain (sb);
|
||||
@@ -681,16 +793,20 @@ char *dart_pool_dump_header(DartCtx *ctx, int fmt) {
|
||||
|
||||
RStrBuf *sb = r_strbuf_new ("");
|
||||
if (quiet) {
|
||||
r_strbuf_appendf (sb, "snapshot_hash=%s dart_version=%s vm_data=0x%" PFMT64x " vm_instr=0x%" PFMT64x " iso_data=0x%" PFMT64x " iso_instr=0x%" PFMT64x " cws=%d", ctx->snapshot_hash[0]? ctx->snapshot_hash: "(unknown)", version? version: "unknown", (ut64)ctx->vm_data, (ut64)ctx->vm_instr, (ut64)ctx->iso_data, (ut64)ctx->iso_instr, ctx->compressed_word_size);
|
||||
r_strbuf_appendf (sb, "snapshot_hash=%s dart_version=%s version_source=%s vm_data=0x%" PFMT64x " vm_instr=0x%" PFMT64x " iso_data=0x%" PFMT64x " iso_instr=0x%" PFMT64x " cws=%d", ctx->snapshot_hash[0]? ctx->snapshot_hash: "(unknown)", version, version_source, (ut64)ctx->vm_data, (ut64)ctx->vm_instr, (ut64)ctx->iso_data, (ut64)ctx->iso_instr, ctx->compressed_word_size);
|
||||
if (ctx->layout) {
|
||||
const DartVerLayout *l = ctx->layout;
|
||||
r_strbuf_appendf (sb, " tag_style=%s alignment=%d", dart_tag_style_to_string (l->tag_style), l->max_alignment);
|
||||
if (l->single_snapshot) {
|
||||
r_strbuf_append (sb, " single_snapshot=true");
|
||||
}
|
||||
}
|
||||
return r_strbuf_drain (sb);
|
||||
}
|
||||
r_strbuf_appendf (sb, "# Dart AOT Snapshot Header\n\n");
|
||||
r_strbuf_appendf (sb, "snapshot_hash: %s\n", ctx->snapshot_hash[0]? ctx->snapshot_hash: "(unknown)");
|
||||
r_strbuf_appendf (sb, "dart_version: %s\n", version? version: "unknown");
|
||||
r_strbuf_appendf (sb, "dart_version: %s\n", version);
|
||||
r_strbuf_appendf (sb, "version_source: %s\n", version_source);
|
||||
if (ctx->layout) {
|
||||
const DartVerLayout *l = ctx->layout;
|
||||
r_strbuf_appendf (sb, "tag_style: %s\n", dart_tag_style_to_string_verbose (l->tag_style));
|
||||
@@ -701,10 +817,15 @@ char *dart_pool_dump_header(DartCtx *ctx, int fmt) {
|
||||
}
|
||||
|
||||
r_strbuf_appendf (sb, "\n## Snapshot Addresses\n");
|
||||
r_strbuf_appendf (sb, "vm_data: 0x%" PFMT64x "\n", (ut64)ctx->vm_data);
|
||||
r_strbuf_appendf (sb, "vm_instr: 0x%" PFMT64x "\n", (ut64)ctx->vm_instr);
|
||||
r_strbuf_appendf (sb, "iso_data: 0x%" PFMT64x "\n", (ut64)ctx->iso_data);
|
||||
r_strbuf_appendf (sb, "iso_instr: 0x%" PFMT64x "\n", (ut64)ctx->iso_instr);
|
||||
if (ctx->layout && ctx->layout->single_snapshot) {
|
||||
r_strbuf_appendf (sb, "snapshot_data: 0x%" PFMT64x "\n", (ut64)ctx->iso_data);
|
||||
r_strbuf_appendf (sb, "snapshot_text: 0x%" PFMT64x "\n", (ut64)ctx->iso_instr);
|
||||
} else {
|
||||
r_strbuf_appendf (sb, "vm_data: 0x%" PFMT64x "\n", (ut64)ctx->vm_data);
|
||||
r_strbuf_appendf (sb, "vm_instr: 0x%" PFMT64x "\n", (ut64)ctx->vm_instr);
|
||||
r_strbuf_appendf (sb, "iso_data: 0x%" PFMT64x "\n", (ut64)ctx->iso_data);
|
||||
r_strbuf_appendf (sb, "iso_instr: 0x%" PFMT64x "\n", (ut64)ctx->iso_instr);
|
||||
}
|
||||
if (ctx->container_kind[0]) {
|
||||
r_strbuf_appendf (sb, "\n## Container\n");
|
||||
r_strbuf_appendf (sb, "kind: %s\n", ctx->container_kind);
|
||||
@@ -714,8 +835,9 @@ char *dart_pool_dump_header(DartCtx *ctx, int fmt) {
|
||||
r_strbuf_appendf (sb, "macho_off: 0x%" PFMT64x "\n", (ut64)ctx->container_macho_offset);
|
||||
}
|
||||
|
||||
ut64 addrs[2] = { ctx->vm_data, ctx->iso_data };
|
||||
const char *labels[2] = { "VM", "Isolate" };
|
||||
const bool single = ctx->layout && ctx->layout->single_snapshot;
|
||||
ut64 addrs[2] = { single? ctx->iso_data: ctx->vm_data, single? 0: ctx->iso_data };
|
||||
const char *labels[2] = { single? "Combined": "VM", "Isolate" };
|
||||
for (int si = 0; si < 2; si++) {
|
||||
if (!addrs[si]) {
|
||||
continue;
|
||||
@@ -776,19 +898,24 @@ static void dump_header_snapshot_json(DartCtx *ctx, PJ *pj, const char *label, u
|
||||
pj_kn (pj, "base_objects", sh.nb);
|
||||
pj_kn (pj, "objects", sh.no);
|
||||
pj_kn (pj, "clusters_count", sh.nc);
|
||||
if (sh.ncc) {
|
||||
pj_kn (pj, "canonical_clusters_count", sh.ncc);
|
||||
}
|
||||
if (sh.field_table_len) {
|
||||
pj_kn (pj, "field_table_len", sh.field_table_len);
|
||||
}
|
||||
pj_kn (pj, "it_length", sh.itlen);
|
||||
pj_kn (pj, "it_data_off", sh.itdata);
|
||||
pj_kn (pj, "cluster_start", sh.cluster_start);
|
||||
ut64 cluster_end = addr + sh.total_len;
|
||||
pj_kn (pj, "cluster_end", cluster_end);
|
||||
if (ctx->layout) {
|
||||
ut64 align = ctx->layout->max_alignment? (ut64)ctx->layout->max_alignment: 16;
|
||||
ut64 data_image_base = addr + ((sh.total_len + (align - 1)) & ~ (align - 1));
|
||||
ut64 data_image_base = dart_snapshot_data_image_base (addr, sh.total_len);
|
||||
pj_kn (pj, "data_image_base", data_image_base);
|
||||
pj_kn (pj, "instruction_table_addr", data_image_base + sh.itdata);
|
||||
}
|
||||
pj_ka (pj, "clusters");
|
||||
const DartModernClusterSummaryRequest cluster_req = {
|
||||
const ModernSummaryReq cluster_req = {
|
||||
.ctx = ctx,
|
||||
.cluster_start = sh.cluster_start,
|
||||
.cluster_end = cluster_end,
|
||||
@@ -798,7 +925,7 @@ static void dump_header_snapshot_json(DartCtx *ctx, PJ *pj, const char *label, u
|
||||
.detail = detail,
|
||||
.pj = pj,
|
||||
};
|
||||
const bool parsed = dart_modern_emit_cluster_summary (&cluster_req);
|
||||
const bool parsed = modern_emit_summary (&cluster_req);
|
||||
pj_end (pj);
|
||||
pj_kb (pj, "clusters_parsed", parsed);
|
||||
if (!parsed) {
|
||||
@@ -811,7 +938,8 @@ static void dump_header_snapshot_json(DartCtx *ctx, PJ *pj, const char *label, u
|
||||
}
|
||||
|
||||
static char *dump_header_ext_json(DartCtx *ctx, int detail) {
|
||||
const char *version = dart_ctx_effective_version (ctx);
|
||||
char vbuf[48];
|
||||
const char *version = dart_ctx_version_label (ctx, vbuf, sizeof (vbuf));
|
||||
DartSnapshotHeader iso = { 0 };
|
||||
if (ctx->iso_data) {
|
||||
dart_snapshot_header_read (ctx, ctx->iso_data, &iso);
|
||||
@@ -827,8 +955,12 @@ static char *dump_header_ext_json(DartCtx *ctx, int detail) {
|
||||
pj_kn (pj, "vm_instr", ctx->vm_instr);
|
||||
pj_kn (pj, "iso_data", ctx->iso_data);
|
||||
pj_kn (pj, "iso_instr", ctx->iso_instr);
|
||||
if (ctx->layout && ctx->layout->single_snapshot) {
|
||||
pj_kb (pj, "single_snapshot", true);
|
||||
}
|
||||
pj_ki (pj, "cws", ctx->compressed_word_size);
|
||||
pj_ks (pj, "dart_version", version? version: "unknown");
|
||||
pj_ks (pj, "dart_version", version);
|
||||
pj_ks (pj, "version_source", dart_version_source_str (ctx? ctx->version_source: DART_VERSION_SOURCE_UNKNOWN));
|
||||
pj_ki (pj, "detail", detail);
|
||||
if (ctx->container_kind[0]) {
|
||||
pj_k (pj, "container");
|
||||
@@ -861,8 +993,12 @@ static char *dump_header_ext_json(DartCtx *ctx, int detail) {
|
||||
pj_end (pj);
|
||||
}
|
||||
pj_ka (pj, "snapshots");
|
||||
dump_header_snapshot_json (ctx, pj, "VM", ctx->vm_data, detail);
|
||||
dump_header_snapshot_json (ctx, pj, "Isolate", ctx->iso_data, detail);
|
||||
if (ctx->layout && ctx->layout->single_snapshot) {
|
||||
dump_header_snapshot_json (ctx, pj, "Combined", ctx->iso_data, detail);
|
||||
} else {
|
||||
dump_header_snapshot_json (ctx, pj, "VM", ctx->vm_data, detail);
|
||||
dump_header_snapshot_json (ctx, pj, "Isolate", ctx->iso_data, detail);
|
||||
}
|
||||
pj_end (pj);
|
||||
pj_end (pj);
|
||||
return pj_drain (pj);
|
||||
@@ -880,8 +1016,7 @@ static void dump_header_ext_snapshot_text(DartCtx *ctx, RStrBuf *sb, const char
|
||||
return;
|
||||
}
|
||||
ut64 cluster_end = addr + sh.total_len;
|
||||
ut64 align = ctx->layout && ctx->layout->max_alignment? (ut64)ctx->layout->max_alignment: 16;
|
||||
ut64 data_image_base = addr + ((sh.total_len + (align - 1)) & ~ (align - 1));
|
||||
ut64 data_image_base = dart_snapshot_data_image_base (addr, sh.total_len);
|
||||
r_strbuf_appendf (sb, " cluster_start: 0x%" PFMT64x "\n", (ut64)sh.cluster_start);
|
||||
r_strbuf_appendf (sb, " cluster_end: 0x%" PFMT64x "\n", (ut64)cluster_end);
|
||||
r_strbuf_appendf (sb, " data_image: 0x%" PFMT64x "\n", (ut64)data_image_base);
|
||||
@@ -890,7 +1025,7 @@ static void dump_header_ext_snapshot_text(DartCtx *ctx, RStrBuf *sb, const char
|
||||
r_strbuf_appendf (sb, " detail: object_pool_entries\n");
|
||||
}
|
||||
r_strbuf_appendf (sb, " columns: idx cid alloc fill count refs flags alloc_range fill_range extras\n");
|
||||
const DartModernClusterSummaryRequest cluster_req = {
|
||||
const ModernSummaryReq cluster_req = {
|
||||
.ctx = ctx,
|
||||
.cluster_start = sh.cluster_start,
|
||||
.cluster_end = cluster_end,
|
||||
@@ -900,7 +1035,7 @@ static void dump_header_ext_snapshot_text(DartCtx *ctx, RStrBuf *sb, const char
|
||||
.detail = detail,
|
||||
.sb = sb,
|
||||
};
|
||||
const bool parsed = dart_modern_emit_cluster_summary (&cluster_req);
|
||||
const bool parsed = modern_emit_summary (&cluster_req);
|
||||
if (!parsed) {
|
||||
r_strbuf_appendf (sb, " parser: unsupported or failed cluster stream\n");
|
||||
}
|
||||
@@ -918,8 +1053,7 @@ static void dump_header_ext_snapshot_r2(DartCtx *ctx, RStrBuf *sb, const char *l
|
||||
return;
|
||||
}
|
||||
ut64 cluster_end = addr + sh.total_len;
|
||||
ut64 align = ctx->layout && ctx->layout->max_alignment? (ut64)ctx->layout->max_alignment: 16;
|
||||
ut64 data_image_base = addr + ((sh.total_len + (align - 1)) & ~ (align - 1));
|
||||
ut64 data_image_base = dart_snapshot_data_image_base (addr, sh.total_len);
|
||||
r_strbuf_appendf (sb, "'f dart.%s.cluster_start = 0x%" PFMT64x "\n", scope, (ut64)sh.cluster_start);
|
||||
r_strbuf_appendf (sb, "'f dart.%s.cluster_end = 0x%" PFMT64x "\n", scope, (ut64)cluster_end);
|
||||
r_strbuf_appendf (sb, "'f dart.%s.data_image = 0x%" PFMT64x "\n", scope, (ut64)data_image_base);
|
||||
@@ -927,7 +1061,7 @@ static void dump_header_ext_snapshot_r2(DartCtx *ctx, RStrBuf *sb, const char *l
|
||||
if (detail >= 3) {
|
||||
r_strbuf_appendf (sb, "'# Dart %s cluster detail: object_pool_entries\n", label);
|
||||
}
|
||||
const DartModernClusterSummaryRequest cluster_req = {
|
||||
const ModernSummaryReq cluster_req = {
|
||||
.ctx = ctx,
|
||||
.cluster_start = sh.cluster_start,
|
||||
.cluster_end = cluster_end,
|
||||
@@ -938,7 +1072,7 @@ static void dump_header_ext_snapshot_r2(DartCtx *ctx, RStrBuf *sb, const char *l
|
||||
.r2_scope = scope,
|
||||
.sb = sb,
|
||||
};
|
||||
const bool parsed = dart_modern_emit_cluster_summary (&cluster_req);
|
||||
const bool parsed = modern_emit_summary (&cluster_req);
|
||||
if (!parsed) {
|
||||
r_strbuf_appendf (sb, "'# Dart %s cluster parser: unsupported or failed cluster stream\n", label);
|
||||
}
|
||||
@@ -958,15 +1092,23 @@ static char *dart_pool_dump_header_ext_level(DartCtx *ctx, int fmt, int detail)
|
||||
char *header = dart_pool_dump_header (ctx, fmt);
|
||||
RStrBuf *sb = r_strbuf_new (header? header: "");
|
||||
free (header);
|
||||
dump_header_ext_snapshot_r2 (ctx, sb, "VM", "vm", ctx->vm_data, detail);
|
||||
dump_header_ext_snapshot_r2 (ctx, sb, "Isolate", "isolate", ctx->iso_data, detail);
|
||||
if (ctx->layout && ctx->layout->single_snapshot) {
|
||||
dump_header_ext_snapshot_r2 (ctx, sb, "Combined", "snapshot", ctx->iso_data, detail);
|
||||
} else {
|
||||
dump_header_ext_snapshot_r2 (ctx, sb, "VM", "vm", ctx->vm_data, detail);
|
||||
dump_header_ext_snapshot_r2 (ctx, sb, "Isolate", "isolate", ctx->iso_data, detail);
|
||||
}
|
||||
return r_strbuf_drain (sb);
|
||||
}
|
||||
char *header = dart_pool_dump_header (ctx, fmt);
|
||||
RStrBuf *sb = r_strbuf_new (header? header: "");
|
||||
free (header);
|
||||
dump_header_ext_snapshot_text (ctx, sb, "VM", ctx->vm_data, detail);
|
||||
dump_header_ext_snapshot_text (ctx, sb, "Isolate", ctx->iso_data, detail);
|
||||
if (ctx->layout && ctx->layout->single_snapshot) {
|
||||
dump_header_ext_snapshot_text (ctx, sb, "Combined", ctx->iso_data, detail);
|
||||
} else {
|
||||
dump_header_ext_snapshot_text (ctx, sb, "VM", ctx->vm_data, detail);
|
||||
dump_header_ext_snapshot_text (ctx, sb, "Isolate", ctx->iso_data, detail);
|
||||
}
|
||||
return r_strbuf_drain (sb);
|
||||
}
|
||||
|
||||
@@ -1017,7 +1159,7 @@ bool dart_pp_info_read_slot(const DartPpInfo *info, ut64 offset, DartPpSlotRaw *
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool resolve_pp_from_snapshot(DartCtx *ctx, ut64 snapshot_base, const char *label, DartPpInfo *info) {
|
||||
static bool resolve_pp_from_snapshot(DartCtx *ctx, ut64 snapshot_base, DartPpInfo *info) {
|
||||
if (!ctx || !snapshot_base || !info) {
|
||||
return false;
|
||||
}
|
||||
@@ -1025,10 +1167,8 @@ static bool resolve_pp_from_snapshot(DartCtx *ctx, ut64 snapshot_base, const cha
|
||||
if (!dart_snapshot_header_read (ctx, snapshot_base, &sh) || !sh.ok) {
|
||||
return false;
|
||||
}
|
||||
ut64 align = ctx->layout && ctx->layout->max_alignment? (ut64)ctx->layout->max_alignment: 16;
|
||||
ut64 data_image_base = snapshot_base + ((sh.total_len + (align - 1)) & ~ (align - 1));
|
||||
const DartModernClusterRequest req = { ctx, sh.cluster_start, snapshot_base + sh.total_len, sh.nc, sh.nb };
|
||||
return dart_modern_build_synthetic_pp (&req, snapshot_base, label, data_image_base, info);
|
||||
const ModernReq req = { ctx, sh.cluster_start, snapshot_base + sh.total_len, sh.nc, sh.nb };
|
||||
return modern_build_pp (&req, info);
|
||||
}
|
||||
|
||||
bool dart_resolve_pp_info(DartCtx *ctx, DartPpInfo *info) {
|
||||
@@ -1041,9 +1181,9 @@ bool dart_resolve_pp_info(DartCtx *ctx, DartPpInfo *info) {
|
||||
}
|
||||
DartVerLayout layout_tmp;
|
||||
DartVerLayout *layout_owned = ctx->layout? NULL: dart_ctx_init_layout (ctx, &layout_tmp);
|
||||
bool ok = resolve_pp_from_snapshot (ctx, ctx->iso_data, "Isolate", info);
|
||||
bool ok = resolve_pp_from_snapshot (ctx, ctx->iso_data, info);
|
||||
if (!ok) {
|
||||
ok = resolve_pp_from_snapshot (ctx, ctx->vm_data, "VM", info);
|
||||
ok = resolve_pp_from_snapshot (ctx, ctx->vm_data, info);
|
||||
}
|
||||
if (layout_owned) {
|
||||
dart_ctx_fini_layout (ctx, layout_owned);
|
||||
@@ -1103,7 +1243,9 @@ static char *dump_pp_r2(const DartPpInfo *info, bool quiet) {
|
||||
r_strbuf_appendf (sb, "s 0x%" PFMT64x "\n", info->base);
|
||||
r_strbuf_appendf (sb, "wx %s\n", hex);
|
||||
r_strbuf_appendf (sb, "e anal.gp=0x%" PFMT64x "\n", info->base);
|
||||
r_strbuf_append (sb, "e anal.roregs=\n");
|
||||
r_strbuf_appendf (sb, "dr x27=0x%" PFMT64x "\n", info->base);
|
||||
r_strbuf_append (sb, "e anal.roregs=x27,gp,zero\n");
|
||||
r_strbuf_appendf (sb, "f PP = 0x%" PFMT64x "\n", info->base);
|
||||
r_strbuf_appendf (sb, "f dart.pp 0x%" PFMT64x " @ 0x%" PFMT64x "\n", info->size, info->base);
|
||||
r_strbuf_appendf (sb, "f dart.pp.entries 0x%" PFMT64x " @ 0x%" PFMT64x "\n", info->entry_bits_offset - info->entries_offset, info->base + info->entries_offset);
|
||||
@@ -1119,7 +1261,7 @@ static char *dump_pp_text(const DartPpInfo *info, bool quiet) {
|
||||
if (quiet) {
|
||||
return r_str_newf ("vaddr=0x%" PFMT64x " paddr=0x%" PFMT64x, info->vaddr, info->paddr);
|
||||
}
|
||||
RStrBuf *sb = r_strbuf_new ("# Dart ObjectPool PP\n\n");
|
||||
RStrBuf *sb = r_strbuf_new ("");
|
||||
r_strbuf_appendf (sb, "pp.vaddr: 0x%" PFMT64x "\n", info->vaddr);
|
||||
r_strbuf_appendf (sb, "pp.paddr: 0x%" PFMT64x "\n", info->paddr);
|
||||
r_strbuf_appendf (sb, "kind: synthetic\n");
|
||||
@@ -1132,11 +1274,10 @@ static char *dump_pp_text(const DartPpInfo *info, bool quiet) {
|
||||
r_strbuf_appendf (sb, "image_size: 0x%" PFMT64x "\n", info->size);
|
||||
r_strbuf_appendf (sb, "entries_offset: 0x%" PFMT64x "\n", info->entries_offset);
|
||||
r_strbuf_appendf (sb, "entry_bits_off: 0x%" PFMT64x "\n", info->entry_bits_offset);
|
||||
r_strbuf_appendf (sb, "note: runtime PP is not serialized; use -r -p to map this reconstructed pool and set anal.gp\n");
|
||||
return r_strbuf_drain (sb);
|
||||
}
|
||||
|
||||
char *dart_pool_dump_pp(DartCtx *ctx, int fmt) {
|
||||
char *dart_pool_dump_pool(DartCtx *ctx, int fmt) {
|
||||
DartPpInfo info = { 0 };
|
||||
if (!dart_resolve_pp_info (ctx, &info)) {
|
||||
if (fmt == 'j') {
|
||||
@@ -1167,6 +1308,7 @@ static void collect_it_entry_cb(const DartInstructionTableEntry *entry, void *us
|
||||
}
|
||||
*dup = *entry;
|
||||
dup->name = entry->name? strdup (entry->name): NULL;
|
||||
dup->signature = entry->signature? strdup (entry->signature): NULL;
|
||||
}
|
||||
|
||||
RVecDartInstructionTableEntry *dart_pool_extract_instruction_table(DartCtx *ctx) {
|
||||
@@ -1205,6 +1347,182 @@ RVecDartInstructionTableEntry *dart_pool_extract_instruction_table(DartCtx *ctx)
|
||||
return list;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
ut64 entrypoint;
|
||||
ut64 index;
|
||||
ut64 code_index;
|
||||
ut64 vm_instr_size;
|
||||
ut64 iso_instr_size;
|
||||
bool has_code;
|
||||
char *name;
|
||||
} DartEntrypointInfo;
|
||||
|
||||
static bool dart_snapshot_raw_size(DartCtx *ctx, ut64 addr, ut64 *out) {
|
||||
if (!ctx || !addr || !out) {
|
||||
return false;
|
||||
}
|
||||
ut8 hdr[16];
|
||||
if (!read_mem (ctx, addr, hdr, sizeof (hdr))) {
|
||||
return false;
|
||||
}
|
||||
if (r_read_le32 (hdr) != DART_SNAPSHOT_MAGIC) {
|
||||
ut64 image_size = r_read_le64 (hdr);
|
||||
ut64 header_size = r_read_le64 (hdr + 8);
|
||||
if (image_size < 16 || image_size > (1ULL << 34) || header_size > image_size) {
|
||||
return false;
|
||||
}
|
||||
*out = image_size;
|
||||
return true;
|
||||
}
|
||||
ut64 size = r_read_le64 (hdr + 4) + 4;
|
||||
if (size < DART_SNAPSHOT_FIXED_SIZE || size > (1ULL << 34)) {
|
||||
return false;
|
||||
}
|
||||
*out = size;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool dart_pool_entrypoint_info(DartCtx *ctx, DartEntrypointInfo *info) {
|
||||
if (!ctx || !info) {
|
||||
return false;
|
||||
}
|
||||
memset (info, 0, sizeof (*info));
|
||||
if (find_snapshots (ctx) != 0) {
|
||||
return false;
|
||||
}
|
||||
(void)dart_snapshot_raw_size (ctx, ctx->vm_instr, &info->vm_instr_size);
|
||||
(void)dart_snapshot_raw_size (ctx, ctx->iso_instr, &info->iso_instr_size);
|
||||
|
||||
int old_limit = ctx->dump_fns_limit;
|
||||
ctx->dump_fns_limit = 1000000;
|
||||
RVecDartInstructionTableEntry *entries = dart_pool_extract_instruction_table (ctx);
|
||||
ctx->dump_fns_limit = old_limit;
|
||||
if (!entries) {
|
||||
return false;
|
||||
}
|
||||
DartInstructionTableEntry *entry;
|
||||
DartInstructionTableEntry *fallback = NULL;
|
||||
R_VEC_FOREACH (entries, entry) {
|
||||
if (!fallback && entry->address) {
|
||||
fallback = entry;
|
||||
}
|
||||
if (entry->has_code && entry->address) {
|
||||
fallback = entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fallback) {
|
||||
info->entrypoint = fallback->address;
|
||||
info->index = fallback->index;
|
||||
info->code_index = fallback->code_index;
|
||||
info->has_code = fallback->has_code;
|
||||
info->name = fallback->name? strdup (fallback->name): NULL;
|
||||
}
|
||||
dart_instruction_table_list_free (entries);
|
||||
return info->entrypoint != 0;
|
||||
}
|
||||
|
||||
static void dart_pool_entrypoint_json(PJ *pj, DartCtx *ctx, const DartEntrypointInfo *info) {
|
||||
pj_o (pj);
|
||||
pj_kn (pj, "entrypoint", info->entrypoint);
|
||||
pj_kn (pj, "index", info->index);
|
||||
if (info->has_code) {
|
||||
pj_kn (pj, "code_index", info->code_index);
|
||||
}
|
||||
pj_ks (pj, "kind", info->has_code? "code": "stub");
|
||||
if (R_STR_ISNOTEMPTY (info->name)) {
|
||||
pj_ks (pj, "name", info->name);
|
||||
}
|
||||
pj_kn (pj, "vm_instr", ctx->vm_instr);
|
||||
if (info->vm_instr_size) {
|
||||
pj_kn (pj, "vm_instr_size", info->vm_instr_size);
|
||||
}
|
||||
pj_kn (pj, "iso_instr", ctx->iso_instr);
|
||||
if (info->iso_instr_size) {
|
||||
pj_kn (pj, "iso_instr_size", info->iso_instr_size);
|
||||
}
|
||||
pj_end (pj);
|
||||
}
|
||||
|
||||
static void dart_pool_entrypoint_r2_mark(RStrBuf *sb, const char *name, ut64 addr, ut64 size, bool quiet) {
|
||||
if (!addr) {
|
||||
return;
|
||||
}
|
||||
r_strbuf_appendf (sb, "f dart.%s = 0x%" PFMT64x "\n", name, addr);
|
||||
if (size >= 4) {
|
||||
ut64 words = size / 4;
|
||||
r_strbuf_appendf (sb, "Cd 4 %" PRIu64 " @ 0x%" PFMT64x "\n", (uint64_t)words, addr);
|
||||
if (!quiet) {
|
||||
r_strbuf_appendf (sb, "CC Dart %s snapshot instructions dword array size=0x%" PFMT64x " @ 0x%" PFMT64x "\n", name, size, addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static char *dart_pool_drain_trimmed_strbuf(RStrBuf *sb) {
|
||||
char *out = r_strbuf_drain (sb);
|
||||
r_str_trim_tail (out);
|
||||
return out;
|
||||
}
|
||||
|
||||
char *dart_pool_dump_entry(DartCtx *ctx, int fmt) {
|
||||
DartEntrypointInfo info;
|
||||
if (!dart_pool_entrypoint_info (ctx, &info)) {
|
||||
return fmt == 'j'? strdup ("{\"error\":\"Dart entrypoint not found\"}"): strdup ("Error: Dart entrypoint not found\n");
|
||||
}
|
||||
const bool quiet = ctx && ctx->quiet;
|
||||
if (fmt == 'j') {
|
||||
PJ *pj = pj_new ();
|
||||
if (!pj) {
|
||||
free (info.name);
|
||||
return strdup ("{\"error\":\"Failed to create JSON\"}");
|
||||
}
|
||||
dart_pool_entrypoint_json (pj, ctx, &info);
|
||||
free (info.name);
|
||||
return pj_drain (pj);
|
||||
}
|
||||
RStrBuf *sb = r_strbuf_new ("");
|
||||
if (fmt == 'r') {
|
||||
if (!quiet) {
|
||||
r_strbuf_append (sb, "# Dart AOT entrypoint and instruction snapshot data\n");
|
||||
}
|
||||
r_strbuf_append (sb, "fs dart\n");
|
||||
r_strbuf_appendf (sb, "f dart.entrypoint = 0x%" PFMT64x "\n", info.entrypoint);
|
||||
if (!quiet) {
|
||||
r_strbuf_appendf (sb, "CC Dart code entrypoint it[%" PRIu64 "] %s @ 0x%" PFMT64x "\n", (uint64_t)info.index, R_STR_ISNOTEMPTY (info.name)? info.name: (info.has_code? "code": "stub"), info.entrypoint);
|
||||
}
|
||||
dart_pool_entrypoint_r2_mark (sb, "vm_instr", ctx->vm_instr, info.vm_instr_size, quiet);
|
||||
dart_pool_entrypoint_r2_mark (sb, "iso_instr", ctx->iso_instr, info.iso_instr_size, quiet);
|
||||
free (info.name);
|
||||
return dart_pool_drain_trimmed_strbuf (sb);
|
||||
}
|
||||
if (quiet) {
|
||||
r_strbuf_appendf (sb, "0x%" PFMT64x, info.entrypoint);
|
||||
free (info.name);
|
||||
return r_strbuf_drain (sb);
|
||||
}
|
||||
r_strbuf_appendf (sb, "dart_entrypoint: 0x%" PFMT64x "\n", info.entrypoint);
|
||||
r_strbuf_appendf (sb, "it_index: %" PRIu64 "\n", (uint64_t)info.index);
|
||||
if (info.has_code) {
|
||||
r_strbuf_appendf (sb, "code_index: %" PRIu64 "\n", (uint64_t)info.code_index);
|
||||
}
|
||||
r_strbuf_appendf (sb, "kind: %s\n", info.has_code? "code": "stub");
|
||||
if (R_STR_ISNOTEMPTY (info.name)) {
|
||||
r_strbuf_appendf (sb, "name: %s\n", info.name);
|
||||
}
|
||||
r_strbuf_appendf (sb, "vm_instr: 0x%" PFMT64x, ctx->vm_instr);
|
||||
if (info.vm_instr_size) {
|
||||
r_strbuf_appendf (sb, " size=0x%" PFMT64x, info.vm_instr_size);
|
||||
}
|
||||
r_strbuf_append (sb, "\n");
|
||||
r_strbuf_appendf (sb, "iso_instr: 0x%" PFMT64x, ctx->iso_instr);
|
||||
if (info.iso_instr_size) {
|
||||
r_strbuf_appendf (sb, " size=0x%" PFMT64x, info.iso_instr_size);
|
||||
}
|
||||
r_strbuf_append (sb, "\n");
|
||||
free (info.name);
|
||||
return dart_pool_drain_trimmed_strbuf (sb);
|
||||
}
|
||||
|
||||
char *dart_pool_dump_it(DartCtx *ctx, int fmt) {
|
||||
RVecDartInstructionTableEntry *entries = dart_pool_extract_instruction_table (ctx);
|
||||
if (!entries) {
|
||||
|
||||
@@ -17,62 +17,40 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
// Which flavour of type a DartTypeInfo holds. Not snapshot class ids: these
|
||||
// never leave the parser, so they carry no version dependency.
|
||||
typedef enum {
|
||||
kIllegalCid = 0,
|
||||
kClassCid = 5,
|
||||
kPatchClassCid = 6,
|
||||
kFunctionCid = 7,
|
||||
kClosureDataCid = 8,
|
||||
kFfiTrampolineDataCid = 9,
|
||||
kFieldCid = 10,
|
||||
kScriptCid = 11,
|
||||
kLibraryCid = 12,
|
||||
kNamespaceCid = 13,
|
||||
kKernelProgramInfoCid = 14,
|
||||
kCodeCid = 40,
|
||||
kInstructionsCid = 41,
|
||||
kObjectPoolCid = 45,
|
||||
kCodeSourceMapCid = 49,
|
||||
kPcDescriptorsCid = 50,
|
||||
kStringCid = 72,
|
||||
kOneByteStringCid = 73,
|
||||
kTwoByteStringCid = 74,
|
||||
kArrayCid = 75,
|
||||
kImmutableArrayCid = 76,
|
||||
kGrowableObjectArrayCid = 77,
|
||||
kMintCid = 78,
|
||||
kDoubleCid = 79,
|
||||
kTypedDataBaseCid = 80,
|
||||
kTypeCid = 110,
|
||||
kFunctionTypeCid = 111,
|
||||
kRecordTypeCid = 112,
|
||||
kTypeParametersCid = 113,
|
||||
kTypeParameterCid = 114,
|
||||
kTypeArgumentsCid = 115,
|
||||
kNumPredefinedCids = 128
|
||||
} DartCid;
|
||||
|
||||
typedef struct {
|
||||
ut64 ref_id;
|
||||
ut64 name_ref;
|
||||
ut64 library_ref;
|
||||
char *name;
|
||||
int instance_size;
|
||||
} DartClass;
|
||||
|
||||
typedef struct {
|
||||
ut64 ref_id;
|
||||
ut64 name_ref;
|
||||
ut64 owner_ref;
|
||||
ut64 code_ref;
|
||||
ut64 entry_point;
|
||||
char *name;
|
||||
} DartPoolFunction;
|
||||
DART_TYPE_KIND_TYPE = 1,
|
||||
DART_TYPE_KIND_FUNCTION_TYPE,
|
||||
DART_TYPE_KIND_TYPE_PARAMETER,
|
||||
} DartTypeKind;
|
||||
|
||||
#define DART_SNAPSHOT_MAGIC 0xdcdcf5f5
|
||||
#define DART_SNAPSHOT_FIXED_SIZE (4 + 8 + 8)
|
||||
#define DART_SNAPSHOT_HASH_SIZE 32
|
||||
#define DART_SNAPSHOT_FEATURES_SCAN_MAX 2048
|
||||
#define DART_IMAGE_ALIGNMENT 64
|
||||
|
||||
static inline ut32 dart_cid_from_object_header(ut64 tags) {
|
||||
return (ut32) ((tags >> 12) & 0xfffffU);
|
||||
}
|
||||
|
||||
static inline ut64 dart_snapshot_data_image_base(ut64 snapshot_base, ut64 snapshot_len) {
|
||||
return snapshot_base + ((snapshot_len + (DART_IMAGE_ALIGNMENT - 1)) & ~ (DART_IMAGE_ALIGNMENT - 1));
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
bool ok;
|
||||
@@ -84,6 +62,8 @@ typedef struct {
|
||||
ut64 nb;
|
||||
ut64 no;
|
||||
ut64 nc;
|
||||
ut64 ncc;
|
||||
ut64 field_table_len;
|
||||
ut64 itlen;
|
||||
ut64 itdata;
|
||||
ut64 cluster_start;
|
||||
@@ -185,7 +165,40 @@ typedef struct {
|
||||
char *resolved_name;
|
||||
int resolved_cid;
|
||||
ut64 resolved_code_index;
|
||||
} DartModernPoolSlotInfo;
|
||||
} ModernPoolSlot;
|
||||
|
||||
typedef struct {
|
||||
ut64 pp_offset;
|
||||
ut64 pp_base;
|
||||
ut64 pool_ref;
|
||||
ut64 pool_index;
|
||||
ut64 entry_index;
|
||||
ut64 target_ref;
|
||||
const char *target_kind;
|
||||
ut64 field_index;
|
||||
ut64 string_ref;
|
||||
ut64 string_addr;
|
||||
ut32 string_flags;
|
||||
bool via_collection;
|
||||
const char *string_value;
|
||||
} ModernPoolStrRef;
|
||||
|
||||
typedef void(*ModernPoolStrRefCb)(const ModernPoolStrRef *info, void *user);
|
||||
|
||||
typedef struct {
|
||||
ut64 pp_offset;
|
||||
ut64 pool_ref;
|
||||
ut64 pool_index;
|
||||
ut64 entry_index;
|
||||
ut64 ref;
|
||||
const char *kind; // resolved kind: "string", "function", "class", "code", ...
|
||||
const char *name; // resolved name/value, or NULL (valid only during the callback)
|
||||
int cid;
|
||||
ut64 code_index; // UT64_MAX when the entry does not resolve to code
|
||||
ut64 value_addr; // snapshot address of the resolved object, or 0
|
||||
} ModernPoolRefInfo;
|
||||
|
||||
typedef void(*ModernPoolRefCb)(const ModernPoolRefInfo *info, void *user);
|
||||
|
||||
typedef void(*DartInstructionTableEntryCallback)(const DartInstructionTableEntry *entry, void *user);
|
||||
|
||||
@@ -210,7 +223,9 @@ typedef struct {
|
||||
ut64 cluster_end;
|
||||
ut64 num_clusters;
|
||||
ut64 num_base_objects;
|
||||
} DartModernClusterRequest;
|
||||
} ModernReq;
|
||||
|
||||
typedef struct modern_value_graph_t ModernValueGraph;
|
||||
|
||||
typedef struct {
|
||||
DartCtx *ctx;
|
||||
@@ -223,7 +238,7 @@ typedef struct {
|
||||
const char *r2_scope;
|
||||
RStrBuf *sb;
|
||||
PJ *pj;
|
||||
} DartModernClusterSummaryRequest;
|
||||
} ModernSummaryReq;
|
||||
|
||||
bool read_mem(DartCtx *ctx, ut64 addr, void *buf, int len);
|
||||
bool read_u32_at(DartCtx *ctx, ut64 addr, ut32 *out);
|
||||
@@ -232,7 +247,8 @@ bool dart_read_unsigned_at(DartCtx *ctx, ut64 addr, ut64 *out_val, ut64 *out_nex
|
||||
bool dart_read_unsigned_buf(const ut8 *buf, ut64 size, ut64 pos, ut64 *out_val, ut64 *out_next);
|
||||
char *dart_utf16le_to_utf8(const ut8 *buf, ut64 size);
|
||||
bool dart_snapshot_header_read(DartCtx *ctx, ut64 base, DartSnapshotHeader *out);
|
||||
bool dart_snapshot_header_read_buf(const ut8 *buf, ut64 size, DartSnapshotHeader *out);
|
||||
bool dart_snapshot_fingerprint_read(DartCtx *ctx, ut64 base, DartSnapshotHeader *out);
|
||||
bool dart_snapshot_header_read_buf(const ut8 *buf, ut64 size, const DartVerLayout *layout, DartSnapshotHeader *out);
|
||||
DartVerLayout *dart_ctx_init_layout(DartCtx *ctx, DartVerLayout *tmp);
|
||||
void dart_ctx_fini_layout(DartCtx *ctx, DartVerLayout *owned);
|
||||
int find_snapshots(DartCtx *ctx);
|
||||
@@ -262,18 +278,26 @@ bool cs_read_bytes(ClusterStream *s, ut8 *buf, int len);
|
||||
void free_dart_string(void *p);
|
||||
int decode_string_cluster(ClusterStream *s, DartCtx *ctx, ut64 *ref_counter, bool is_canonical);
|
||||
void skip_generic_cluster(ClusterStream *stream);
|
||||
int deserialize_clusters(DartCtx *ctx, ut64 cluster_start, ut64 cluster_end, ut64 num_clusters, ut64 iso_instr);
|
||||
void resolve_names(DartCtx *ctx);
|
||||
|
||||
bool modern_skip_n_bytes(ClusterStream *s, ut64 len);
|
||||
bool dart_modern_is_supported_snapshot(DartCtx *ctx);
|
||||
bool dart_modern_emit_cluster_summary(const DartModernClusterSummaryRequest *req);
|
||||
bool dart_modern_build_synthetic_pp(const DartModernClusterRequest *req, ut64 snapshot_base, const char *snapshot_label, ut64 data_image_base, DartPpInfo *out);
|
||||
bool dart_modern_resolve_pp_slot(const DartModernClusterRequest *req, ut64 pp_offset, DartModernPoolSlotInfo *out);
|
||||
void dart_modern_pool_slot_info_fini(DartModernPoolSlotInfo *info);
|
||||
bool dart_modern_extract_object_pool_strings_from_clusters(const DartModernClusterRequest *req, RList *strings, HtUP *seen_refs, ut64 *ref_counter);
|
||||
bool dart_modern_scan_names_from_clusters(DartCtx *ctx, ut64 cluster_start, ut64 cluster_end, ut64 num_clusters, ut64 itlen);
|
||||
bool dart_modern_extract_classes_from_clusters(const DartModernClusterRequest *req, RList *class_list);
|
||||
bool modern_supported(DartCtx *ctx);
|
||||
bool modern_emit_summary(const ModernSummaryReq *req);
|
||||
bool modern_probe_snapshot(const ModernReq *req, ut64 num_objects, ut64 *semantic_score);
|
||||
bool modern_build_pp(const ModernReq *req, DartPpInfo *out);
|
||||
bool modern_resolve_pp_slot(const ModernReq *req, ut64 pp_off, ModernPoolSlot *out);
|
||||
void modern_slot_fini(ModernPoolSlot *slot);
|
||||
ModernValueGraph *modern_value_graph_new(const ModernReq *req, ut64 root_ref);
|
||||
void modern_value_graph_free(ModernValueGraph *graph);
|
||||
void modern_value_graph_json(PJ *pj, ModernValueGraph *graph);
|
||||
void modern_value_graph_text(RStrBuf *sb, ModernValueGraph *graph);
|
||||
const char *modern_value_graph_root_kind(ModernValueGraph *graph);
|
||||
bool modern_collect_direct_pool_strrefs(const ModernReq *req, HtUP *pp_offs, ModernPoolStrRefCb cb, void *user);
|
||||
bool modern_collect_direct_pool_refs(const ModernReq *req, ModernPoolRefCb cb, void *user);
|
||||
bool modern_collect_pool_strrefs(const ModernReq *req, HtUP *pp_offs, ModernPoolStrRefCb cb, void *user);
|
||||
bool modern_collect_pool_value_strrefs(const ModernReq *req, HtUP *pp_offs, ModernPoolStrRefCb cb, void *user);
|
||||
bool modern_extract_pool_strings(const ModernReq *req, RList *strings, HtUP *seen, ut64 *next_ref);
|
||||
bool modern_scan_names(DartCtx *ctx, ut64 start, ut64 end, ut64 nclusters, ut64 itlen);
|
||||
bool modern_extract_classes(const ModernReq *req, RList *classes);
|
||||
DartStringCategory dart_string_classify_value(const char *s);
|
||||
|
||||
void init_function_layout(DartCtx *ctx, DartFunctionLayout *fl);
|
||||
|
||||
+215
-48
@@ -1,11 +1,87 @@
|
||||
/* r2flutter - MIT - Copyright 2026 - pancake */
|
||||
|
||||
#include <ctype.h>
|
||||
#include "dart_pool_parse_priv.h"
|
||||
|
||||
// Window size for the read cache. Cluster streams are decoded one byte at a
|
||||
// time, so serving those reads from a prefetched window turns millions of
|
||||
// r_io_read_at calls into a handful.
|
||||
#define RMEM_WINDOW (1024 * 1024)
|
||||
// Several windows are kept because decoders interleave distant regions, e.g.
|
||||
// a cluster alloc stream and the RO data image it indexes. With a single
|
||||
// window every such step refilled it twice.
|
||||
#define RMEM_WINDOWS 4
|
||||
|
||||
typedef struct {
|
||||
ut8 *buf;
|
||||
ut64 addr;
|
||||
ut64 used; // last access tick, for least-recently-used eviction
|
||||
int len;
|
||||
} DartReadWindow;
|
||||
|
||||
typedef struct dart_read_cache_t {
|
||||
DartReadWindow w[RMEM_WINDOWS];
|
||||
ut64 tick;
|
||||
} DartReadCache;
|
||||
|
||||
void dart_read_cache_free(DartReadCache *cache) {
|
||||
if (!cache) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < RMEM_WINDOWS; i++) {
|
||||
free (cache->w[i].buf);
|
||||
}
|
||||
free (cache);
|
||||
}
|
||||
|
||||
bool read_mem(DartCtx *ctx, ut64 addr, void *buf, int len) {
|
||||
if (!ctx || !ctx->core || !buf || len <= 0) {
|
||||
return false;
|
||||
}
|
||||
// Reads larger than a window bypass the cache.
|
||||
if (len > RMEM_WINDOW) {
|
||||
return r_io_read_at (ctx->core->io, addr, (ut8 *)buf, len);
|
||||
}
|
||||
if (!ctx->rmem) {
|
||||
ctx->rmem = R_NEW0 (DartReadCache);
|
||||
}
|
||||
DartReadCache *cache = ctx->rmem;
|
||||
cache->tick++;
|
||||
// Fast path: request fully contained in a cached window.
|
||||
DartReadWindow *victim = &cache->w[0];
|
||||
for (int i = 0; i < RMEM_WINDOWS; i++) {
|
||||
DartReadWindow *w = &cache->w[i];
|
||||
if (w->len && addr >= w->addr) {
|
||||
ut64 off = addr - w->addr;
|
||||
if (off + (ut64)len <= (ut64)w->len) {
|
||||
w->used = cache->tick;
|
||||
memcpy (buf, w->buf + off, (size_t)len);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (w->used < victim->used) {
|
||||
victim = w;
|
||||
}
|
||||
}
|
||||
if (!victim->buf) {
|
||||
victim->buf = malloc (RMEM_WINDOW);
|
||||
if (!victim->buf) {
|
||||
return r_io_read_at (ctx->core->io, addr, (ut8 *)buf, len);
|
||||
}
|
||||
}
|
||||
// Refill the least recently used window at addr. r_io_read_at only
|
||||
// succeeds when the whole range is readable, so a successful window read
|
||||
// means every byte in it is valid and safe to serve. On failure (e.g. near
|
||||
// the end of a mapped region) fall back to an exact read and drop that
|
||||
// window, whose buffer the failed read may have clobbered.
|
||||
if (r_io_read_at (ctx->core->io, addr, victim->buf, RMEM_WINDOW)) {
|
||||
victim->addr = addr;
|
||||
victim->len = RMEM_WINDOW;
|
||||
victim->used = cache->tick;
|
||||
memcpy (buf, victim->buf, (size_t)len);
|
||||
return true;
|
||||
}
|
||||
victim->len = 0;
|
||||
return r_io_read_at (ctx->core->io, addr, (ut8 *)buf, len);
|
||||
}
|
||||
|
||||
@@ -106,7 +182,71 @@ char *dart_utf16le_to_utf8(const ut8 *buf, ut64 size) {
|
||||
return out;
|
||||
}
|
||||
|
||||
bool dart_snapshot_header_read(DartCtx *ctx, ut64 base, DartSnapshotHeader *out) {
|
||||
static const DartVerLayout *dart_snapshot_header_layout(DartCtx *ctx, const char *hash) {
|
||||
if (ctx && ctx->layout) {
|
||||
return ctx->layout;
|
||||
}
|
||||
if (ctx && R_STR_ISNOTEMPTY (ctx->dart_version_override)) {
|
||||
const DartVerLayout *layout = dart_profile_from_version (ctx->dart_version_override);
|
||||
if (layout) {
|
||||
return layout;
|
||||
}
|
||||
}
|
||||
const char *version = dart_version_from_hash (hash);
|
||||
const DartVerLayout *layout = version? dart_profile_from_version (version): NULL;
|
||||
return layout? layout: dart_newest_profile ();
|
||||
}
|
||||
|
||||
static bool dart_snapshot_header_set_fields(DartSnapshotHeader *out, const DartVerLayout *layout, const ut64 *fields, int count) {
|
||||
if (!out || !layout || !fields || count != layout->header_fields) {
|
||||
return false;
|
||||
}
|
||||
switch (layout->header_style) {
|
||||
case DART_HEADER_STYLE_210:
|
||||
out->nb = fields[0];
|
||||
out->no = fields[1];
|
||||
out->nc = fields[2];
|
||||
out->field_table_len = fields[3];
|
||||
break;
|
||||
case DART_HEADER_STYLE_212:
|
||||
if (UT64_MAX - fields[2] < fields[3]) {
|
||||
return false;
|
||||
}
|
||||
out->nb = fields[0];
|
||||
out->no = fields[1];
|
||||
out->ncc = fields[2];
|
||||
out->nc = fields[2] + fields[3];
|
||||
out->field_table_len = fields[4];
|
||||
break;
|
||||
case DART_HEADER_STYLE_214:
|
||||
out->nb = fields[0];
|
||||
out->no = fields[1];
|
||||
out->nc = fields[2];
|
||||
out->field_table_len = fields[3];
|
||||
out->itlen = fields[4];
|
||||
break;
|
||||
case DART_HEADER_STYLE_216:
|
||||
out->nb = fields[0];
|
||||
out->no = fields[1];
|
||||
out->nc = fields[2];
|
||||
out->field_table_len = fields[3];
|
||||
out->itlen = fields[4];
|
||||
out->itdata = fields[5];
|
||||
break;
|
||||
case DART_HEADER_STYLE_MODERN:
|
||||
out->nb = fields[0];
|
||||
out->no = fields[1];
|
||||
out->nc = fields[2];
|
||||
out->itlen = fields[3];
|
||||
out->itdata = fields[4];
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool dart_snapshot_outer_read(DartCtx *ctx, ut64 base, DartSnapshotHeader *out, ut64 *out_cursor) {
|
||||
if (!ctx || !out) {
|
||||
return false;
|
||||
}
|
||||
@@ -119,17 +259,34 @@ bool dart_snapshot_header_read(DartCtx *ctx, ut64 base, DartSnapshotHeader *out)
|
||||
if (out->magic != DART_SNAPSHOT_MAGIC) {
|
||||
return false;
|
||||
}
|
||||
out->total_len = r_read_le64 (hdr + 4) + 4;
|
||||
const ut64 stored_len = r_read_le64 (hdr + 4);
|
||||
if (stored_len > UT64_MAX - 4) {
|
||||
return false;
|
||||
}
|
||||
out->total_len = stored_len + 4;
|
||||
if (out->total_len < DART_SNAPSHOT_FIXED_SIZE + DART_SNAPSHOT_HASH_SIZE + 1 || out->total_len > (1ULL << 34) || base > UT64_MAX - out->total_len) {
|
||||
return false;
|
||||
}
|
||||
RIOMap *map = r_io_map_get_at (ctx->core->io, base);
|
||||
if (map && base + out->total_len - 1 > r_io_map_to (map)) {
|
||||
return false;
|
||||
}
|
||||
out->kind = r_read_le64 (hdr + 12);
|
||||
ut64 cursor = base + DART_SNAPSHOT_FIXED_SIZE;
|
||||
if (!read_mem (ctx, cursor, out->hash, DART_SNAPSHOT_HASH_SIZE)) {
|
||||
return false;
|
||||
}
|
||||
out->hash[DART_SNAPSHOT_HASH_SIZE] = '\0';
|
||||
for (int i = 0; i < DART_SNAPSHOT_HASH_SIZE; i++) {
|
||||
if (!isxdigit ((ut8)out->hash[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
cursor += DART_SNAPSHOT_HASH_SIZE;
|
||||
ut8 b = 0;
|
||||
int scanned = 0;
|
||||
while (scanned < DART_SNAPSHOT_FEATURES_SCAN_MAX) {
|
||||
const ut64 snapshot_end = base + out->total_len;
|
||||
while (scanned < DART_SNAPSHOT_FEATURES_SCAN_MAX && cursor + (ut64)scanned < snapshot_end) {
|
||||
if (!read_mem (ctx, cursor + scanned, &b, 1)) {
|
||||
return false;
|
||||
}
|
||||
@@ -138,7 +295,7 @@ bool dart_snapshot_header_read(DartCtx *ctx, ut64 base, DartSnapshotHeader *out)
|
||||
}
|
||||
scanned++;
|
||||
}
|
||||
if (b) {
|
||||
if (b || cursor + (ut64)scanned >= snapshot_end) {
|
||||
return false;
|
||||
}
|
||||
int tocopy = R_MIN (scanned, (int)sizeof (out->flags) - 1);
|
||||
@@ -147,20 +304,36 @@ bool dart_snapshot_header_read(DartCtx *ctx, ut64 base, DartSnapshotHeader *out)
|
||||
}
|
||||
out->flags[tocopy] = '\0';
|
||||
cursor += (ut64)scanned + 1;
|
||||
if (cursor >= snapshot_end) {
|
||||
return false;
|
||||
}
|
||||
if (out_cursor) {
|
||||
*out_cursor = cursor;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dart_snapshot_fingerprint_read(DartCtx *ctx, ut64 base, DartSnapshotHeader *out) {
|
||||
return dart_snapshot_outer_read (ctx, base, out, NULL);
|
||||
}
|
||||
|
||||
bool dart_snapshot_header_read(DartCtx *ctx, ut64 base, DartSnapshotHeader *out) {
|
||||
ut64 cursor = 0;
|
||||
if (!dart_snapshot_outer_read (ctx, base, out, &cursor)) {
|
||||
return false;
|
||||
}
|
||||
const DartVerLayout *layout = dart_snapshot_header_layout (ctx, out->hash);
|
||||
if (!layout || layout->header_fields < 4 || layout->header_fields > 6) {
|
||||
return false;
|
||||
}
|
||||
ut64 next = cursor;
|
||||
if (!dart_read_unsigned_at (ctx, next, &out->nb, &next)) {
|
||||
return false;
|
||||
ut64 fields[6] = { 0 };
|
||||
for (int i = 0; i < layout->header_fields; i++) {
|
||||
if (!dart_read_unsigned_at (ctx, next, &fields[i], &next)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!dart_read_unsigned_at (ctx, next, &out->no, &next)) {
|
||||
return false;
|
||||
}
|
||||
if (!dart_read_unsigned_at (ctx, next, &out->nc, &next)) {
|
||||
return false;
|
||||
}
|
||||
if (!dart_read_unsigned_at (ctx, next, &out->itlen, &next)) {
|
||||
return false;
|
||||
}
|
||||
if (!dart_read_unsigned_at (ctx, next, &out->itdata, &next)) {
|
||||
if (!dart_snapshot_header_set_fields (out, layout, fields, layout->header_fields)) {
|
||||
return false;
|
||||
}
|
||||
out->cluster_start = next;
|
||||
@@ -168,7 +341,7 @@ bool dart_snapshot_header_read(DartCtx *ctx, ut64 base, DartSnapshotHeader *out)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dart_snapshot_header_read_buf(const ut8 *buf, ut64 size, DartSnapshotHeader *out) {
|
||||
bool dart_snapshot_header_read_buf(const ut8 *buf, ut64 size, const DartVerLayout *layout, DartSnapshotHeader *out) {
|
||||
if (!buf || !out || size < DART_SNAPSHOT_FIXED_SIZE + DART_SNAPSHOT_HASH_SIZE + 1) {
|
||||
return false;
|
||||
}
|
||||
@@ -202,20 +375,20 @@ bool dart_snapshot_header_read_buf(const ut8 *buf, ut64 size, DartSnapshotHeader
|
||||
}
|
||||
out->flags[tocopy] = '\0';
|
||||
cursor += (ut64)scanned + 1;
|
||||
if (!layout) {
|
||||
layout = dart_snapshot_header_layout (NULL, out->hash);
|
||||
}
|
||||
if (!layout || layout->header_fields < 4 || layout->header_fields > 6) {
|
||||
return false;
|
||||
}
|
||||
ut64 next = cursor;
|
||||
if (!dart_read_unsigned_buf (buf, size, next, &out->nb, &next)) {
|
||||
return false;
|
||||
ut64 fields[6] = { 0 };
|
||||
for (int i = 0; i < layout->header_fields; i++) {
|
||||
if (!dart_read_unsigned_buf (buf, size, next, &fields[i], &next)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!dart_read_unsigned_buf (buf, size, next, &out->no, &next)) {
|
||||
return false;
|
||||
}
|
||||
if (!dart_read_unsigned_buf (buf, size, next, &out->nc, &next)) {
|
||||
return false;
|
||||
}
|
||||
if (!dart_read_unsigned_buf (buf, size, next, &out->itlen, &next)) {
|
||||
return false;
|
||||
}
|
||||
if (!dart_read_unsigned_buf (buf, size, next, &out->itdata, &next)) {
|
||||
if (!dart_snapshot_header_set_fields (out, layout, fields, layout->header_fields)) {
|
||||
return false;
|
||||
}
|
||||
if (next >= out->total_len) {
|
||||
@@ -257,6 +430,9 @@ bool cs_read_unsigned(ClusterStream *s, ut64 *out) {
|
||||
}
|
||||
|
||||
bool cs_read_ref_id(ClusterStream *s, ut64 *out) {
|
||||
if (s && s->ctx && s->ctx->layout && s->ctx->layout->ref_encoding == DART_REF_ENCODING_UNSIGNED) {
|
||||
return cs_read_unsigned (s, out);
|
||||
}
|
||||
int64_t result = 0;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
ut8 raw = 0;
|
||||
@@ -307,33 +483,24 @@ bool cs_read_tagged32(ClusterStream *s, ut32 *out) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ReadStream::Read<int64_t>: 7-bit groups, then a final byte holding a signed
|
||||
// value in [-64, 63] biased by kEndByteMarker (192).
|
||||
bool cs_read_tagged64(ClusterStream *s, int64_t *out) {
|
||||
ut8 raw = 0;
|
||||
if (!cs_read_u8 (s, &raw)) {
|
||||
return false;
|
||||
}
|
||||
int8_t b = (int8_t)raw;
|
||||
if (b < 0) {
|
||||
if (out) {
|
||||
*out = (int64_t)b + 192;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
int64_t v = (int64_t)raw;
|
||||
int shift = 7;
|
||||
for (int i = 1; i < 10; i++) {
|
||||
ut64 v = 0;
|
||||
int shift = 0;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ut8 raw = 0;
|
||||
if (!cs_read_u8 (s, &raw)) {
|
||||
return false;
|
||||
}
|
||||
b = (int8_t)raw;
|
||||
if (b < 0) {
|
||||
v |= ((int64_t)b + 192) << shift;
|
||||
if (raw > 0x7f) {
|
||||
v |= (ut64) ((int64_t)raw - 192) << shift;
|
||||
if (out) {
|
||||
*out = v;
|
||||
*out = (int64_t)v;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
v |= ((int64_t)raw) << shift;
|
||||
v |= (ut64)raw << shift;
|
||||
shift += 7;
|
||||
}
|
||||
return false;
|
||||
|
||||
+116
-80
@@ -26,7 +26,17 @@ void dart_string_list_free(RList *list) {
|
||||
r_list_free (list);
|
||||
}
|
||||
|
||||
#define DART_STRING_SCAN_LIMIT 50000
|
||||
typedef struct {
|
||||
RList *list;
|
||||
HtUP *seen_addrs;
|
||||
ut64 ref_counter;
|
||||
ut64 limit; // 0 means unlimited
|
||||
} StringScan;
|
||||
|
||||
static bool string_scan_full(const StringScan *ss) {
|
||||
return ss->limit && (ut64)r_list_length (ss->list) >= ss->limit;
|
||||
}
|
||||
|
||||
DartStringCategory dart_string_classify_value(const char *s) {
|
||||
if (R_STR_ISEMPTY (s)) {
|
||||
return DART_STRING_CAT_UNKNOWN;
|
||||
@@ -57,7 +67,7 @@ static const char *string_category_name(DartStringCategory cat) {
|
||||
}
|
||||
}
|
||||
|
||||
static void scan_utf16_strings(const ut8 *buf, ut64 base, ut64 size, RList *list, HtUP *seen_addrs, ut64 *ref_counter);
|
||||
static void scan_utf16_strings(const ut8 *buf, ut64 base, ut64 size, StringScan *ss);
|
||||
static bool should_scan_section(const RBinSection *sec);
|
||||
|
||||
static bool is_common_text_punct(ut8 ch) {
|
||||
@@ -146,31 +156,28 @@ static void packed_string_record_fini(PackedStringRecord *rec) {
|
||||
rec->value = NULL;
|
||||
}
|
||||
|
||||
static void append_string_info(RList *list, HtUP *seen_addrs, const char *value, ut32 len, ut32 flags, ut64 addr, DartStringCategory cat, ut64 *ref_counter) {
|
||||
if (!list || !value) {
|
||||
static void append_string_info(StringScan *ss, const char *value, ut32 len, ut32 flags, ut64 addr, DartStringCategory cat) {
|
||||
if (!value || string_scan_full (ss)) {
|
||||
return;
|
||||
}
|
||||
if (r_list_length (list) >= DART_STRING_SCAN_LIMIT) {
|
||||
return;
|
||||
}
|
||||
if (seen_addrs && addr && ht_up_find (seen_addrs, addr, NULL)) {
|
||||
if (addr && ht_up_find (ss->seen_addrs, addr, NULL)) {
|
||||
return;
|
||||
}
|
||||
DartStringInfo *si = R_NEW0 (DartStringInfo);
|
||||
si->ref_id = ref_counter? (*ref_counter)++: 0;
|
||||
si->ref_id = ss->ref_counter++;
|
||||
si->length = len;
|
||||
si->flags = flags | DART_STRING_CANONICAL;
|
||||
si->address = addr;
|
||||
si->category = cat;
|
||||
si->references = r_list_newf ((RListFree)dart_string_ref_free);
|
||||
si->value = strdup (value);
|
||||
r_list_append (list, si);
|
||||
if (seen_addrs && addr) {
|
||||
ht_up_insert (seen_addrs, addr, si);
|
||||
r_list_append (ss->list, si);
|
||||
if (addr) {
|
||||
ht_up_insert (ss->seen_addrs, addr, si);
|
||||
}
|
||||
}
|
||||
|
||||
static void scan_ascii_strings(const ut8 *buf, ut64 base, ut64 size, RList *list, HtUP *seen_addrs, ut64 *ref_counter) {
|
||||
static void scan_ascii_strings(const ut8 *buf, ut64 base, ut64 size, StringScan *ss) {
|
||||
if (!buf || !size) {
|
||||
return;
|
||||
}
|
||||
@@ -190,7 +197,7 @@ static void scan_ascii_strings(const ut8 *buf, ut64 base, ut64 size, RList *list
|
||||
memcpy (tmp, buf + start, length);
|
||||
tmp[length] = '\0';
|
||||
if (looks_like_text (tmp)) {
|
||||
append_string_info (list, seen_addrs, tmp, (ut32)length, 0, base + start, dart_string_classify_value (tmp), ref_counter);
|
||||
append_string_info (ss, tmp, (ut32)length, 0, base + start, dart_string_classify_value (tmp));
|
||||
}
|
||||
free (tmp);
|
||||
}
|
||||
@@ -334,12 +341,12 @@ static bool parse_packed_string_record_text(const ut8 *buf, ut64 size, ut64 pos,
|
||||
return out->value != NULL;
|
||||
}
|
||||
|
||||
static void scan_packed_string_runs(const ut8 *buf, ut64 base, ut64 size, RList *list, HtUP *seen_addrs, ut64 *ref_counter) {
|
||||
if (!buf || !size || !list) {
|
||||
static void scan_packed_string_runs(const ut8 *buf, ut64 base, ut64 size, StringScan *ss) {
|
||||
if (!buf || !size) {
|
||||
return;
|
||||
}
|
||||
ut64 pos = 0;
|
||||
while (pos < size && r_list_length (list) < DART_STRING_SCAN_LIMIT) {
|
||||
while (pos < size && !string_scan_full (ss)) {
|
||||
PackedStringRecord probe = { 0 };
|
||||
if (!parse_packed_string_record_text (buf, size, pos, &probe)) {
|
||||
pos++;
|
||||
@@ -372,9 +379,9 @@ static void scan_packed_string_runs(const ut8 *buf, ut64 base, ut64 size, RList
|
||||
skips++;
|
||||
}
|
||||
if (n_records >= DART_PACKED_STRING_MIN_RUN) {
|
||||
for (int i = 0; i < n_records && r_list_length (list) < DART_STRING_SCAN_LIMIT; i++) {
|
||||
for (int i = 0; i < n_records && !string_scan_full (ss); i++) {
|
||||
PackedStringRecord *rec = &records[i];
|
||||
append_string_info (list, seen_addrs, rec->value, rec->length, rec->flags, base + rec->payload_off, dart_string_classify_value (rec->value), ref_counter);
|
||||
append_string_info (ss, rec->value, rec->length, rec->flags, base + rec->payload_off, dart_string_classify_value (rec->value));
|
||||
}
|
||||
pos = run_end;
|
||||
} else {
|
||||
@@ -386,8 +393,8 @@ static void scan_packed_string_runs(const ut8 *buf, ut64 base, ut64 size, RList
|
||||
}
|
||||
}
|
||||
|
||||
static void scan_packed_strings_from_snapshot(DartCtx *ctx, ut64 snapshot_base, RList *list, HtUP *seen_addrs, ut64 *ref_counter) {
|
||||
if (!ctx || !snapshot_base || !list) {
|
||||
static void scan_packed_strings_from_snapshot(DartCtx *ctx, ut64 snapshot_base, StringScan *ss) {
|
||||
if (!ctx || !snapshot_base) {
|
||||
return;
|
||||
}
|
||||
ut8 hdrbuf[12];
|
||||
@@ -410,30 +417,30 @@ static void scan_packed_strings_from_snapshot(DartCtx *ctx, ut64 snapshot_base,
|
||||
return;
|
||||
}
|
||||
DartSnapshotHeader hdr;
|
||||
if (!dart_snapshot_header_read_buf (buf, total_len, &hdr)) {
|
||||
if (!dart_snapshot_header_read_buf (buf, total_len, ctx->layout, &hdr)) {
|
||||
free (buf);
|
||||
return;
|
||||
}
|
||||
if (ctx->verbose > 1) {
|
||||
fprintf (stderr, "[r2flutter] packed string scan snapshot=0x%" PFMT64x " cluster_start=0x%" PFMT64x " total=0x%" PFMT64x "\n", snapshot_base, snapshot_base + hdr.cluster_start, hdr.total_len);
|
||||
}
|
||||
scan_packed_string_runs (buf + hdr.cluster_start, snapshot_base + hdr.cluster_start, hdr.total_len - hdr.cluster_start, list, seen_addrs, ref_counter);
|
||||
scan_packed_string_runs (buf + hdr.cluster_start, snapshot_base + hdr.cluster_start, hdr.total_len - hdr.cluster_start, ss);
|
||||
free (buf);
|
||||
}
|
||||
|
||||
#define DART_SNAPSHOT_SCAN_MAX (64ULL << 20)
|
||||
#define DART_DATA_IMAGE_SCAN_MAX (8ULL << 20)
|
||||
|
||||
static void scan_string_buffer(const ut8 *buf, ut64 base, ut64 size, RList *list, HtUP *seen_addrs, ut64 *ref_counter) {
|
||||
if (!buf || !size || !list) {
|
||||
static void scan_string_buffer(const ut8 *buf, ut64 base, ut64 size, StringScan *ss) {
|
||||
if (!buf || !size) {
|
||||
return;
|
||||
}
|
||||
scan_ascii_strings (buf, base, size, list, seen_addrs, ref_counter);
|
||||
scan_utf16_strings (buf, base, size, list, seen_addrs, ref_counter);
|
||||
scan_ascii_strings (buf, base, size, ss);
|
||||
scan_utf16_strings (buf, base, size, ss);
|
||||
}
|
||||
|
||||
static void scan_snapshot_region(DartCtx *ctx, ut64 start, ut64 size, RList *list, HtUP *seen_addrs, ut64 *ref_counter) {
|
||||
if (!ctx || !ctx->core || !ctx->core->bin || !start || !size || !list || size > DART_SNAPSHOT_SCAN_MAX) {
|
||||
static void scan_snapshot_region(DartCtx *ctx, ut64 start, ut64 size, StringScan *ss) {
|
||||
if (!ctx || !ctx->core || !ctx->core->bin || !start || !size || size > DART_SNAPSHOT_SCAN_MAX) {
|
||||
return;
|
||||
}
|
||||
ut64 end = start + size;
|
||||
@@ -468,16 +475,16 @@ static void scan_snapshot_region(DartCtx *ctx, ut64 start, ut64 size, RList *lis
|
||||
free (buf);
|
||||
continue;
|
||||
}
|
||||
scan_string_buffer (buf, chunk_start, chunk_size, list, seen_addrs, ref_counter);
|
||||
scan_string_buffer (buf, chunk_start, chunk_size, ss);
|
||||
free (buf);
|
||||
}
|
||||
}
|
||||
|
||||
static void scan_strings_from_snapshot_window(DartCtx *ctx, ut64 snapshot_base, ut64 upper_bound, RList *list, HtUP *seen_addrs, ut64 *ref_counter) {
|
||||
if (!ctx || !snapshot_base || !list) {
|
||||
static void scan_strings_from_snapshot_window(DartCtx *ctx, ut64 snapshot_base, ut64 upper_bound, StringScan *ss) {
|
||||
if (!ctx || !snapshot_base) {
|
||||
return;
|
||||
}
|
||||
ut64 before_count = r_list_length (list);
|
||||
ut64 before_count = r_list_length (ss->list);
|
||||
DartSnapshotHeader sh;
|
||||
if (!dart_snapshot_header_read (ctx, snapshot_base, &sh)) {
|
||||
goto fallback;
|
||||
@@ -486,10 +493,10 @@ static void scan_strings_from_snapshot_window(DartCtx *ctx, ut64 snapshot_base,
|
||||
goto fallback;
|
||||
}
|
||||
if (ctx->compressed_word_size == 4) {
|
||||
scan_packed_strings_from_snapshot (ctx, snapshot_base, list, seen_addrs, ref_counter);
|
||||
scan_packed_strings_from_snapshot (ctx, snapshot_base, ss);
|
||||
return;
|
||||
}
|
||||
scan_snapshot_region (ctx, snapshot_base + sh.cluster_start, sh.total_len - sh.cluster_start, list, seen_addrs, ref_counter);
|
||||
scan_snapshot_region (ctx, snapshot_base + sh.cluster_start, sh.total_len - sh.cluster_start, ss);
|
||||
ut64 align = ctx->layout && ctx->layout->max_alignment? (ut64)ctx->layout->max_alignment: 16;
|
||||
if (align == 0) {
|
||||
align = 16;
|
||||
@@ -497,18 +504,18 @@ static void scan_strings_from_snapshot_window(DartCtx *ctx, ut64 snapshot_base,
|
||||
ut64 data_start = snapshot_base + ((sh.total_len + (align - 1)) & ~ (align - 1));
|
||||
ut64 data_end = upper_bound;
|
||||
if (data_end > data_start && (data_end - data_start) <= DART_DATA_IMAGE_SCAN_MAX) {
|
||||
scan_snapshot_region (ctx, data_start, data_end - data_start, list, seen_addrs, ref_counter);
|
||||
scan_snapshot_region (ctx, data_start, data_end - data_start, ss);
|
||||
}
|
||||
if ((ut64)r_list_length (list) > before_count) {
|
||||
if ((ut64)r_list_length (ss->list) > before_count) {
|
||||
return;
|
||||
}
|
||||
fallback:
|
||||
if (upper_bound > snapshot_base + 0x100 && (upper_bound - (snapshot_base + 0x100)) <= DART_DATA_IMAGE_SCAN_MAX) {
|
||||
scan_snapshot_region (ctx, snapshot_base + 0x100, upper_bound - (snapshot_base + 0x100), list, seen_addrs, ref_counter);
|
||||
scan_snapshot_region (ctx, snapshot_base + 0x100, upper_bound - (snapshot_base + 0x100), ss);
|
||||
}
|
||||
}
|
||||
|
||||
static void scan_utf16_strings(const ut8 *buf, ut64 base, ut64 size, RList *list, HtUP *seen_addrs, ut64 *ref_counter) {
|
||||
static void scan_utf16_strings(const ut8 *buf, ut64 base, ut64 size, StringScan *ss) {
|
||||
if (!buf || size < 8) {
|
||||
return;
|
||||
}
|
||||
@@ -536,7 +543,7 @@ static void scan_utf16_strings(const ut8 *buf, ut64 base, ut64 size, RList *list
|
||||
if (utf8 && units >= 4 && utf16le_has_ascii_profile (buf, start, start + (units * 2))) {
|
||||
ut32 ulen = (ut32)strlen (utf8);
|
||||
if (ulen >= 4 && ulen <= 512 && looks_like_text (utf8)) {
|
||||
append_string_info (list, seen_addrs, utf8, ulen, DART_STRING_TWO_BYTE, base + start, dart_string_classify_value (utf8), ref_counter);
|
||||
append_string_info (ss, utf8, ulen, DART_STRING_TWO_BYTE, base + start, dart_string_classify_value (utf8));
|
||||
}
|
||||
}
|
||||
r_strbuf_fini (&sb);
|
||||
@@ -583,16 +590,15 @@ RList *dart_pool_extract_strings(DartCtx *ctx) {
|
||||
return NULL;
|
||||
}
|
||||
RList *string_list = r_list_newf ((RListFree)dart_string_info_free);
|
||||
HtUP *seen_addrs = ht_up_new0 ();
|
||||
if (!seen_addrs) {
|
||||
return string_list;
|
||||
}
|
||||
RVecRBinSection *sections = r_bin_get_sections_vec (ctx->core->bin);
|
||||
if (!sections) {
|
||||
ht_up_free (seen_addrs);
|
||||
return string_list;
|
||||
}
|
||||
ut64 ref_counter = 0;
|
||||
StringScan ss = {
|
||||
.list = string_list,
|
||||
.seen_addrs = ht_up_new0 (),
|
||||
.limit = ctx->str_scan_limit
|
||||
};
|
||||
if (find_snapshots (ctx) == 0 && ctx->vm_data) {
|
||||
DartVerLayout layout_tmp;
|
||||
DartVerLayout *layout_owned = dart_ctx_init_layout (ctx, &layout_tmp);
|
||||
@@ -608,14 +614,14 @@ RList *dart_pool_extract_strings(DartCtx *ctx) {
|
||||
} else if (ctx->iso_instr > ctx->iso_data) {
|
||||
iso_upper = ctx->iso_instr;
|
||||
}
|
||||
scan_strings_from_snapshot_window (ctx, ctx->vm_data, vm_upper, string_list, seen_addrs, &ref_counter);
|
||||
scan_strings_from_snapshot_window (ctx, ctx->iso_data, iso_upper, string_list, seen_addrs, &ref_counter);
|
||||
scan_strings_from_snapshot_window (ctx, ctx->vm_data, vm_upper, &ss);
|
||||
scan_strings_from_snapshot_window (ctx, ctx->iso_data, iso_upper, &ss);
|
||||
if (r_list_length (string_list) == 0) {
|
||||
if (vm_upper > ctx->vm_data + 0x100) {
|
||||
scan_snapshot_region (ctx, ctx->vm_data + 0x100, vm_upper - (ctx->vm_data + 0x100), string_list, seen_addrs, &ref_counter);
|
||||
scan_snapshot_region (ctx, ctx->vm_data + 0x100, vm_upper - (ctx->vm_data + 0x100), &ss);
|
||||
}
|
||||
if (iso_upper > ctx->iso_data + 0x100) {
|
||||
scan_snapshot_region (ctx, ctx->iso_data + 0x100, iso_upper - (ctx->iso_data + 0x100), string_list, seen_addrs, &ref_counter);
|
||||
scan_snapshot_region (ctx, ctx->iso_data + 0x100, iso_upper - (ctx->iso_data + 0x100), &ss);
|
||||
}
|
||||
}
|
||||
dart_ctx_fini_layout (ctx, layout_owned);
|
||||
@@ -637,9 +643,9 @@ RList *dart_pool_extract_strings(DartCtx *ctx) {
|
||||
free (buf);
|
||||
continue;
|
||||
}
|
||||
scan_string_buffer (buf, sec->vaddr, size, string_list, seen_addrs, &ref_counter);
|
||||
scan_string_buffer (buf, sec->vaddr, size, &ss);
|
||||
free (buf);
|
||||
if (r_list_length (string_list) >= DART_STRING_SCAN_LIMIT) {
|
||||
if (string_scan_full (&ss)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -648,17 +654,17 @@ RList *dart_pool_extract_strings(DartCtx *ctx) {
|
||||
if (size > 0 && size <= DART_SNAPSHOT_SCAN_MAX) {
|
||||
ut8 *buf = (ut8 *)malloc ((size_t)size);
|
||||
if (buf && read_mem (ctx, 0, buf, (int)size)) {
|
||||
scan_string_buffer (buf, 0, size, string_list, seen_addrs, &ref_counter);
|
||||
scan_string_buffer (buf, 0, size, &ss);
|
||||
}
|
||||
free (buf);
|
||||
}
|
||||
}
|
||||
ht_up_free (seen_addrs);
|
||||
ht_up_free (ss.seen_addrs);
|
||||
r_list_sort (string_list, (RListComparator)string_info_addr_cmp);
|
||||
return string_list;
|
||||
}
|
||||
|
||||
RList *dart_pool_extract_object_pool_strings(DartCtx *ctx) {
|
||||
RList *dart_pool_extract_pool_strings(DartCtx *ctx) {
|
||||
if (!ctx || !ctx->core || !ctx->core->bin) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -667,7 +673,7 @@ RList *dart_pool_extract_object_pool_strings(DartCtx *ctx) {
|
||||
if (find_snapshots (ctx) == 0 && ctx->vm_data) {
|
||||
DartVerLayout layout_tmp;
|
||||
DartVerLayout *layout_owned = dart_ctx_init_layout (ctx, &layout_tmp);
|
||||
if (dart_modern_is_supported_snapshot (ctx)) {
|
||||
if (modern_supported (ctx)) {
|
||||
const ut64 snapshots[] = {
|
||||
ctx->vm_data,
|
||||
ctx->iso_data
|
||||
@@ -682,8 +688,8 @@ RList *dart_pool_extract_object_pool_strings(DartCtx *ctx) {
|
||||
if (!seen_refs) {
|
||||
continue;
|
||||
}
|
||||
const DartModernClusterRequest req = { ctx, sh.cluster_start, base + sh.total_len, sh.nc, sh.nb };
|
||||
(void)dart_modern_extract_object_pool_strings_from_clusters (&req, string_list, seen_refs, &ref_counter);
|
||||
const ModernReq req = { ctx, sh.cluster_start, base + sh.total_len, sh.nc, sh.nb };
|
||||
(void)modern_extract_pool_strings (&req, string_list, seen_refs, &ref_counter);
|
||||
ht_up_free (seen_refs);
|
||||
}
|
||||
}
|
||||
@@ -832,50 +838,80 @@ static void dump_string_text(RStrBuf *sb, const DartStringInfo *si, int fmt, boo
|
||||
}
|
||||
}
|
||||
|
||||
static bool string_matches(const DartCtx *ctx, const RRegex *rx, const DartStringInfo *si) {
|
||||
if (!si || !si->value) {
|
||||
return false;
|
||||
}
|
||||
if (ctx->str_minlen > 0 && si->length < (ut32)ctx->str_minlen) {
|
||||
return false;
|
||||
}
|
||||
if (ctx->str_maxlen > 0 && si->length > (ut32)ctx->str_maxlen) {
|
||||
return false;
|
||||
}
|
||||
return !rx || r_regex_exec (rx, si->value, 0, NULL, 0) == 0;
|
||||
}
|
||||
|
||||
static char *dump_strings_list(DartCtx *ctx, RList *strings, int fmt) {
|
||||
const bool refs = ctx && ctx->dump_string_refs;
|
||||
RRegex *rx = NULL;
|
||||
if (R_STR_ISNOTEMPTY (ctx->str_filter)) {
|
||||
rx = r_regex_new (ctx->str_filter, "e");
|
||||
if (!rx) {
|
||||
R_LOG_ERROR ("Invalid string filter regex: %s", ctx->str_filter);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
const bool refs = ctx->dump_string_refs;
|
||||
const bool quiet = ctx->quiet;
|
||||
PJ *pj = NULL;
|
||||
RStrBuf *sb = NULL;
|
||||
if (fmt == 'j') {
|
||||
if (!strings || r_list_length (strings) == 0) {
|
||||
return strdup ("[]");
|
||||
}
|
||||
PJ *pj = pj_new ();
|
||||
pj = pj_new ();
|
||||
pj_a (pj);
|
||||
RListIter *it;
|
||||
DartStringInfo *si;
|
||||
r_list_foreach (strings, it, si) {
|
||||
if (si) {
|
||||
dump_string_json (pj, si, refs);
|
||||
}
|
||||
}
|
||||
pj_end (pj);
|
||||
return pj_drain (pj);
|
||||
} else {
|
||||
sb = r_strbuf_new (fmt == 'r' && !quiet? "# Dart Strings\n": "");
|
||||
}
|
||||
if (!strings) {
|
||||
return strdup ("# No strings found\n");
|
||||
}
|
||||
const bool quiet = ctx && ctx->quiet;
|
||||
RStrBuf *sb = r_strbuf_new (fmt == 'r' && !quiet? "# Dart Strings\n": "");
|
||||
ut64 count = 0;
|
||||
RListIter *it;
|
||||
DartStringInfo *si;
|
||||
r_list_foreach (strings, it, si) {
|
||||
dump_string_text (sb, si, fmt, quiet, refs);
|
||||
if (ctx->str_limit && count >= ctx->str_limit) {
|
||||
break;
|
||||
}
|
||||
if (!string_matches (ctx, rx, si)) {
|
||||
continue;
|
||||
}
|
||||
if (pj) {
|
||||
dump_string_json (pj, si, refs);
|
||||
} else {
|
||||
dump_string_text (sb, si, fmt, quiet, refs);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
r_regex_free (rx);
|
||||
if (pj) {
|
||||
pj_end (pj);
|
||||
return pj_drain (pj);
|
||||
}
|
||||
if (fmt == 'r' && !quiet) {
|
||||
r_strbuf_appendf (sb, "# Total: %d strings\n", r_list_length (strings));
|
||||
r_strbuf_appendf (sb, "# Total: %" PFMT64u " strings\n", count);
|
||||
}
|
||||
return r_strbuf_drain (sb);
|
||||
}
|
||||
|
||||
char *dart_pool_dump_strings(DartCtx *ctx, int fmt) {
|
||||
RList *strings = dart_pool_extract_object_pool_strings (ctx);
|
||||
RList *strings = dart_pool_extract_pool_strings (ctx);
|
||||
char *out = dump_strings_list (ctx, strings, fmt);
|
||||
dart_string_list_free (strings);
|
||||
return out;
|
||||
}
|
||||
|
||||
char *dart_pool_dump_strings_fuzzy(DartCtx *ctx, int fmt) {
|
||||
// stop carving early only when every scanned string is going to be printed
|
||||
const bool filtered = ctx->str_minlen > 0 || ctx->str_maxlen > 0 || R_STR_ISNOTEMPTY (ctx->str_filter);
|
||||
ctx->str_scan_limit = filtered? 0: ctx->str_limit;
|
||||
DartRecoveryModel model = { 0 };
|
||||
dart_recovery_model_load (ctx, &model, DART_RECOVERY_STRINGS | DART_RECOVERY_CLASSES | DART_RECOVERY_STRING_REFS);
|
||||
ctx->str_scan_limit = 0;
|
||||
char *out = dump_strings_list (ctx, model.strings, fmt);
|
||||
dart_recovery_model_fini (&model);
|
||||
return out;
|
||||
|
||||
+405
-56
@@ -26,6 +26,19 @@ typedef struct {
|
||||
char *fn_name;
|
||||
} DartPoolUseInfo;
|
||||
|
||||
typedef struct {
|
||||
ut64 pp_off;
|
||||
RList *uses;
|
||||
} DartPpUseBucket;
|
||||
|
||||
typedef struct {
|
||||
DartCtx *ctx;
|
||||
HtUP *buckets;
|
||||
RList *xrefs;
|
||||
ut64 *count;
|
||||
ut64 limit;
|
||||
} DartSerializedPoolXrefCtx;
|
||||
|
||||
static void dart_xref_info_free(void *p) {
|
||||
DartXrefInfo *xi = (DartXrefInfo *)p;
|
||||
if (xi) {
|
||||
@@ -45,6 +58,12 @@ static void dart_pool_use_info_free(void *p) {
|
||||
free (ui);
|
||||
}
|
||||
|
||||
static void dart_pp_use_bucket_free(void *p) {
|
||||
DartPpUseBucket *bucket = (DartPpUseBucket *)p;
|
||||
r_list_free (bucket->uses);
|
||||
free (bucket);
|
||||
}
|
||||
|
||||
static char *xref_join_names(const char *owner, const char *name) {
|
||||
if (R_STR_ISNOTEMPTY (owner) && R_STR_ISNOTEMPTY (name)) {
|
||||
return r_str_newf ("%s.%s", owner, name);
|
||||
@@ -178,19 +197,18 @@ static void collect_data_image_xrefs(DartCtx *ctx, DartRecoveryModel *model, RLi
|
||||
}
|
||||
DartVerLayout layout_tmp;
|
||||
DartVerLayout *layout_owned = dart_ctx_init_layout (ctx, &layout_tmp);
|
||||
ut64 kAlign = ctx->layout && ctx->layout->max_alignment? (ut64)ctx->layout->max_alignment: 16;
|
||||
RList *seen_fields = r_list_newf (free);
|
||||
HtUP *seen_ep = ht_up_new0 ();
|
||||
DartSnapshotHeader sh;
|
||||
if (dart_snapshot_header_read (ctx, ctx->iso_data, &sh)) {
|
||||
ut64 data_image_base = ctx->iso_data + ((sh.total_len + (kAlign - 1)) & ~ (kAlign - 1));
|
||||
ut64 data_image_base = dart_snapshot_data_image_base (ctx->iso_data, sh.total_len);
|
||||
ut64 data_image_end = ctx->iso_instr? ctx->iso_instr: (data_image_base + (4ULL << 20));
|
||||
collect_field_scan_xrefs (ctx, model, xrefs, seen_fields, count, limit, data_image_base, data_image_end);
|
||||
collect_method_scan_xrefs (ctx, model, xrefs, seen_ep, count, limit, data_image_base, data_image_end);
|
||||
}
|
||||
if (ctx->vm_data && !xref_limit_reached (*count, limit) &&
|
||||
dart_snapshot_header_read (ctx, ctx->vm_data, &sh)) {
|
||||
ut64 vm_data_base = ctx->vm_data + ((sh.total_len + (kAlign - 1)) & ~ (kAlign - 1));
|
||||
ut64 vm_data_base = dart_snapshot_data_image_base (ctx->vm_data, sh.total_len);
|
||||
ut64 vm_data_end = ctx->vm_instr? ctx->vm_instr: (vm_data_base + (4ULL << 20));
|
||||
collect_field_scan_xrefs (ctx, model, xrefs, seen_fields, count, limit, vm_data_base, vm_data_end);
|
||||
collect_method_scan_xrefs (ctx, model, xrefs, seen_ep, count, limit, vm_data_base, vm_data_end);
|
||||
@@ -240,6 +258,191 @@ static bool xref_parse_pp_offset(const char *opstr, ut64 *out) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static char *xref_lower_dup(const char *s) {
|
||||
if (R_STR_ISEMPTY (s)) {
|
||||
return NULL;
|
||||
}
|
||||
char *d = strdup (s);
|
||||
for (char *p = d; *p; p++) {
|
||||
*p = (char)tolower ((ut8)*p);
|
||||
if (*p == '#') {
|
||||
*p = ' ';
|
||||
}
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
static void xref_skip_spaces(const char **p) {
|
||||
while (p && *p && (**p == ' ' || **p == '\t')) {
|
||||
(*p)++;
|
||||
}
|
||||
}
|
||||
|
||||
static bool xref_parse_reg_token(const char **p, int *out) {
|
||||
if (!p || !*p || !out) {
|
||||
return false;
|
||||
}
|
||||
xref_skip_spaces (p);
|
||||
if (**p != 'x' && **p != 'w') {
|
||||
return false;
|
||||
}
|
||||
(*p)++;
|
||||
if (!isdigit ((ut8) * *p)) {
|
||||
return false;
|
||||
}
|
||||
int reg = 0;
|
||||
while (isdigit ((ut8) * *p)) {
|
||||
reg = (reg * 10) + (**p - '0');
|
||||
(*p)++;
|
||||
}
|
||||
if (reg < 0 || reg > 30) {
|
||||
return false;
|
||||
}
|
||||
*out = reg;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool xref_parse_comma(const char **p) {
|
||||
xref_skip_spaces (p);
|
||||
if (!p || !*p || **p != ',') {
|
||||
return false;
|
||||
}
|
||||
(*p)++;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool xref_parse_number_token(const char **p, ut64 *out) {
|
||||
if (!p || !*p || !out) {
|
||||
return false;
|
||||
}
|
||||
xref_skip_spaces (p);
|
||||
bool neg = false;
|
||||
if (**p == '-') {
|
||||
neg = true;
|
||||
(*p)++;
|
||||
}
|
||||
const char *start = *p;
|
||||
int base = 10;
|
||||
if (r_str_startswith (start, "0x")) {
|
||||
base = 16;
|
||||
*p += 2;
|
||||
start = *p;
|
||||
while (isxdigit ((ut8) * *p)) {
|
||||
(*p)++;
|
||||
}
|
||||
} else {
|
||||
while (isdigit ((ut8) * *p)) {
|
||||
(*p)++;
|
||||
}
|
||||
}
|
||||
if (*p == start) {
|
||||
return false;
|
||||
}
|
||||
ut64 v = strtoull (start, NULL, base);
|
||||
*out = neg? (ut64) (- (st64)v): v;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool xref_parse_add_pp_offset(const char *text, const bool reg_valid[31], const ut64 reg_off[31], int *dst_out, ut64 *off_out) {
|
||||
char *lower = xref_lower_dup (text);
|
||||
if (!lower) {
|
||||
return false;
|
||||
}
|
||||
const char *p = lower;
|
||||
if (r_str_startswith (p, "add ")) {
|
||||
p += 4;
|
||||
}
|
||||
int dst = -1;
|
||||
int src = -1;
|
||||
ut64 imm = 0;
|
||||
bool ok = xref_parse_reg_token (&p, &dst) &&
|
||||
xref_parse_comma (&p) &&
|
||||
xref_parse_reg_token (&p, &src) &&
|
||||
xref_parse_comma (&p) &&
|
||||
xref_parse_number_token (&p, &imm);
|
||||
if (ok) {
|
||||
xref_skip_spaces (&p);
|
||||
if (*p == ',') {
|
||||
p++;
|
||||
xref_skip_spaces (&p);
|
||||
}
|
||||
if (r_str_startswith (p, "lsl")) {
|
||||
p += 3;
|
||||
ut64 shift = 0;
|
||||
if (xref_parse_number_token (&p, &shift) && shift < 64) {
|
||||
imm <<= shift;
|
||||
}
|
||||
}
|
||||
ut64 base = 0;
|
||||
if (src == 27) {
|
||||
base = 0;
|
||||
} else if (src >= 0 && src < 31 && reg_valid[src]) {
|
||||
base = reg_off[src];
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
if (ok) {
|
||||
if (dst_out) {
|
||||
*dst_out = dst;
|
||||
}
|
||||
if (off_out) {
|
||||
*off_out = base + imm;
|
||||
}
|
||||
}
|
||||
}
|
||||
free (lower);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool xref_parse_mem_pp_offset(const char *text, const bool reg_valid[31], const ut64 reg_off[31], ut64 *off_out) {
|
||||
char *lower = xref_lower_dup (text);
|
||||
if (!lower) {
|
||||
return false;
|
||||
}
|
||||
bool ok = false;
|
||||
const char *b = strchr (lower, '[');
|
||||
while (b && !ok) {
|
||||
const char *p = b + 1;
|
||||
int reg = -1;
|
||||
if (xref_parse_reg_token (&p, ®)) {
|
||||
ut64 base = 0;
|
||||
if (reg == 27) {
|
||||
ok = true;
|
||||
} else if (reg >= 0 && reg < 31 && reg_valid[reg]) {
|
||||
base = reg_off[reg];
|
||||
ok = true;
|
||||
}
|
||||
if (ok) {
|
||||
ut64 imm = 0;
|
||||
if (xref_parse_comma (&p)) {
|
||||
(void)xref_parse_number_token (&p, &imm);
|
||||
}
|
||||
if (off_out) {
|
||||
*off_out = base + imm;
|
||||
}
|
||||
}
|
||||
}
|
||||
b = strchr (b + 1, '[');
|
||||
}
|
||||
free (lower);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static int xref_parse_dest_reg(const char *text) {
|
||||
char *lower = xref_lower_dup (text);
|
||||
if (!lower) {
|
||||
return -1;
|
||||
}
|
||||
const char *p = lower;
|
||||
while (isalpha ((ut8)*p)) {
|
||||
p++;
|
||||
}
|
||||
int reg = -1;
|
||||
(void)xref_parse_reg_token (&p, ®);
|
||||
free (lower);
|
||||
return reg;
|
||||
}
|
||||
|
||||
static ut64 xref_object_pool_base(DartCtx *ctx) {
|
||||
if (!ctx || !ctx->core || !ctx->core->config) {
|
||||
return 0;
|
||||
@@ -295,80 +498,97 @@ static const char *xref_pool_flag_kind(const char *name, const char **dst_type,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool xref_pool_use_seen(HtUP *seen, ut64 at, ut64 pp_off) {
|
||||
ut64 key = at ^ (pp_off << 8) ^ (pp_off >> 8);
|
||||
if (ht_up_find (seen, key, NULL)) {
|
||||
static bool xref_pool_use_seen(HtUP *seen, ut64 at) {
|
||||
// Each instruction contributes at most one PP use. Key by its address so
|
||||
// distinct sites cannot collide through a lossy address/offset combination.
|
||||
if (ht_up_find (seen, at, NULL)) {
|
||||
return true;
|
||||
}
|
||||
ht_up_insert (seen, key, (void *)1);
|
||||
ht_up_insert (seen, at, (void *)1);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void collect_pool_uses_from_entry(DartCtx *ctx, const DartInstructionTableEntry *entry, RList *uses, HtUP *seen) {
|
||||
if (!ctx || !ctx->core || !entry || !entry->address || !uses || !seen) {
|
||||
// `end_addr` is the next entry's address, or the instructions-image end for
|
||||
// the last entry. Scan the whole payload without crossing into its neighbour.
|
||||
static void collect_pool_uses_from_entry(DartCtx *ctx, const DartInstructionTableEntry *entry, ut64 end_addr, RList *uses, HtUP *seen) {
|
||||
if (!ctx || !ctx->core || !entry || !entry->address || end_addr <= entry->address || !uses || !seen) {
|
||||
return;
|
||||
}
|
||||
r_strf_var (cmd, 128, "pdj 96 @ 0x%" PFMT64x, entry->address);
|
||||
char *s = r_core_cmd_str (ctx->core, cmd);
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
RJson *j = r_json_parse (s);
|
||||
if (!j) {
|
||||
free (s);
|
||||
return;
|
||||
}
|
||||
const RJson *ops = r_json_get (j, "ops");
|
||||
const RJson *arr = ops? ops: j;
|
||||
for (size_t i = 0;; i++) {
|
||||
const RJson *item = r_json_item (arr, i);
|
||||
if (!item) {
|
||||
// Decode the function's instructions one at a time via r_core_anal_op and
|
||||
// feed op->mnemonic to the operand parsers, rather than rendering the whole
|
||||
// window to `pdj` JSON and parsing that back. Same disassembly text, minus
|
||||
// the JSON build/parse round-trip; matches the -AAA path in flutter_analysis.
|
||||
bool reg_valid[31] = { false };
|
||||
ut64 reg_off[31] = { 0 };
|
||||
ut64 at = entry->address;
|
||||
while (at < end_addr) {
|
||||
RAnalOp *op = r_core_anal_op (ctx->core, at, R_ARCH_OP_MASK_BASIC | R_ARCH_OP_MASK_DISASM);
|
||||
if (!op || op->size <= 0 || (ut64)op->size > end_addr - at || R_STR_ISEMPTY (op->mnemonic)) {
|
||||
r_anal_op_free (op);
|
||||
break;
|
||||
}
|
||||
const char *opstr = r_json_get_str (item, "opstr");
|
||||
if (R_STR_ISEMPTY (opstr)) {
|
||||
opstr = r_json_get_str (item, "opcode");
|
||||
}
|
||||
const char *m = op->mnemonic;
|
||||
ut64 pp_off = 0;
|
||||
if (!xref_parse_pp_offset (opstr, &pp_off)) {
|
||||
continue;
|
||||
bool have_pp = xref_parse_pp_offset (m, &pp_off) ||
|
||||
xref_parse_mem_pp_offset (m, reg_valid, reg_off, &pp_off);
|
||||
if (have_pp && !xref_pool_use_seen (seen, at)) {
|
||||
DartPoolUseInfo *ui = R_NEW0 (DartPoolUseInfo);
|
||||
ui->at = at;
|
||||
ui->fn_index = entry->index;
|
||||
ui->pp_off = pp_off;
|
||||
ui->fn_name = R_STR_ISNOTEMPTY (entry->name)? strdup (entry->name): xref_it_label (entry->index);
|
||||
r_list_append (uses, ui);
|
||||
}
|
||||
ut64 at = (ut64)r_json_get_num (item, "offset");
|
||||
if (!at) {
|
||||
at = entry->address;
|
||||
int add_dst = -1;
|
||||
ut64 add_off = 0;
|
||||
if (xref_parse_add_pp_offset (m, reg_valid, reg_off, &add_dst, &add_off) && add_dst >= 0 && add_dst < 31) {
|
||||
reg_valid[add_dst] = true;
|
||||
reg_off[add_dst] = add_off;
|
||||
} else {
|
||||
const int dst = xref_parse_dest_reg (m);
|
||||
if (dst >= 0 && dst < 31) {
|
||||
reg_valid[dst] = false;
|
||||
}
|
||||
}
|
||||
if (xref_pool_use_seen (seen, at, pp_off)) {
|
||||
continue;
|
||||
}
|
||||
DartPoolUseInfo *ui = R_NEW0 (DartPoolUseInfo);
|
||||
ui->at = at;
|
||||
ui->fn_index = entry->index;
|
||||
ui->pp_off = pp_off;
|
||||
ui->fn_name = R_STR_ISNOTEMPTY (entry->name)? strdup (entry->name): xref_it_label (entry->index);
|
||||
r_list_append (uses, ui);
|
||||
at += op->size;
|
||||
r_anal_op_free (op);
|
||||
}
|
||||
r_json_free (j);
|
||||
free (s);
|
||||
}
|
||||
|
||||
static RList *collect_pool_uses(DartCtx *ctx, RVecDartInstructionTableEntry *entries, ut64 limit) {
|
||||
static ut64 xref_instructions_end(DartCtx *ctx) {
|
||||
ut64 size = 0;
|
||||
if (find_snapshots (ctx) != 0 || !ctx->iso_instr || !read_u64_at (ctx, ctx->iso_instr, &size) || size < DART_IMAGE_ALIGNMENT || size > UT64_MAX - ctx->iso_instr) {
|
||||
return 0;
|
||||
}
|
||||
const ut64 end = ctx->iso_instr + size;
|
||||
RIOMap *map = r_io_map_get_at (ctx->core->io, ctx->iso_instr);
|
||||
return map && end - 1 <= r_io_map_to (map)? end: 0;
|
||||
}
|
||||
|
||||
static RList *collect_pool_uses(DartCtx *ctx, RVecDartInstructionTableEntry *entries) {
|
||||
RList *uses = r_list_newf (dart_pool_use_info_free);
|
||||
if (!ctx || !entries || RVecDartInstructionTableEntry_length (entries) == 0) {
|
||||
return uses;
|
||||
}
|
||||
HtUP *seen = ht_up_new0 ();
|
||||
ut64 scanned = 0;
|
||||
ut64 scan_limit = limit > 0? limit: 4096;
|
||||
DartInstructionTableEntry *entry;
|
||||
R_VEC_FOREACH (entries, entry) {
|
||||
if (!entry->has_code || !entry->address) {
|
||||
const ut64 image_end = xref_instructions_end (ctx);
|
||||
const ut64 nentries = RVecDartInstructionTableEntry_length (entries);
|
||||
for (ut64 i = 0; i < nentries; i++) {
|
||||
const DartInstructionTableEntry *entry = RVecDartInstructionTableEntry_at (entries, i);
|
||||
if (!entry || !entry->address) {
|
||||
continue;
|
||||
}
|
||||
if (scan_limit > 0 && scanned >= scan_limit) {
|
||||
break;
|
||||
// Entries are emitted in ascending address order, so the next one with
|
||||
// a distinct address bounds this one, including discarded-Code entries.
|
||||
ut64 end_addr = image_end;
|
||||
for (ut64 k = i + 1; k < nentries; k++) {
|
||||
const DartInstructionTableEntry *nx = RVecDartInstructionTableEntry_at (entries, k);
|
||||
if (nx && nx->address > entry->address) {
|
||||
end_addr = image_end? R_MIN (image_end, nx->address): nx->address;
|
||||
break;
|
||||
}
|
||||
}
|
||||
collect_pool_uses_from_entry (ctx, entry, uses, seen);
|
||||
scanned++;
|
||||
collect_pool_uses_from_entry (ctx, entry, end_addr, uses, seen);
|
||||
}
|
||||
ht_up_free (seen);
|
||||
return uses;
|
||||
@@ -428,15 +648,101 @@ static bool resolve_pool_entry_xref(DartCtx *ctx, DartRecoveryModel *model, RLis
|
||||
return false;
|
||||
}
|
||||
|
||||
static void xref_serialized_pool_string_cb(const ModernPoolStrRef *info, void *user) {
|
||||
DartSerializedPoolXrefCtx *ux = (DartSerializedPoolXrefCtx *)user;
|
||||
if (!info || !ux || !ux->buckets || !ux->xrefs || !ux->count || xref_limit_reached (*ux->count, ux->limit)) {
|
||||
return;
|
||||
}
|
||||
if (!info->string_addr || R_STR_ISEMPTY (info->string_value)) {
|
||||
return;
|
||||
}
|
||||
DartPpUseBucket *bucket = ht_up_find (ux->buckets, info->pp_offset, NULL);
|
||||
if (!bucket || !bucket->uses) {
|
||||
return;
|
||||
}
|
||||
RListIter *it;
|
||||
DartPoolUseInfo *use;
|
||||
r_list_foreach (bucket->uses, it, use) {
|
||||
if (!use || xref_limit_reached (*ux->count, ux->limit)) {
|
||||
continue;
|
||||
}
|
||||
append_xref_info (ux->xrefs, ux->count, ux->limit, "code", "code.string", "code", use->fn_name, use->fn_index, use->at, "string", info->string_value, info->string_ref, info->string_addr);
|
||||
}
|
||||
}
|
||||
|
||||
static bool collect_serialized_object_pool_xrefs(DartCtx *ctx, RList *uses, RList *xrefs, ut64 *count, ut64 limit) {
|
||||
if (!ctx || !ctx->core || !uses || !xrefs || xref_limit_reached (*count, limit)) {
|
||||
return false;
|
||||
}
|
||||
if (find_snapshots (ctx) != 0) {
|
||||
return false;
|
||||
}
|
||||
HtUP *wanted = ht_up_new0 ();
|
||||
RList *buckets = r_list_newf (dart_pp_use_bucket_free);
|
||||
RListIter *it;
|
||||
DartPoolUseInfo *use;
|
||||
r_list_foreach (uses, it, use) {
|
||||
if (!use) {
|
||||
continue;
|
||||
}
|
||||
DartPpUseBucket *bucket = ht_up_find (wanted, use->pp_off, NULL);
|
||||
if (!bucket) {
|
||||
bucket = R_NEW0 (DartPpUseBucket);
|
||||
bucket->pp_off = use->pp_off;
|
||||
bucket->uses = r_list_new ();
|
||||
r_list_append (buckets, bucket);
|
||||
ht_up_insert (wanted, use->pp_off, bucket);
|
||||
}
|
||||
r_list_append (bucket->uses, use);
|
||||
}
|
||||
DartVerLayout layout_tmp;
|
||||
DartVerLayout *layout_owned = ctx->layout? NULL: dart_ctx_init_layout (ctx, &layout_tmp);
|
||||
DartSerializedPoolXrefCtx ux = {
|
||||
.ctx = ctx,
|
||||
.buckets = wanted,
|
||||
.xrefs = xrefs,
|
||||
.count = count,
|
||||
.limit = limit,
|
||||
};
|
||||
bool ok = false;
|
||||
const ut64 snapshots[] = {
|
||||
ctx->vm_data,
|
||||
ctx->iso_data
|
||||
};
|
||||
for (size_t i = 0; i < R_ARRAY_SIZE (snapshots) && !xref_limit_reached (*count, limit); i++) {
|
||||
ut64 base = snapshots[i];
|
||||
DartSnapshotHeader sh = { 0 };
|
||||
if (!base || !dart_snapshot_header_read (ctx, base, &sh) || !sh.ok) {
|
||||
continue;
|
||||
}
|
||||
const ModernReq req = {
|
||||
.ctx = ctx,
|
||||
.cluster_start = sh.cluster_start,
|
||||
.cluster_end = base + sh.total_len,
|
||||
.num_clusters = sh.nc,
|
||||
.num_base_objects = sh.nb,
|
||||
};
|
||||
ok |= modern_collect_pool_strrefs (&req, wanted, xref_serialized_pool_string_cb, &ux);
|
||||
}
|
||||
if (layout_owned) {
|
||||
dart_ctx_fini_layout (ctx, layout_owned);
|
||||
}
|
||||
r_list_free (buckets);
|
||||
ht_up_free (wanted);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void collect_object_pool_xrefs(DartCtx *ctx, DartRecoveryModel *model, RList *xrefs, ut64 *count, ut64 limit) {
|
||||
if (!ctx || !ctx->core || !model || !model->it_entries || !xrefs || xref_limit_reached (*count, limit)) {
|
||||
return;
|
||||
}
|
||||
RList *uses = collect_pool_uses (ctx, model->it_entries);
|
||||
(void)collect_serialized_object_pool_xrefs (ctx, uses, xrefs, count, limit);
|
||||
ut64 pool_base = xref_object_pool_base (ctx);
|
||||
if (!pool_base) {
|
||||
r_list_free (uses);
|
||||
return;
|
||||
}
|
||||
RList *uses = collect_pool_uses (ctx, model->it_entries, limit);
|
||||
ut64 heap_base = xref_flag_addr (ctx->core, "app.heap_base");
|
||||
RListIter *it;
|
||||
DartPoolUseInfo *use;
|
||||
@@ -462,7 +768,11 @@ static RList *dart_pool_extract_xrefs(DartCtx *ctx) {
|
||||
const ut64 limit = ctx->dump_fns_limit > 0? (ut64)ctx->dump_fns_limit: 0;
|
||||
ut64 count = 0;
|
||||
DartRecoveryModel model = { 0 };
|
||||
dart_recovery_model_load (ctx, &model, DART_RECOVERY_STRINGS | DART_RECOVERY_CLASSES | DART_RECOVERY_CLASS_FIELDS | DART_RECOVERY_IT);
|
||||
dart_recovery_model_load (ctx, &model, DART_RECOVERY_STRINGS | DART_RECOVERY_CLASSES | DART_RECOVERY_CLASS_FIELDS);
|
||||
// Even limited output needs the full table to bound every scanned payload.
|
||||
DartCtx work = *ctx;
|
||||
work.dump_fns_limit = 0;
|
||||
model.it_entries = dart_pool_extract_instruction_table (&work);
|
||||
RList *classes = model.classes;
|
||||
RVecDartInstructionTableEntry *it_entries = model.it_entries;
|
||||
if (classes) {
|
||||
@@ -693,3 +1003,42 @@ char *dart_pool_dump_xrefs(DartCtx *ctx, int fmt) {
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Apply the recovered xref graph to the RCore as real r2 xrefs. Unlike the
|
||||
// analysis disassembly scan, which only sees loads inside functions, this also
|
||||
// surfaces references that live in the data image (e.g. a const object in
|
||||
// .rodata pointing at a string), so `axt` resolves pool-referenced strings that
|
||||
// no instruction loads directly. Returns the number of xrefs applied.
|
||||
int dart_pool_apply_xrefs(DartCtx *ctx) {
|
||||
if (!ctx || !ctx->core || !ctx->core->anal) {
|
||||
return 0;
|
||||
}
|
||||
// The reliable ObjectPool strings carry their reference sites (source_addr),
|
||||
// including references that live in the data image (e.g. a const object in
|
||||
// .rodata pointing at the string). The analysis disassembly scan only sees
|
||||
// loads inside functions, so without this `axt <string>` is empty for
|
||||
// strings no instruction loads directly.
|
||||
RList *strings = dart_pool_extract_pool_strings (ctx);
|
||||
if (!strings) {
|
||||
return 0;
|
||||
}
|
||||
int applied = 0;
|
||||
RListIter *it;
|
||||
DartStringInfo *si;
|
||||
r_list_foreach (strings, it, si) {
|
||||
if (!si || !si->address || !si->references) {
|
||||
continue;
|
||||
}
|
||||
RListIter *rit;
|
||||
DartStringRef *sr;
|
||||
r_list_foreach (si->references, rit, sr) {
|
||||
if (!sr || sr->source_addr == 0) {
|
||||
continue;
|
||||
}
|
||||
r_anal_xrefs_set (ctx->core->anal, sr->source_addr, si->address, R_ANAL_REF_TYPE_STRN | R_ANAL_REF_TYPE_READ);
|
||||
applied++;
|
||||
}
|
||||
}
|
||||
dart_string_list_free (strings);
|
||||
return applied;
|
||||
}
|
||||
+4
-4
@@ -190,7 +190,7 @@ static void sbom_add_snapshot_runtime(DartCtx *ctx, RList *components) {
|
||||
}
|
||||
}
|
||||
}
|
||||
const char *version = dart_ctx_effective_version (ctx);
|
||||
const char *version = dart_ctx_version (ctx);
|
||||
char evidence[96];
|
||||
snprintf (evidence, sizeof (evidence), "snapshot_hash=%s", ctx->snapshot_hash[0]? ctx->snapshot_hash: "unknown");
|
||||
sbom_add_component (components, "runtime", "dart-sdk", version, "snapshot_header", version? 100: 70, evidence);
|
||||
@@ -359,7 +359,7 @@ static void sbom_scan_pubspec_lock(RList *components, const char *path) {
|
||||
bool in_packages = false;
|
||||
char *current = NULL;
|
||||
char *saveptr = NULL;
|
||||
for (char *line = strtok_r (yaml, "\n", &saveptr); line; line = strtok_r (NULL, "\n", &saveptr)) {
|
||||
for (char *line = r_str_tok_r (yaml, "\n", &saveptr); line; line = r_str_tok_r (NULL, "\n", &saveptr)) {
|
||||
if (!strcmp (line, "packages:")) {
|
||||
in_packages = true;
|
||||
continue;
|
||||
@@ -470,7 +470,7 @@ static void sbom_dump_component_json(PJ *pj, const DartSbomComponent *c) {
|
||||
}
|
||||
|
||||
static char *sbom_dump_json(DartCtx *ctx, RList *components, const char *input_path) {
|
||||
const char *version = dart_ctx_effective_version (ctx);
|
||||
const char *version = dart_ctx_version (ctx);
|
||||
PJ *pj = pj_new ();
|
||||
pj_o (pj);
|
||||
pj_ks (pj, "format", "r2flutter-recovered-sbom");
|
||||
@@ -530,7 +530,7 @@ static char *sbom_dump_text(DartCtx *ctx, RList *components) {
|
||||
const bool quiet = ctx && ctx->quiet;
|
||||
RStrBuf *sb = r_strbuf_new ("");
|
||||
if (!quiet) {
|
||||
const char *version = dart_ctx_effective_version (ctx);
|
||||
const char *version = dart_ctx_version (ctx);
|
||||
r_strbuf_append (sb, "# Dart/Flutter Recovered Components\n");
|
||||
r_strbuf_append (sb, "# complete: false\n");
|
||||
r_strbuf_append (sb, "# versions: explicit metadata only; AOT snapshots usually do not serialize pub package versions\n");
|
||||
|
||||
+88
-31
@@ -2,7 +2,7 @@
|
||||
|
||||
// Dart SDK version detection and layout profiles
|
||||
//
|
||||
// Supported Dart SDK versions: 2.10.0 - 3.12.2
|
||||
// Supported Dart SDK versions: 2.10.0 - 3.14.0
|
||||
// Based on Dart SDK source tags plus external snapshot-hash references.
|
||||
|
||||
#include <r_core.h>
|
||||
@@ -45,6 +45,7 @@ static const struct {
|
||||
{ "8ee4ef7a67df9845fba331734198a953", "2.10.0" },
|
||||
{ "d979fa911b038a8682e98cc8c8569261", "2.10.0" },
|
||||
// Dart 2.12.x
|
||||
{ "5b97292b25f0a715613b7a28e0734f77", "2.12.0" },
|
||||
{ "e036be9d5df68283e1a043e348fa7ff4", "2.12.0" },
|
||||
// Dart 2.13.x
|
||||
{ "0c7f1aa25fc597a7b3b9a19ed390afa1", "2.13.0" },
|
||||
@@ -124,31 +125,42 @@ static const struct {
|
||||
// Dart 3.12.x
|
||||
{ "41be3daaabd524b8aa7423bc24584957", "3.12.0" },
|
||||
{ "ace654289f5abc240509fc941453ebc5", "3.12.1" },
|
||||
// Dart 3.13.x
|
||||
{ "0451907c2eaa8467e848c0067bfe8ed4", "3.13.0" },
|
||||
// Dart 3.14.x
|
||||
{ "7a1ea3f6f5cf1089a7f6e55d7f20dbfd", "3.14.0" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
// Range-start version profiles. CID tables are generated in dart_cid.c.
|
||||
static const DartVerLayout version_profiles[] = {
|
||||
{ "", "2.10.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_INT32, 4 }, // Dart 2.10.0
|
||||
{ "", "2.12.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_INT32, 4 }, // Dart 2.12.0
|
||||
{ "", "2.13.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_INT32, 5 }, // Dart 2.13.0
|
||||
{ "", "2.14.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5 }, // Dart 2.14.0
|
||||
{ "", "2.15.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5 }, // Dart 2.15.0
|
||||
{ "", "2.16.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 6 }, // Dart 2.16.0
|
||||
{ "", "2.17.0", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 6 }, // Dart 2.17.0
|
||||
{ "", "2.18.0", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5 }, // Dart 2.18.0
|
||||
{ "", "2.18.2", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5 }, // Dart 2.18.2
|
||||
{ "", "2.18.3", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5 }, // Dart 2.18.3
|
||||
{ "", "2.19.0", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5 }, // Dart 2.19.0
|
||||
{ "", "3.0.0", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5 }, // Dart 3.0.0
|
||||
{ "", "3.1.0", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5 }, // Dart 3.1.0
|
||||
{ "", "3.2.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5 }, // Dart 3.2.0
|
||||
{ "", "3.3.0", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5 }, // Dart 3.3.0
|
||||
{ "", "3.4.0", 8, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5 }, // Dart 3.4.0
|
||||
{ "", "3.5.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5 }, // Dart 3.5.0
|
||||
{ "", "3.6.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5 }, // Dart 3.6.0
|
||||
{ "", "3.9.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5 }, // Dart 3.9.0
|
||||
{ { 0 }, NULL, 0, 0, 0, 0, 0, 0 }
|
||||
{ "", "2.10.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_INT32, 4, DART_HEADER_STYLE_210, DART_REF_ENCODING_UNSIGNED, DART_POOL_ENCODING_LOW7, DART_CODE_ALLOC_DEFERRED_ONLY, 7, 0, true, false, false, false, false }, // Dart 2.10.0
|
||||
{ "", "2.12.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_INT32, 5, DART_HEADER_STYLE_212, DART_REF_ENCODING_UNSIGNED, DART_POOL_ENCODING_LOW7, DART_CODE_ALLOC_DEFERRED_ONLY, 7, 0, true, false, false, false, false }, // Dart 2.12.0
|
||||
{ "", "2.13.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_INT32, 5, DART_HEADER_STYLE_212, DART_REF_ENCODING_UNSIGNED, DART_POOL_ENCODING_LOW7, DART_CODE_ALLOC_DEFERRED_ONLY, 7, 0, true, false, false, false, false }, // Dart 2.13.0
|
||||
{ "", "2.14.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_214, DART_REF_ENCODING_UNSIGNED, DART_POOL_ENCODING_LOW7, DART_CODE_ALLOC_STATE_DEFERRED, 7, 0, true, false, false, false, false }, // Dart 2.14.0
|
||||
{ "", "2.15.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_214, DART_REF_ENCODING_UNSIGNED, DART_POOL_ENCODING_LOW7, DART_CODE_ALLOC_STATE_DEFERRED, 7, 0, true, false, false, false, false }, // Dart 2.15.0
|
||||
{ "", "2.16.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 6, DART_HEADER_STYLE_216, DART_REF_ENCODING_UNSIGNED, DART_POOL_ENCODING_LOW7, DART_CODE_ALLOC_STATE_DEFERRED, 7, 0, false, false, false, false, false }, // Dart 2.16.0
|
||||
{ "", "2.17.0", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 6, DART_HEADER_STYLE_216, DART_REF_ENCODING_UNSIGNED, DART_POOL_ENCODING_LOW7, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false, false, false, false, false }, // Dart 2.17.0
|
||||
{ "", "2.18.0", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_LOW7, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false, false, false, false, false }, // Dart 2.18.0
|
||||
{ "", "2.18.2", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_LOW7, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false, false, false, false, false }, // Dart 2.18.2
|
||||
{ "", "2.18.3", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_LOW7, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false, false, false, false, false }, // Dart 2.18.3
|
||||
{ "", "2.19.0", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_LOW7, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false, false, false, false, true }, // Dart 2.19.0
|
||||
{ "", "3.0.0", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_LOW7, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false, false, false, false, true }, // Dart 3.0.0
|
||||
{ "", "3.1.0", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_LOW7, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false, false, false, false, true }, // Dart 3.1.0
|
||||
{ "", "3.2.0", 8, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_LOW7_SWAPPED, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false, false, false, false, true }, // Dart 3.2.0
|
||||
{ "", "3.3.0", 4, 1, 16, 20000, DART_TAG_STYLE_CID_SHIFT1, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false, false, false, false, true }, // Dart 3.3.0
|
||||
{ "", "3.4.0", 8, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false, false, false, false, true }, // Dart 3.4.0
|
||||
{ "", "3.5.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false, false, false, false, true }, // Dart 3.5.0
|
||||
{ "", "3.6.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false, false, false, false, true }, // Dart 3.6.0
|
||||
{ "", "3.7.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false, false, false, false, true }, // Dart 3.7.0
|
||||
{ "", "3.8.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false, false, false, false, true }, // Dart 3.8.0
|
||||
{ "", "3.9.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false, false, false, false, true }, // Dart 3.9.0
|
||||
{ "", "3.10.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false, false, false, false, true }, // Dart 3.10.0
|
||||
{ "", "3.11.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false, false, false, false, true }, // Dart 3.11.0
|
||||
{ "", "3.12.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN, DART_CODE_ALLOC_STATE_DEFERRED, 6, 0, false, false, false, false, true }, // Dart 3.12.0
|
||||
{ "", "3.13.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN, DART_CODE_ALLOC_STATE_DEFERRED, 6, 2, false, true, true, true, true }, // Dart 3.13.0
|
||||
{ "", "3.14.0", 4, 1, 16, 20000, DART_TAG_STYLE_OBJECT_HEADER, 5, DART_HEADER_STYLE_MODERN, DART_REF_ENCODING_REF_ID128, DART_POOL_ENCODING_MODERN, DART_CODE_ALLOC_STATE_DEFERRED, 6, 2, false, true, true, true, true }, // Dart 3.14.0
|
||||
{ { 0 }, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
const char *dart_version_from_hash(const char *hash) {
|
||||
@@ -163,6 +175,51 @@ const char *dart_version_from_hash(const char *hash) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const DartVerLayout *dart_newest_profile(void) {
|
||||
const DartVerLayout *newest = version_profiles;
|
||||
for (int i = 0; version_profiles[i].dart_version; i++) {
|
||||
newest = &version_profiles[i];
|
||||
}
|
||||
return newest;
|
||||
}
|
||||
|
||||
const DartVerLayout *dart_fingerprint_profile(void) {
|
||||
const DartVerLayout *baseline = version_profiles;
|
||||
for (int i = 0; version_profiles[i].dart_version; i++) {
|
||||
if (!version_profiles[i].single_snapshot) {
|
||||
baseline = &version_profiles[i];
|
||||
}
|
||||
}
|
||||
return baseline;
|
||||
}
|
||||
|
||||
const char *dart_version_source_str(int source) {
|
||||
switch (source) {
|
||||
case DART_VERSION_SOURCE_EXACT:
|
||||
return "exact-hash";
|
||||
case DART_VERSION_SOURCE_OVERRIDE:
|
||||
return "override";
|
||||
case DART_VERSION_SOURCE_PROBE:
|
||||
return "structural-probe";
|
||||
case DART_VERSION_SOURCE_FINGERPRINT:
|
||||
return "fingerprint";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
int dart_profile_count(void) {
|
||||
int count = 0;
|
||||
while (version_profiles[count].dart_version) {
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
const DartVerLayout *dart_profile_at(int index) {
|
||||
return index >= 0 && index < dart_profile_count ()? &version_profiles[index]: NULL;
|
||||
}
|
||||
|
||||
const DartVerLayout *dart_profile_from_version(const char *version) {
|
||||
if (!version) {
|
||||
return NULL;
|
||||
@@ -181,7 +238,7 @@ const DartVerLayout *dart_profile_from_version(const char *version) {
|
||||
return best? best: version_profiles;
|
||||
}
|
||||
|
||||
DartVerLayout *dart_pick_layout_by_hash(const char *hash) {
|
||||
DartVerLayout *dart_layout_from_hash(const char *hash) {
|
||||
DartVerLayout *dvl = R_NEW0 (DartVerLayout);
|
||||
const char *version = dart_version_from_hash (hash);
|
||||
const DartVerLayout *profile = version? dart_profile_from_version (version): NULL;
|
||||
@@ -195,16 +252,16 @@ DartVerLayout *dart_pick_layout_by_hash(const char *hash) {
|
||||
}
|
||||
return dvl;
|
||||
}
|
||||
// Default to v3.9.2 layout for unknown hashes
|
||||
dvl->compressed_word_size = 4;
|
||||
dvl->heap_object_tag = 1;
|
||||
dvl->max_alignment = 16;
|
||||
dvl->it_cap = 20000;
|
||||
dvl->tag_style = DART_TAG_STYLE_OBJECT_HEADER;
|
||||
dvl->header_fields = 5;
|
||||
dvl->dart_version = "unknown";
|
||||
// A single-snapshot profile changes discovery as well as cluster framing, so
|
||||
// an unknown hash must not inherit one without structural evidence. Keep the
|
||||
// newest compatible split-snapshot baseline until probing selects a layout.
|
||||
const DartVerLayout *baseline = dart_fingerprint_profile ();
|
||||
memcpy (dvl, baseline, sizeof (DartVerLayout));
|
||||
if (hash && *hash) {
|
||||
r_str_ncpy (dvl->hash, hash, sizeof (dvl->hash));
|
||||
}
|
||||
if (G_VERBOSE > 0) {
|
||||
fprintf (stderr, "[r2flutter] Unknown snapshot hash, using v3.9.2 defaults (hash=%s)\n", hash? hash: "(null)");
|
||||
fprintf (stderr, "[r2flutter] Unknown snapshot hash, fingerprinting as compatible profile %s (hash=%s)\n", baseline->dart_version, hash? hash: "(null)");
|
||||
}
|
||||
return dvl;
|
||||
}
|
||||
|
||||
+12
-4
@@ -1,9 +1,13 @@
|
||||
CC ?= gcc
|
||||
CFLAGS += -Wall -Wextra -O2 -fPIC
|
||||
CFLAGS += -I../../include -I../../include/r2flutter
|
||||
CFLAGS += $(shell pkg-config --cflags r_core 2>/dev/null || echo "-I/usr/local/include/libr")
|
||||
R2_CFLAGS ?= $(shell r2 -H R2_CFLAGS 2>/dev/null)
|
||||
R2_LDFLAGS ?= $(shell r2 -H R2_LDFLAGS 2>/dev/null)
|
||||
R2_LIBS ?= $(shell r2 -H R2_LIBS 2>/dev/null)
|
||||
R2_PLUGDIR ?= $(shell r2 -H R2_USER_PLUGINS)
|
||||
LDFLAGS += $(shell pkg-config --libs r_core 2>/dev/null || echo "-L/usr/local/lib -lr_core -lr_util -ldl")
|
||||
CFLAGS += $(R2_CFLAGS)
|
||||
LDFLAGS += $(R2_LDFLAGS)
|
||||
R2_PLUGIN_LIBS = $(R2_LIBS)
|
||||
DEPFLAGS = -MMD -MP
|
||||
|
||||
# Detect platform for shared library extension
|
||||
@@ -11,6 +15,9 @@ UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
EXT_SO = dylib
|
||||
LDFLAGS += -dynamiclib -undefined dynamic_lookup
|
||||
# Resolve radare2 symbols from the host. Linking a second installation's
|
||||
# libr_* dylibs into a core plugin corrupts ABI-compatible host structures.
|
||||
R2_PLUGIN_LIBS =
|
||||
else
|
||||
EXT_SO = so
|
||||
LDFLAGS += -shared
|
||||
@@ -25,11 +32,12 @@ CORE_PLUGIN = core_flutter.$(EXT_SO)
|
||||
# Library
|
||||
R2FLUTTER_LIB_DIR = ../../build
|
||||
R2FLUTTER_LIB = $(R2FLUTTER_LIB_DIR)/libr2flutter.a
|
||||
R2FLUTTER_LIB_TARGET = build/libr2flutter.a
|
||||
|
||||
all: $(CORE_PLUGIN)
|
||||
|
||||
$(CORE_PLUGIN): $(CORE_OBJ) $(R2FLUTTER_LIB)
|
||||
$(CC) $(LDFLAGS) -o $@ $(CORE_OBJ) $(R2FLUTTER_LIB)
|
||||
$(CC) $(LDFLAGS) -o $@ $(CORE_OBJ) $(R2FLUTTER_LIB) $(R2_PLUGIN_LIBS) $(LDLIBS)
|
||||
|
||||
core_flutter.o: core_flutter.c
|
||||
$(CC) $(CFLAGS) $(DEPFLAGS) -c $< -o $@
|
||||
@@ -38,7 +46,7 @@ flutter_analysis.o: flutter_analysis.c flutter_analysis.h
|
||||
$(CC) $(CFLAGS) $(DEPFLAGS) -c $< -o $@
|
||||
|
||||
$(R2FLUTTER_LIB):
|
||||
$(MAKE) -C ../.. $(R2FLUTTER_LIB_DIR)/libr2flutter.a
|
||||
$(MAKE) -C ../.. $(R2FLUTTER_LIB_TARGET)
|
||||
|
||||
user-install: $(CORE_PLUGIN)
|
||||
mkdir -p $(R2_PLUGDIR)
|
||||
|
||||
+125
-30
@@ -12,6 +12,10 @@
|
||||
#define R2FLUTTER_CFG_MAPFILE "r2flutter.mapfile"
|
||||
#define R2FLUTTER_CFG_NAMEPOOL "r2flutter.namepool"
|
||||
#define R2FLUTTER_CFG_PROFILE "r2flutter.profile"
|
||||
#define R2FLUTTER_CFG_STR_LIMIT "r2flutter.str.limit"
|
||||
#define R2FLUTTER_CFG_STR_MINLEN "r2flutter.str.minlen"
|
||||
#define R2FLUTTER_CFG_STR_MAXLEN "r2flutter.str.maxlen"
|
||||
#define R2FLUTTER_CFG_STR_FILTER "r2flutter.str.filter"
|
||||
|
||||
typedef struct {
|
||||
char action;
|
||||
@@ -21,6 +25,8 @@ typedef struct {
|
||||
int string_depth;
|
||||
bool string_refs;
|
||||
bool help;
|
||||
bool subcmd_help;
|
||||
char invalid_flag;
|
||||
char *obf_map_path;
|
||||
char *object_spec;
|
||||
} R2FlutterCmd;
|
||||
@@ -34,6 +40,14 @@ static bool r2flutter_core_init(RCorePluginSession *cps) {
|
||||
r_config_node_desc (node, "Enable heuristic name-pool fallback resolution");
|
||||
node = r_config_set (cfg, R2FLUTTER_CFG_PROFILE, "");
|
||||
r_config_node_desc (node, "Override Dart snapshot profile by 32-byte hash or Dart version");
|
||||
node = r_config_set_i (cfg, R2FLUTTER_CFG_STR_LIMIT, 0);
|
||||
r_config_node_desc (node, "Maximum number of strings to print (0 = unlimited)");
|
||||
node = r_config_set_i (cfg, R2FLUTTER_CFG_STR_MINLEN, 0);
|
||||
r_config_node_desc (node, "Skip printing strings shorter than this length");
|
||||
node = r_config_set_i (cfg, R2FLUTTER_CFG_STR_MAXLEN, 0);
|
||||
r_config_node_desc (node, "Skip printing strings longer than this length (0 = unlimited)");
|
||||
node = r_config_set (cfg, R2FLUTTER_CFG_STR_FILTER, "");
|
||||
r_config_node_desc (node, "Only print strings matching this extended regex");
|
||||
r_config_lock (cfg, true);
|
||||
return true;
|
||||
}
|
||||
@@ -47,6 +61,10 @@ static bool r2flutter_core_fini(RCorePluginSession *cps) {
|
||||
r_config_rm (cfg, R2FLUTTER_CFG_MAPFILE);
|
||||
r_config_rm (cfg, R2FLUTTER_CFG_NAMEPOOL);
|
||||
r_config_rm (cfg, R2FLUTTER_CFG_PROFILE);
|
||||
r_config_rm (cfg, R2FLUTTER_CFG_STR_LIMIT);
|
||||
r_config_rm (cfg, R2FLUTTER_CFG_STR_MINLEN);
|
||||
r_config_rm (cfg, R2FLUTTER_CFG_STR_MAXLEN);
|
||||
r_config_rm (cfg, R2FLUTTER_CFG_STR_FILTER);
|
||||
r_config_lock (cfg, true);
|
||||
#endif
|
||||
return true;
|
||||
@@ -55,44 +73,70 @@ static bool r2flutter_core_fini(RCorePluginSession *cps) {
|
||||
static void r2flutter_apply_config(RCore *core, DartCtx *dctx) {
|
||||
dctx->obf_map_path = r_config_get (core->config, R2FLUTTER_CFG_MAPFILE);
|
||||
dctx->use_name_pool = r_config_get_b (core->config, R2FLUTTER_CFG_NAMEPOOL);
|
||||
dctx->str_limit = r_config_get_i (core->config, R2FLUTTER_CFG_STR_LIMIT);
|
||||
dctx->str_minlen = (int)r_config_get_i (core->config, R2FLUTTER_CFG_STR_MINLEN);
|
||||
dctx->str_maxlen = (int)r_config_get_i (core->config, R2FLUTTER_CFG_STR_MAXLEN);
|
||||
dctx->str_filter = r_config_get (core->config, R2FLUTTER_CFG_STR_FILTER);
|
||||
const char *profile = r_config_get (core->config, R2FLUTTER_CFG_PROFILE);
|
||||
if (R_STR_ISNOTEMPTY (profile) && !dart_ctx_set_profile_override (dctx, profile)) {
|
||||
R_LOG_WARN ("Ignoring unsupported Dart snapshot profile override: %s", profile);
|
||||
}
|
||||
}
|
||||
|
||||
static void r2flutter_help(RCore *core);
|
||||
|
||||
static void r2flutter_subcmd_help(RCore *core, char action) {
|
||||
switch (action) {
|
||||
case 'i':
|
||||
r_cons_printf (core->cons,
|
||||
"Usage: r2flutter -i[jr*] dump instruction table entries\n"
|
||||
"| -i plain text output\n"
|
||||
"| -ie dump Dart code entrypoint; with -* mark instruction snapshots as dword arrays\n"
|
||||
"| -ij JSON output\n"
|
||||
"| -ir, -i* r2 commands output\n");
|
||||
break;
|
||||
case 'e':
|
||||
r_cons_printf (core->cons,
|
||||
"Usage: r2flutter -ie[jr*] dump Dart code entrypoint\n"
|
||||
"| -ie plain text output\n"
|
||||
"| -iej JSON output\n"
|
||||
"| -ier, -ie* r2 commands output; mark instruction snapshots as dword arrays\n");
|
||||
break;
|
||||
default:
|
||||
r2flutter_help (core);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void r2flutter_help(RCore *core) {
|
||||
r_cons_printf (core->cons,
|
||||
"Usage: r2flutter [options] <action>\n"
|
||||
"| r2flutter show same -h help message\n"
|
||||
"| r2flutter -A analyze dart snapshot and apply flags/comments\n"
|
||||
"| r2flutter -AA analyze with field extraction enabled\n"
|
||||
"| r2flutter -AAA run Dart-aware code analysis and recover code refs\n"
|
||||
"| r2flutter -c dump classes\n"
|
||||
"Usage: r2flutter [qjr*] <action>\n"
|
||||
"| r2flutter -A apply flags, function names and signatures\n"
|
||||
"| r2flutter -AA also seed anal.gp and apply ObjectPool xrefs\n"
|
||||
"| r2flutter -AAA also run the full register-tracking scan (slow)\n"
|
||||
"| r2flutter -c[jr*] dump classes\n"
|
||||
"| r2flutter -C apply Dart classes, fields, methods and types\n"
|
||||
"| r2flutter -D prof override Dart snapshot profile by hash or version\n"
|
||||
"| r2flutter -f dump recovered functions\n"
|
||||
"| r2flutter -H dump Dart AOT snapshot header info\n"
|
||||
"| r2flutter -HH dump Dart AOT snapshot header and cluster layout\n"
|
||||
"| r2flutter -HHH decode selected cluster payloads for diagnostics\n"
|
||||
"| r2flutter -h show this help\n"
|
||||
"| r2flutter -i dump instruction table entries\n"
|
||||
"| r2flutter -j <act> output JSON for dump actions\n"
|
||||
"| r2flutter -E[jr*] dump Dart code entrypoint\n"
|
||||
"| r2flutter -f[jr*] dump recovered functions\n"
|
||||
"| r2flutter -H[HH] dump Dart AOT snapshot header info\n"
|
||||
"| r2flutter -i[jr*] dump instruction table entries\n"
|
||||
"| r2flutter -ie[jr*] dump Dart code entrypoint; with -r/-* mark instruction snapshots as dword arrays\n"
|
||||
"| r2flutter -l N limit function/instruction-table/xref output\n"
|
||||
"| r2flutter -m file load Flutter obfuscation map JSON\n"
|
||||
"| r2flutter -n use heuristic name-pool fallback; names may be wrong\n"
|
||||
"| r2flutter -O addr decode Dart tagged/object pointer or ObjectPool PP slot\n"
|
||||
"| r2flutter -p print reconstructed ObjectPool PP value\n"
|
||||
"| r2flutter -q <act> compact output; quiet analysis logs\n"
|
||||
"| r2flutter -r <act> output r2 commands for dump actions\n"
|
||||
"| r2flutter -O spec decode a tagged pointer, PP slot, or snapshot ref:N\n"
|
||||
"| r2flutter -p[jr*] print reconstructed ObjectPool PP value\n"
|
||||
"| r2flutter -R dump full radare2 script (like standalone -R)\n"
|
||||
"| r2flutter -S dump best-effort recovered SBOM/components\n"
|
||||
"| r2flutter -T dump string-based type names\n"
|
||||
"| r2flutter -S[jr*] dump best-effort recovered SBOM/components\n"
|
||||
"| r2flutter -T[jr*] dump string-based type names\n"
|
||||
"| r2flutter -x[jr*] dump metadata/data-image xrefs; with -z, include string refs/ax in -r/-*\n"
|
||||
"| r2flutter -z[jr*] dump reliable ObjectPool-referenced strings\n"
|
||||
"| r2flutter -zz[jr*] dump all fuzzy/carved strings (-xzz includes refs/ax in -r/-*)\n"
|
||||
"| r2flutter -h show this help\n"
|
||||
"| r2flutter -v increase parser verbosity\n"
|
||||
"| r2flutter -V show version\n"
|
||||
"| r2flutter -x dump metadata/data-image xrefs; with -z, include string refs/ax in -r\n"
|
||||
"| r2flutter -z dump reliable ObjectPool-referenced strings (-q prints values only)\n"
|
||||
"| r2flutter -zz dump all fuzzy/carved strings (-xzz includes refs/ax in -r)\n");
|
||||
"Modifiers: q=quiet, j=json, r/*=r2 commands\n");
|
||||
}
|
||||
|
||||
static bool r2flutter_analyze(RCore *core, DartCtx *dctx, int quiet) {
|
||||
@@ -109,7 +153,7 @@ static bool r2flutter_analyze(RCore *core, DartCtx *dctx, int quiet) {
|
||||
}
|
||||
}
|
||||
|
||||
dart_dumper_apply_to_core (app);
|
||||
dart_dumper_apply_to_core (app, false);
|
||||
|
||||
dart_app_free (app);
|
||||
return true;
|
||||
@@ -121,7 +165,7 @@ static bool r2flutter_dump_r2script(RCore *core, DartCtx *dctx) {
|
||||
return false;
|
||||
}
|
||||
dart_app_load_info (app);
|
||||
char *script = dart_dumper_dump4radare2 (app);
|
||||
char *script = dart_dumper_dump_r2 (app);
|
||||
if (script) {
|
||||
r_cons_print (core->cons, script);
|
||||
free (script);
|
||||
@@ -176,6 +220,8 @@ static bool r2flutter_parse_cmd(const char *args, DartCtx *dctx, R2FlutterCmd *c
|
||||
cmd->string_depth = 0;
|
||||
cmd->string_refs = false;
|
||||
cmd->help = false;
|
||||
cmd->subcmd_help = false;
|
||||
cmd->invalid_flag = 0;
|
||||
cmd->obf_map_path = NULL;
|
||||
cmd->object_spec = NULL;
|
||||
char *dup = strdup (args);
|
||||
@@ -190,6 +236,10 @@ static bool r2flutter_parse_cmd(const char *args, DartCtx *dctx, R2FlutterCmd *c
|
||||
continue;
|
||||
}
|
||||
if (word[0] != '-') {
|
||||
if (!strcmp (word, "?")) {
|
||||
cmd->subcmd_help = true;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const char *flags = word + 1;
|
||||
@@ -204,6 +254,9 @@ static bool r2flutter_parse_cmd(const char *args, DartCtx *dctx, R2FlutterCmd *c
|
||||
case 'c':
|
||||
cmd->action = flag;
|
||||
break;
|
||||
case 'E':
|
||||
cmd->action = 'e';
|
||||
break;
|
||||
case 'C':
|
||||
cmd->action = flag;
|
||||
break;
|
||||
@@ -228,6 +281,14 @@ static bool r2flutter_parse_cmd(const char *args, DartCtx *dctx, R2FlutterCmd *c
|
||||
dctx->dump_it = true;
|
||||
cmd->action = flag;
|
||||
break;
|
||||
case 'e':
|
||||
if (cmd->action == 'i') {
|
||||
cmd->action = flag;
|
||||
} else {
|
||||
cmd->invalid_flag = flag;
|
||||
cmd->help = true;
|
||||
}
|
||||
break;
|
||||
case 'O':
|
||||
{
|
||||
const char *arg = r2flutter_opt_arg (tail, words, nwords, &i);
|
||||
@@ -288,6 +349,7 @@ static bool r2flutter_parse_cmd(const char *args, DartCtx *dctx, R2FlutterCmd *c
|
||||
dctx->quiet = 1;
|
||||
break;
|
||||
case 'r':
|
||||
case '*':
|
||||
cmd->fmt = 'r';
|
||||
break;
|
||||
case 'R':
|
||||
@@ -320,7 +382,12 @@ static bool r2flutter_parse_cmd(const char *args, DartCtx *dctx, R2FlutterCmd *c
|
||||
cmd->action = flag;
|
||||
cmd->string_depth++;
|
||||
break;
|
||||
case '?':
|
||||
cmd->subcmd_help = true;
|
||||
cmd->help = true;
|
||||
break;
|
||||
default:
|
||||
cmd->invalid_flag = flag;
|
||||
cmd->help = true;
|
||||
break;
|
||||
}
|
||||
@@ -346,13 +413,28 @@ static bool r2flutter_run_cmd(RCore *core, DartCtx *dctx, const R2FlutterCmd *cm
|
||||
r2flutter_help (core);
|
||||
return true;
|
||||
case 'A':
|
||||
// Tiered analysis:
|
||||
// -A flags, function names and signatures (fast)
|
||||
// -AA + anal.gp seeding and the ObjectPool xref graph (data-image
|
||||
// string references), without the expensive per-function scan
|
||||
// -AAA + full register-tracking scan (PP-slot comments, field/arg
|
||||
// tracking, null/bool annotations, indirect-call resolution)
|
||||
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 {
|
||||
r2flutter_analyze (core, dctx, dctx->quiet);
|
||||
if (cmd->analysis_depth >= 2) {
|
||||
int graph_xrefs = r2flutter_apply_pool_xrefs (core, dctx);
|
||||
if (!dctx->quiet) {
|
||||
R_LOG_INFO ("r2flutter: applied %d ObjectPool xrefs", graph_xrefs);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
case 'C':
|
||||
@@ -365,6 +447,9 @@ static bool r2flutter_run_cmd(RCore *core, DartCtx *dctx, const R2FlutterCmd *cm
|
||||
dctx->dump_fields = 1;
|
||||
out = dart_pool_dump_classes (dctx, cmd->fmt);
|
||||
break;
|
||||
case 'e':
|
||||
out = dart_pool_dump_entry (dctx, cmd->fmt);
|
||||
break;
|
||||
case 'f':
|
||||
out = r2flutter_dump_functions (core, dctx, cmd->fmt);
|
||||
break;
|
||||
@@ -379,13 +464,13 @@ static bool r2flutter_run_cmd(RCore *core, DartCtx *dctx, const R2FlutterCmd *cm
|
||||
out = dart_pool_dump_object (dctx, cmd->object_spec, cmd->fmt);
|
||||
break;
|
||||
case 'p':
|
||||
out = dart_pool_dump_pp (dctx, cmd->fmt);
|
||||
out = dart_pool_dump_pool (dctx, cmd->fmt);
|
||||
break;
|
||||
case 'R':
|
||||
r2flutter_dump_r2script (core, dctx);
|
||||
return true;
|
||||
case 'S':
|
||||
out = dart_pool_dump_sbom (dctx, NULL, cmd->fmt);
|
||||
out = dart_pool_dump_sbom (dctx, core->io && core->io->desc? core->io->desc->name: NULL, cmd->fmt);
|
||||
break;
|
||||
case 'z':
|
||||
out = cmd->string_depth >= 2? dart_pool_dump_strings_fuzzy (dctx, cmd->fmt): dart_pool_dump_strings (dctx, cmd->fmt);
|
||||
@@ -439,7 +524,16 @@ static bool r_cmd_r2flutter_call(RCorePluginSession *cps, const char *input) {
|
||||
bool ret = true;
|
||||
R2FlutterCmd cmd = { 0 };
|
||||
if (!r2flutter_parse_cmd (args, &dctx, &cmd) || cmd.help) {
|
||||
r2flutter_help (core);
|
||||
if (cmd.subcmd_help) {
|
||||
r2flutter_subcmd_help (core, cmd.action);
|
||||
} else if (cmd.invalid_flag) {
|
||||
r_core_return_invalid_command (core, "r2flutter", cmd.invalid_flag);
|
||||
ret = false;
|
||||
} else {
|
||||
r2flutter_help (core);
|
||||
}
|
||||
} else if (cmd.subcmd_help) {
|
||||
r2flutter_subcmd_help (core, cmd.action);
|
||||
} else {
|
||||
ret = r2flutter_run_cmd (core, &dctx, &cmd);
|
||||
}
|
||||
@@ -448,8 +542,8 @@ static bool r_cmd_r2flutter_call(RCorePluginSession *cps, const char *input) {
|
||||
free (cmd.object_spec);
|
||||
return ret;
|
||||
}
|
||||
r2flutter_help (core);
|
||||
return true;
|
||||
r_core_return_invalid_command (core, "r2flutter", ch0);
|
||||
return false;
|
||||
}
|
||||
|
||||
RCorePlugin r_core_plugin_r2flutter = {
|
||||
@@ -470,6 +564,7 @@ R_API RLibStruct radare_plugin = {
|
||||
.type = R_LIB_TYPE_CORE,
|
||||
.data = &r_core_plugin_r2flutter,
|
||||
.version = R2_VERSION,
|
||||
.abiversion = R2_ABIVERSION
|
||||
.abiversion = R2_ABIVERSION,
|
||||
.pkgname = "r2flutter"
|
||||
};
|
||||
#endif
|
||||
+673
-76
@@ -19,8 +19,10 @@ typedef struct {
|
||||
const DartFieldInfo *field;
|
||||
const DartStringInfo *string;
|
||||
const char *type_name;
|
||||
ut64 pp_off;
|
||||
ut64 pp_off; // this register is a pool POINTER: PP base + pp_off (from x27 / add)
|
||||
bool has_pp_off;
|
||||
ut64 pp_slot; // this register holds the VALUE loaded from PP slot pp_slot (object)
|
||||
bool has_pp_slot;
|
||||
} FlutterTrackedValue;
|
||||
|
||||
typedef struct {
|
||||
@@ -41,6 +43,7 @@ typedef struct {
|
||||
ut64 imm;
|
||||
bool has_mem;
|
||||
bool has_imm;
|
||||
int etype; // effective op type (x86-64 `mov reg,[mem]` reclassified as LOAD/STORE)
|
||||
} FlutterOpInfo;
|
||||
|
||||
typedef struct {
|
||||
@@ -54,6 +57,34 @@ typedef struct {
|
||||
ut64 comments;
|
||||
} FlutterAnalStats;
|
||||
|
||||
typedef struct {
|
||||
ut64 string_addr;
|
||||
char *string_value;
|
||||
bool indirect;
|
||||
} FlutterPpStringRef;
|
||||
|
||||
typedef struct {
|
||||
HtUP *by_pp_off;
|
||||
} FlutterPpStringMap;
|
||||
|
||||
// Non-string resolution of PP slots (functions, classes, code, ...), so that
|
||||
// analysis comments can name what a slot holds instead of a bare offset, and
|
||||
// code/function slots can gain a real xref to their target.
|
||||
typedef struct {
|
||||
char *kind;
|
||||
char *name;
|
||||
ut64 code_index;
|
||||
ut64 value_addr;
|
||||
} FlutterPpRef;
|
||||
|
||||
typedef struct {
|
||||
HtUP *by_pp_off; // pp_off -> FlutterPpRef
|
||||
HtUP *addr_by_code_index; // code_index -> normalized code address
|
||||
ut64 pp_base; // ObjectPool base address, so a [PP, off] load can xref pp_base+off
|
||||
bool is_x64; // x86-64 keeps the heap tag in r15, so a [r15, off] offset is one
|
||||
// less than the arm64/collector offset; add it back before lookup/xref.
|
||||
} FlutterPpRefMap;
|
||||
|
||||
typedef struct {
|
||||
HtUP *seen;
|
||||
RVecFlutterEntry *entries;
|
||||
@@ -63,6 +94,29 @@ static void flutter_stack_slot_free(void *p) {
|
||||
free (p);
|
||||
}
|
||||
|
||||
static void flutter_pp_string_ref_free(void *p) {
|
||||
FlutterPpStringRef *ref = p;
|
||||
free (ref->string_value);
|
||||
free (ref);
|
||||
}
|
||||
|
||||
static void flutter_pp_string_bucket_free(HtUPKv *kv) {
|
||||
r_list_free (kv->value);
|
||||
}
|
||||
|
||||
static void flutter_pp_string_map_init(FlutterPpStringMap *map) {
|
||||
memset (map, 0, sizeof (*map));
|
||||
map->by_pp_off = ht_up_new (NULL, flutter_pp_string_bucket_free, NULL);
|
||||
}
|
||||
|
||||
static void flutter_pp_string_map_fini(FlutterPpStringMap *map) {
|
||||
if (!map) {
|
||||
return;
|
||||
}
|
||||
ht_up_free (map->by_pp_off);
|
||||
memset (map, 0, sizeof (*map));
|
||||
}
|
||||
|
||||
static void flutter_model_fini(FlutterAnalModel *model) {
|
||||
dart_recovery_model_fini (model);
|
||||
}
|
||||
@@ -105,8 +159,44 @@ static bool flutter_reg_is_stack(const char *name) {
|
||||
return !strcmp (name, "x15") || !strcmp (name, "sp") || !strcmp (name, "x29") || !strcmp (name, "fp");
|
||||
}
|
||||
|
||||
// Dart's pool pointer (PP): x27 on arm64, r15 on x86-64.
|
||||
static bool flutter_reg_is_pp(const char *name) {
|
||||
return !strcmp (name, "x27");
|
||||
return !strcmp (name, "x27") || !strcmp (name, "r15");
|
||||
}
|
||||
|
||||
// x22 is Dart AOT's NULL_REG: it caches Object::null() for the whole program.
|
||||
static bool flutter_reg_is_null(const char *name) {
|
||||
return !strcmp (name, "x22");
|
||||
}
|
||||
|
||||
// The VM lays null, true and false out as adjacent heap objects
|
||||
// (runtime/vm/pointer_tagging.h): true = null + 4*word_size, false = null +
|
||||
// 6*word_size. On arm64 AOT the object-alignment word is the 8-byte target word
|
||||
// regardless of pointer compression, so these are 0x20 / 0x30 on every Dart
|
||||
// version (verified on cws=8 and cws=4 snapshots).
|
||||
#define DART_NULL_TRUE_OFFSET 0x20
|
||||
#define DART_NULL_FALSE_OFFSET 0x30
|
||||
|
||||
// Map an x86-64 general-purpose register name (64/32-bit) to a stable slot. Dart
|
||||
// AOT on x64 uses the same tracker; names never collide with arm64 (x/w vs
|
||||
// r/rXX), and a single binary is one arch, so sharing the 0..15 slot space is
|
||||
// safe.
|
||||
static int flutter_x64_reg_index(const char *name) {
|
||||
static const char *const names[16] = {
|
||||
"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
|
||||
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
|
||||
};
|
||||
static const char *const names32[16] = {
|
||||
"eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
|
||||
"r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d"
|
||||
};
|
||||
int i;
|
||||
for (i = 0; i < 16; i++) {
|
||||
if (!strcmp (name, names[i]) || !strcmp (name, names32[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int flutter_reg_index(const char *name) {
|
||||
@@ -125,6 +215,9 @@ static int flutter_reg_index(const char *name) {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
if (name[0] == 'r' || name[0] == 'e') {
|
||||
return flutter_x64_reg_index (name);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -214,68 +307,161 @@ static bool flutter_parse_shifted_imm(const char *line, ut64 *imm) {
|
||||
if (!lsl) {
|
||||
return false;
|
||||
}
|
||||
const char *hash = strchr (lsl, '#');
|
||||
if (!hash) {
|
||||
// RArchValue.imm may already include the shift (depending on the ARM
|
||||
// decoder). Read the unshifted third operand before applying it once.
|
||||
const char *arg = strchr (line, ',');
|
||||
arg = arg? strchr (arg + 1, ','): NULL;
|
||||
if (!arg || arg >= lsl) {
|
||||
return false;
|
||||
}
|
||||
int shift = atoi (hash + 1);
|
||||
if (shift <= 0 || shift >= 63) {
|
||||
arg++;
|
||||
while (isspace ((ut8)*arg) || *arg == '#') {
|
||||
arg++;
|
||||
}
|
||||
char *end;
|
||||
ut64 value = strtoull (arg, &end, 0);
|
||||
if (end == arg) {
|
||||
return false;
|
||||
}
|
||||
*imm <<= shift;
|
||||
const char *p = lsl + 3;
|
||||
while (isspace ((ut8)*p)) {
|
||||
p++;
|
||||
}
|
||||
if (*p == '#') {
|
||||
p++;
|
||||
}
|
||||
if (!isdigit ((ut8)*p)) {
|
||||
return false;
|
||||
}
|
||||
ut64 shift = strtoull (p, NULL, 0);
|
||||
if (shift >= 64) {
|
||||
return false;
|
||||
}
|
||||
*imm = value << shift;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void flutter_opinfo_add_reg(FlutterOpInfo *info, const char *reg) {
|
||||
if (R_STR_ISEMPTY (reg) || info->n_regs >= (int)R_ARRAY_SIZE (info->regs)) {
|
||||
return;
|
||||
}
|
||||
r_str_ncpy (info->regs[info->n_regs], reg, sizeof (info->regs[0]));
|
||||
info->n_regs++;
|
||||
}
|
||||
|
||||
static void flutter_opinfo_add_value(FlutterOpInfo *info, const RArchValue *value, bool add_reg) {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
if (add_reg && R_STR_ISNOTEMPTY (value->reg)) {
|
||||
flutter_opinfo_add_reg (info, value->reg);
|
||||
}
|
||||
if (value->imm) {
|
||||
info->imm = (ut64)value->imm;
|
||||
info->has_imm = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void flutter_parse_mem_operand(const char *line, FlutterOpInfo *info) {
|
||||
const char *p = R_STR_ISNOTEMPTY (line)? strchr (line, '['): NULL;
|
||||
if (!p) {
|
||||
return;
|
||||
}
|
||||
p++;
|
||||
while (isspace ((ut8)*p)) {
|
||||
p++;
|
||||
}
|
||||
const char *reg = p;
|
||||
while (isalnum ((ut8)*p) || *p == '_') {
|
||||
p++;
|
||||
}
|
||||
const size_t reg_len = p - reg;
|
||||
if (!reg_len || reg_len >= sizeof (info->mem_base)) {
|
||||
return;
|
||||
}
|
||||
memcpy (info->mem_base, reg, reg_len);
|
||||
info->mem_base[reg_len] = '\0';
|
||||
info->has_mem = true;
|
||||
while (isspace ((ut8)*p)) {
|
||||
p++;
|
||||
}
|
||||
// arm64 writes `[x27, 0x610]`, x86-64 writes `[r15 + 0x610]`.
|
||||
if (*p != ',' && *p != '+') {
|
||||
return;
|
||||
}
|
||||
p++;
|
||||
while (isspace ((ut8)*p) || *p == '#') {
|
||||
p++;
|
||||
}
|
||||
char *end = NULL;
|
||||
st64 disp = (st64)strtoll (p, &end, 0);
|
||||
if (end != p) {
|
||||
info->mem_disp = disp;
|
||||
}
|
||||
}
|
||||
|
||||
static void flutter_parse_first_operand_reg(const char *line, FlutterOpInfo *info) {
|
||||
const char *p = R_STR_ISNOTEMPTY (line)? strchr (line, ' '): NULL;
|
||||
if (!p) {
|
||||
return;
|
||||
}
|
||||
while (isspace ((ut8)*p)) {
|
||||
p++;
|
||||
}
|
||||
const char *reg = p;
|
||||
while (isalnum ((ut8)*p) || *p == '_') {
|
||||
p++;
|
||||
}
|
||||
const size_t reg_len = p - reg;
|
||||
if (!reg_len || reg_len >= sizeof (info->regs[0])) {
|
||||
return;
|
||||
}
|
||||
char name[sizeof (info->regs[0])];
|
||||
memcpy (name, reg, reg_len);
|
||||
name[reg_len] = '\0';
|
||||
flutter_opinfo_add_reg (info, name);
|
||||
}
|
||||
|
||||
static bool flutter_parse_opinfo(const RAnalOp *op, FlutterOpInfo *info) {
|
||||
if (!op || !info) {
|
||||
return false;
|
||||
}
|
||||
memset (info, 0, sizeof (*info));
|
||||
const char *opex = r_strbuf_get ((RStrBuf *)&op->opex);
|
||||
if (R_STR_ISEMPTY (opex)) {
|
||||
return false;
|
||||
}
|
||||
char *opexdup = strdup (opex);
|
||||
RJson *j = r_json_parse (opexdup);
|
||||
if (!j) {
|
||||
free (opexdup);
|
||||
return false;
|
||||
}
|
||||
const RJson *ops = r_json_get (j, "operands");
|
||||
for (size_t i = 0;; i++) {
|
||||
const RJson *item = r_json_item (ops, i);
|
||||
if (!item) {
|
||||
break;
|
||||
}
|
||||
const char *type = r_json_get_str (item, "type");
|
||||
if (R_STR_ISEMPTY (type)) {
|
||||
continue;
|
||||
}
|
||||
if (!strcmp (type, "reg")) {
|
||||
const char *value = r_json_get_str (item, "value");
|
||||
if (R_STR_ISNOTEMPTY (value) && info->n_regs < 4) {
|
||||
r_str_ncpy (info->regs[info->n_regs], value, sizeof (info->regs[0]));
|
||||
info->n_regs++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!strcmp (type, "mem")) {
|
||||
const char *base = r_json_get_str (item, "base");
|
||||
if (R_STR_ISNOTEMPTY (base)) {
|
||||
r_str_ncpy (info->mem_base, base, sizeof (info->mem_base));
|
||||
info->has_mem = true;
|
||||
}
|
||||
info->mem_disp = (st64)r_json_get_num (item, "disp");
|
||||
continue;
|
||||
}
|
||||
if (!strcmp (type, "imm")) {
|
||||
info->imm = r_json_get_num (item, "value");
|
||||
info->has_imm = true;
|
||||
int type = op->type & R_ANAL_OP_TYPE_MASK;
|
||||
// x86-64 types a memory load/store as MOV; reclassify `mov reg, [mem]` as a
|
||||
// LOAD and `mov [mem], reg` as a STORE by where the brackets sit relative to
|
||||
// the operand separator, so the arm64-shaped load/store logic applies.
|
||||
if (type == R_ANAL_OP_TYPE_MOV && R_STR_ISNOTEMPTY (op->mnemonic)) {
|
||||
const char *br = strchr (op->mnemonic, '[');
|
||||
const char *comma = strchr (op->mnemonic, ',');
|
||||
if (br && comma) {
|
||||
type = br > comma? R_ANAL_OP_TYPE_LOAD: R_ANAL_OP_TYPE_STORE;
|
||||
}
|
||||
}
|
||||
r_json_free (j);
|
||||
free (opexdup);
|
||||
if (info->has_imm && R_STR_ISNOTEMPTY (op->mnemonic)) {
|
||||
info->etype = type;
|
||||
const RArchValue *value = RVecRArchValue_at (&op->dsts, 0);
|
||||
if (type != R_ANAL_OP_TYPE_STORE) {
|
||||
flutter_opinfo_add_value (info, value, true);
|
||||
}
|
||||
const RArchValue *src;
|
||||
R_VEC_FOREACH (&op->srcs, src) {
|
||||
const bool add_reg = type != R_ANAL_OP_TYPE_LOAD;
|
||||
flutter_opinfo_add_value (info, src, add_reg);
|
||||
}
|
||||
if (type == R_ANAL_OP_TYPE_LOAD || type == R_ANAL_OP_TYPE_STORE) {
|
||||
flutter_parse_mem_operand (op->mnemonic, info);
|
||||
}
|
||||
if (type == R_ANAL_OP_TYPE_STORE && info->n_regs == 0) {
|
||||
flutter_parse_first_operand_reg (op->mnemonic, info);
|
||||
}
|
||||
if (!info->has_imm && type == R_ANAL_OP_TYPE_ADD && op->val) {
|
||||
info->imm = op->val;
|
||||
info->has_imm = true;
|
||||
}
|
||||
if (info->n_regs == 0 && (type == R_ANAL_OP_TYPE_RCALL || type == R_ANAL_OP_TYPE_UCALL || type == R_ANAL_OP_TYPE_ICALL || type == R_ANAL_OP_TYPE_IRCALL)) {
|
||||
flutter_parse_first_operand_reg (op->mnemonic, info);
|
||||
}
|
||||
if (type == R_ANAL_OP_TYPE_ADD && info->has_imm && R_STR_ISNOTEMPTY (op->mnemonic)) {
|
||||
(void)flutter_parse_shifted_imm (op->mnemonic, &info->imm);
|
||||
}
|
||||
return info->n_regs > 0 || info->has_mem || info->has_imm;
|
||||
@@ -337,13 +523,44 @@ static void flutter_add_ref(RCore *core, ut64 from, ut64 to, RAnalRefType type)
|
||||
r_anal_xrefs_set (core->anal, from, to, type);
|
||||
}
|
||||
|
||||
// Annotate the Dart NULL_REG (x22) idioms so booleans and null checks read
|
||||
// clearly: `add d, x22, #0x20/#0x30` materialises true/false, `mov d, x22`
|
||||
// loads null, and a compare against x22 is a null check.
|
||||
static void flutter_annotate_null_reg(RCore *core, ut64 at, int optype, const FlutterOpInfo *info, FlutterAnalStats *stats) {
|
||||
if (!core || !info) {
|
||||
return;
|
||||
}
|
||||
if (optype == R_ANAL_OP_TYPE_ADD && info->n_regs >= 2 && info->has_imm && flutter_reg_is_null (info->regs[1])) {
|
||||
const char *b = info->imm == DART_NULL_TRUE_OFFSET? "true": info->imm == DART_NULL_FALSE_OFFSET? "false": NULL;
|
||||
if (b) {
|
||||
char *msg = r_str_newf ("dart: %s", b);
|
||||
flutter_append_comment (core, at, msg, stats);
|
||||
free (msg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (optype == R_ANAL_OP_TYPE_MOV && info->n_regs >= 2 && flutter_reg_is_null (info->regs[1])) {
|
||||
flutter_append_comment (core, at, "dart: null", stats);
|
||||
return;
|
||||
}
|
||||
if (optype == R_ANAL_OP_TYPE_CMP) {
|
||||
int i;
|
||||
for (i = 0; i < info->n_regs; i++) {
|
||||
if (flutter_reg_is_null (info->regs[i])) {
|
||||
flutter_append_comment (core, at, "dart: compare with null", stats);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool flutter_model_load(FlutterAnalModel *model, RCore *core, DartCtx *dctx) {
|
||||
if (!model || !core || !dctx) {
|
||||
return false;
|
||||
}
|
||||
DartCtx ctx = *dctx;
|
||||
ctx.core = core;
|
||||
dart_recovery_model_load (&ctx, model, DART_RECOVERY_STRINGS | DART_RECOVERY_CLASSES | DART_RECOVERY_CLASS_FIELDS | DART_RECOVERY_METHOD_INDEX);
|
||||
dart_recovery_model_load (&ctx, model, DART_RECOVERY_STRINGS | DART_RECOVERY_CLASSES | DART_RECOVERY_CLASS_FIELDS | DART_RECOVERY_METHOD_INDEX | DART_RECOVERY_IT);
|
||||
if (!model->strings || !model->classes) {
|
||||
flutter_model_fini (model);
|
||||
return false;
|
||||
@@ -351,6 +568,183 @@ static bool flutter_model_load(FlutterAnalModel *model, RCore *core, DartCtx *dc
|
||||
return true;
|
||||
}
|
||||
|
||||
static void flutter_pp_string_map_add(FlutterPpStringMap *map, const ModernPoolStrRef *info) {
|
||||
if (!map || !map->by_pp_off || !info || R_STR_ISEMPTY (info->string_value) || !info->string_addr) {
|
||||
return;
|
||||
}
|
||||
RList *refs = ht_up_find (map->by_pp_off, info->pp_offset, NULL);
|
||||
if (!refs) {
|
||||
refs = r_list_newf (flutter_pp_string_ref_free);
|
||||
ht_up_insert (map->by_pp_off, info->pp_offset, refs);
|
||||
}
|
||||
RListIter *it;
|
||||
FlutterPpStringRef *existing;
|
||||
r_list_foreach (refs, it, existing) {
|
||||
if (existing->string_addr == info->string_addr) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
FlutterPpStringRef *ref = R_NEW0 (FlutterPpStringRef);
|
||||
ref->string_addr = info->string_addr;
|
||||
ref->string_value = strdup (info->string_value);
|
||||
ref->indirect = strcmp (info->target_kind, "object_pool.entry") != 0;
|
||||
r_list_append (refs, ref);
|
||||
}
|
||||
|
||||
static void flutter_pp_string_ref_cb(const ModernPoolStrRef *info, void *user) {
|
||||
flutter_pp_string_map_add (user, info);
|
||||
}
|
||||
|
||||
static bool flutter_collect_pp_strings_from_snapshot(DartCtx *ctx, ut64 snapshot_base, HtUP *wanted, FlutterPpStringMap *map) {
|
||||
if (!ctx || !snapshot_base || !map) {
|
||||
return false;
|
||||
}
|
||||
DartSnapshotHeader sh = { 0 };
|
||||
if (!dart_snapshot_header_read (ctx, snapshot_base, &sh) || !sh.ok) {
|
||||
return false;
|
||||
}
|
||||
const ModernReq req = {
|
||||
.ctx = ctx,
|
||||
.cluster_start = sh.cluster_start,
|
||||
.cluster_end = snapshot_base + sh.total_len,
|
||||
.num_clusters = sh.nc,
|
||||
.num_base_objects = sh.nb,
|
||||
};
|
||||
return modern_collect_pool_value_strrefs (&req, wanted, flutter_pp_string_ref_cb, map);
|
||||
}
|
||||
|
||||
static void flutter_pp_string_map_load(DartCtx *ctx, HtUP *wanted, FlutterPpStringMap *map) {
|
||||
if (!ctx || !map || find_snapshots (ctx) != 0) {
|
||||
return;
|
||||
}
|
||||
// When we init the layout here it may live in layout_tmp (a stack buffer)
|
||||
// with dart_ctx_init_layout returning NULL. We must still clear ctx->layout
|
||||
// on the way out, or a later consumer would dereference a dangling pointer.
|
||||
DartVerLayout layout_tmp;
|
||||
bool layout_ours = !ctx->layout;
|
||||
DartVerLayout *layout_owned = layout_ours? dart_ctx_init_layout (ctx, &layout_tmp): NULL;
|
||||
if (!flutter_collect_pp_strings_from_snapshot (ctx, ctx->iso_data, wanted, map)) {
|
||||
flutter_collect_pp_strings_from_snapshot (ctx, ctx->vm_data, wanted, map);
|
||||
}
|
||||
if (layout_ours) {
|
||||
dart_ctx_fini_layout (ctx, layout_owned);
|
||||
}
|
||||
}
|
||||
|
||||
static RList *flutter_pp_string_map_get(FlutterPpStringMap *map, ut64 pp_off) {
|
||||
return map && map->by_pp_off? ht_up_find (map->by_pp_off, pp_off, NULL): NULL;
|
||||
}
|
||||
|
||||
static void flutter_pp_ref_free(HtUPKv *kv) {
|
||||
FlutterPpRef *ref = kv->value;
|
||||
free (ref->kind);
|
||||
free (ref->name);
|
||||
free (ref);
|
||||
}
|
||||
|
||||
static void flutter_pp_ref_map_init(FlutterPpRefMap *map) {
|
||||
memset (map, 0, sizeof (*map));
|
||||
map->by_pp_off = ht_up_new (NULL, flutter_pp_ref_free, NULL);
|
||||
map->addr_by_code_index = ht_up_new0 ();
|
||||
}
|
||||
|
||||
static void flutter_pp_ref_map_fini(FlutterPpRefMap *map) {
|
||||
if (!map) {
|
||||
return;
|
||||
}
|
||||
ht_up_free (map->by_pp_off);
|
||||
ht_up_free (map->addr_by_code_index);
|
||||
memset (map, 0, sizeof (*map));
|
||||
}
|
||||
|
||||
static void flutter_pp_ref_cb(const ModernPoolRefInfo *info, void *user) {
|
||||
FlutterPpRefMap *map = user;
|
||||
if (!map || !map->by_pp_off || !info || R_STR_ISEMPTY (info->kind)) {
|
||||
return;
|
||||
}
|
||||
// Strings are handled by the dedicated string map; skip them here so the
|
||||
// two maps stay cheap and the string path keeps priority.
|
||||
if (!strcmp (info->kind, "string") || ht_up_find (map->by_pp_off, info->pp_offset, NULL)) {
|
||||
return;
|
||||
}
|
||||
FlutterPpRef *ref = R_NEW0 (FlutterPpRef);
|
||||
ref->kind = strdup (info->kind);
|
||||
ref->name = R_STR_ISNOTEMPTY (info->name)? strdup (info->name): NULL;
|
||||
ref->code_index = info->code_index;
|
||||
ref->value_addr = info->value_addr;
|
||||
ht_up_insert (map->by_pp_off, info->pp_offset, ref);
|
||||
}
|
||||
|
||||
static void flutter_pp_ref_map_load(DartCtx *ctx, FlutterPpRefMap *map) {
|
||||
if (!ctx || !map || find_snapshots (ctx) != 0) {
|
||||
return;
|
||||
}
|
||||
// See flutter_pp_string_map_load: clear ctx->layout on exit when we init it,
|
||||
// since it may point at the stack buffer layout_tmp.
|
||||
DartVerLayout layout_tmp;
|
||||
bool layout_ours = !ctx->layout;
|
||||
DartVerLayout *layout_owned = layout_ours? dart_ctx_init_layout (ctx, &layout_tmp): NULL;
|
||||
// The ObjectPool lives in the isolate snapshot; fall back to the VM snapshot
|
||||
// like flutter_pp_string_map_load does.
|
||||
const ut64 bases[2] = { ctx->iso_data, ctx->vm_data };
|
||||
for (int i = 0; i < 2; i++) {
|
||||
ut64 snapshot_base = bases[i];
|
||||
DartSnapshotHeader sh = { 0 };
|
||||
if (!snapshot_base || !dart_snapshot_header_read (ctx, snapshot_base, &sh) || !sh.ok) {
|
||||
continue;
|
||||
}
|
||||
const ModernReq req = {
|
||||
.ctx = ctx,
|
||||
.cluster_start = sh.cluster_start,
|
||||
.cluster_end = snapshot_base + sh.total_len,
|
||||
.num_clusters = sh.nc,
|
||||
.num_base_objects = sh.nb,
|
||||
};
|
||||
if (modern_collect_direct_pool_refs (&req, flutter_pp_ref_cb, map)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Map code_index -> entrypoint address via the instruction table, so a
|
||||
// code/function slot can be xref'd to its real target.
|
||||
RVecDartInstructionTableEntry *entries = dart_pool_extract_instruction_table (ctx);
|
||||
if (entries) {
|
||||
DartInstructionTableEntry *entry;
|
||||
R_VEC_FOREACH (entries, entry) {
|
||||
if (!entry->has_code || !entry->address) {
|
||||
continue;
|
||||
}
|
||||
// Strip the low Thumb/tag bit, matching dart_app's code-addr normalization.
|
||||
ut64 addr = (entry->address & 1ULL)? entry->address - 1: entry->address;
|
||||
ht_up_update (map->addr_by_code_index, entry->code_index, (void *)addr);
|
||||
}
|
||||
dart_instruction_table_list_free (entries);
|
||||
}
|
||||
// The ObjectPool base, so a [PP, off] load can xref the pool slot at
|
||||
// pp_base + off. r2flutter_setup_pp_gp already resolved it into anal.gp.
|
||||
if (ctx->core) {
|
||||
map->pp_base = (ut64)r_config_get_i (ctx->core->config, "anal.gp");
|
||||
const char *arch = r_config_get (ctx->core->config, "asm.arch");
|
||||
const int bits = (int)r_config_get_i (ctx->core->config, "asm.bits");
|
||||
map->is_x64 = arch && bits == 64 && (!strcmp (arch, "x86") || !strcmp (arch, "x64"));
|
||||
}
|
||||
if (layout_ours) {
|
||||
dart_ctx_fini_layout (ctx, layout_owned);
|
||||
}
|
||||
}
|
||||
|
||||
static const FlutterPpRef *flutter_pp_ref_map_get(FlutterPpRefMap *map, ut64 pp_off) {
|
||||
return map && map->by_pp_off? ht_up_find (map->by_pp_off, pp_off, NULL): NULL;
|
||||
}
|
||||
|
||||
static ut64 flutter_pp_ref_code_addr(FlutterPpRefMap *map, ut64 code_index) {
|
||||
if (!map || !map->addr_by_code_index || code_index == UT64_MAX) {
|
||||
return 0;
|
||||
}
|
||||
bool found = false;
|
||||
ut64 addr = (ut64)ht_up_find (map->addr_by_code_index, code_index, &found);
|
||||
return found? addr: 0;
|
||||
}
|
||||
|
||||
static void flutter_apply_model_to_core(FlutterAnalModel *model, RCore *core, FlutterAnalStats *stats) {
|
||||
if (!model || !core) {
|
||||
return;
|
||||
@@ -510,6 +904,17 @@ static RVecFlutterEntry *flutter_collect_entries(RCore *core, const DartApp *app
|
||||
flutter_collect_entries_from_flags (seen, entries, core);
|
||||
flutter_collect_entries_from_app (seen, entries, app);
|
||||
flutter_collect_entries_from_model (seen, entries, model);
|
||||
// Entries before first_entry_with_code still contain executable Dart code;
|
||||
// only their Code objects were discarded by the precompiler.
|
||||
if (model->it_entries) {
|
||||
DartInstructionTableEntry *entry;
|
||||
R_VEC_FOREACH (model->it_entries, entry) {
|
||||
if (entry->address && !ht_up_find (seen, entry->address, NULL)) {
|
||||
RVecFlutterEntry_push_back (entries, &entry->address);
|
||||
ht_up_insert (seen, entry->address, entries);
|
||||
}
|
||||
}
|
||||
}
|
||||
ht_up_free (seen);
|
||||
return entries;
|
||||
}
|
||||
@@ -531,6 +936,18 @@ static void flutter_ensure_functions(RCore *core, RVecFlutterEntry *entries) {
|
||||
if (restore_log_level) {
|
||||
r_log_set_level (R_LOG_LEVEL_INFO);
|
||||
}
|
||||
const bool prev_trycatch = dart_core_trycatch_suspend (core);
|
||||
// `af` only needs to recover each function's basic blocks here; the Dart
|
||||
// scan tracks its own args/fields and resolves calls from the snapshot, so
|
||||
// r2's variable analysis, call-following and jump-table recovery are pure
|
||||
// overhead - disabling them roughly halves this af storm, the dominant cost
|
||||
// of -AAA, without changing the recovered blocks.
|
||||
bool prev_calls = r_config_get_b (core->config, "anal.calls");
|
||||
bool prev_vars = r_config_get_b (core->config, "anal.vars");
|
||||
bool prev_jmptbl = r_config_get_b (core->config, "anal.jmp.tbl");
|
||||
r_config_set_b (core->config, "anal.calls", false);
|
||||
r_config_set_b (core->config, "anal.vars", false);
|
||||
r_config_set_b (core->config, "anal.jmp.tbl", false);
|
||||
ut64 *addrp;
|
||||
R_VEC_FOREACH (entries, addrp) {
|
||||
if (!*addrp) {
|
||||
@@ -540,6 +957,10 @@ static void flutter_ensure_functions(RCore *core, RVecFlutterEntry *entries) {
|
||||
r_core_cmdf (core, "af @ 0x%" PFMT64x, *addrp);
|
||||
}
|
||||
}
|
||||
r_config_set_b (core->config, "anal.calls", prev_calls);
|
||||
r_config_set_b (core->config, "anal.vars", prev_vars);
|
||||
r_config_set_b (core->config, "anal.jmp.tbl", prev_jmptbl);
|
||||
dart_core_trycatch_restore (core, prev_trycatch);
|
||||
if (restore_log_level) {
|
||||
r_log_set_level (old_level);
|
||||
}
|
||||
@@ -740,7 +1161,41 @@ static bool flutter_process_stack_store(FlutterAnalState *state, const FlutterOp
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool flutter_process_pp_load(RCore *core, FlutterAnalState *state, ut64 at, const FlutterOpInfo *info, FlutterAnalStats *stats) {
|
||||
// Annotate a non-string PP slot: name what the slot holds and, when it points
|
||||
// at a code/data object we can locate, add a navigable xref to the target.
|
||||
// Returns true when the slot resolved to a known entry.
|
||||
static bool flutter_annotate_pp_ref(RCore *core, FlutterPpRefMap *pp_refs, ut64 at, ut64 pp_off, bool is_call, FlutterAnalStats *stats) {
|
||||
const FlutterPpRef *ref = flutter_pp_ref_map_get (pp_refs, pp_off);
|
||||
if (!ref) {
|
||||
return false;
|
||||
}
|
||||
ut64 target = flutter_pp_ref_code_addr (pp_refs, ref->code_index);
|
||||
const char *label = NULL;
|
||||
if (target) {
|
||||
RFlagItem *fi = r_flag_get_at (core->flags, target, false);
|
||||
label = (fi && R_STR_ISNOTEMPTY (fi->name))? fi->name: ref->name;
|
||||
flutter_add_ref (core, at, target, is_call? R_ANAL_REF_TYPE_CALL: (R_ANAL_REF_TYPE_DATA | R_ANAL_REF_TYPE_READ));
|
||||
} else if (ref->value_addr) {
|
||||
label = ref->name;
|
||||
flutter_add_ref (core, at, ref->value_addr, R_ANAL_REF_TYPE_DATA | R_ANAL_REF_TYPE_READ);
|
||||
} else {
|
||||
label = ref->name;
|
||||
}
|
||||
const char *what = is_call? "indirect call via PP+0x": "PP slot +0x";
|
||||
char *msg;
|
||||
if (R_STR_ISNOTEMPTY (label)) {
|
||||
char *escaped = r_str_escape_utf8 (label, false, true);
|
||||
msg = r_str_newf ("dart: %s%" PFMT64x " %s %s", what, pp_off, ref->kind, escaped);
|
||||
free (escaped);
|
||||
} else {
|
||||
msg = r_str_newf ("dart: %s%" PFMT64x " %s", what, pp_off, ref->kind);
|
||||
}
|
||||
flutter_append_comment (core, at, msg, stats);
|
||||
free (msg);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool flutter_process_pp_load(RCore *core, FlutterAnalState *state, FlutterPpStringMap *pp_strings, FlutterPpRefMap *pp_refs, ut64 at, const FlutterOpInfo *info, FlutterAnalStats *stats) {
|
||||
if (!state || !info || info->n_regs < 1 || !info->has_mem) {
|
||||
return false;
|
||||
}
|
||||
@@ -758,14 +1213,44 @@ static bool flutter_process_pp_load(RCore *core, FlutterAnalState *state, ut64 a
|
||||
}
|
||||
pp_off = state->regs[base].pp_off + (ut64)info->mem_disp;
|
||||
}
|
||||
// On x86-64 the heap tag is carried in r15, so the instruction offset is one
|
||||
// less than the collector/arm64 slot offset. Add it back to get the
|
||||
// canonical offset used for resolution maps and the slot address.
|
||||
const ut64 slot_off = pp_off + (pp_refs && pp_refs->is_x64? 1: 0);
|
||||
// The destination now holds the pool OBJECT at pp_off, not a PP pointer.
|
||||
// Marking it has_pp_off would make a later [dst, field] load look like a
|
||||
// bogus pool slot (e.g. a Code entry-point deref read as PP+off+7).
|
||||
memset (&state->regs[dst], 0, sizeof (state->regs[dst]));
|
||||
state->regs[dst].has_pp_off = true;
|
||||
state->regs[dst].pp_off = pp_off;
|
||||
char *msg = r_str_newf ("dart: "
|
||||
"PP slot +0x%" PFMT64x,
|
||||
pp_off);
|
||||
flutter_append_comment (core, at, msg, stats);
|
||||
free (msg);
|
||||
state->regs[dst].has_pp_slot = true;
|
||||
state->regs[dst].pp_slot = slot_off;
|
||||
// Link the instruction to the ObjectPool slot it reads (pp_base + off), so
|
||||
// `axt` on the slot shows the loading code; the slot's own ref to its value
|
||||
// (applied by -AA) then chains through to the string/object. Works on any
|
||||
// arch since pp_base + off is the slot address regardless of index scheme.
|
||||
if (pp_refs && pp_refs->pp_base) {
|
||||
flutter_add_ref (core, at, pp_refs->pp_base + slot_off, R_ANAL_REF_TYPE_DATA | R_ANAL_REF_TYPE_READ);
|
||||
}
|
||||
RList *strings = flutter_pp_string_map_get (pp_strings, slot_off);
|
||||
FlutterPpStringRef *first = strings? r_list_first (strings): NULL;
|
||||
if (!first || first->indirect) {
|
||||
if (!flutter_annotate_pp_ref (core, pp_refs, at, slot_off, false, stats)) {
|
||||
char *msg = r_str_newf ("dart: PP slot +0x%" PFMT64x, slot_off);
|
||||
flutter_append_comment (core, at, msg, stats);
|
||||
free (msg);
|
||||
}
|
||||
}
|
||||
RListIter *it;
|
||||
FlutterPpStringRef *string_ref;
|
||||
r_list_foreach (strings, it, string_ref) {
|
||||
flutter_set_flag (core, "dart.str", string_ref->string_value, string_ref->string_addr, (ut32)strlen (string_ref->string_value), true);
|
||||
flutter_add_ref (core, at, string_ref->string_addr, R_ANAL_REF_TYPE_STRN | R_ANAL_REF_TYPE_READ);
|
||||
stats->string_refs++;
|
||||
char *escaped = r_str_escape_utf8 (string_ref->string_value, false, true);
|
||||
char *msg = r_str_newf ("dart: PP slot +0x%" PFMT64x " %sstring \"%s\"", pp_off, string_ref->indirect? "contains ": "", escaped);
|
||||
flutter_append_comment (core, at, msg, stats);
|
||||
free (msg);
|
||||
free (escaped);
|
||||
}
|
||||
stats->pp_refs++;
|
||||
return true;
|
||||
}
|
||||
@@ -821,7 +1306,29 @@ static bool flutter_process_field_store(RCore *core, FlutterAnalModel *model, Fl
|
||||
return true;
|
||||
}
|
||||
|
||||
static void flutter_process_indirect_call(RCore *core, FlutterAnalState *state, ut64 at, const FlutterOpInfo *info, FlutterAnalStats *stats) {
|
||||
// Keep the pool-slot taint across a dereference of a pool object, such as the
|
||||
// `ldr xN, [xN, #entry_point]` that extracts a Code object's entry point right
|
||||
// before an indirect call. Without this the slot association is lost.
|
||||
static bool flutter_process_pp_slot_deref(FlutterAnalState *state, const FlutterOpInfo *info) {
|
||||
if (!state || !info || info->n_regs < 1 || !info->has_mem) {
|
||||
return false;
|
||||
}
|
||||
int base = flutter_reg_index (info->mem_base);
|
||||
if (base < 0 || !state->regs[base].has_pp_slot) {
|
||||
return false;
|
||||
}
|
||||
int dst = flutter_reg_index (info->regs[0]);
|
||||
if (dst < 0) {
|
||||
return false;
|
||||
}
|
||||
ut64 slot = state->regs[base].pp_slot;
|
||||
memset (&state->regs[dst], 0, sizeof (state->regs[dst]));
|
||||
state->regs[dst].has_pp_slot = true;
|
||||
state->regs[dst].pp_slot = slot;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void flutter_process_indirect_call(RCore *core, FlutterAnalState *state, FlutterPpRefMap *pp_refs, ut64 at, const FlutterOpInfo *info, FlutterAnalStats *stats) {
|
||||
if (!core || !state || !info || info->n_regs < 1) {
|
||||
return;
|
||||
}
|
||||
@@ -829,15 +1336,23 @@ static void flutter_process_indirect_call(RCore *core, FlutterAnalState *state,
|
||||
if (reg < 0) {
|
||||
return;
|
||||
}
|
||||
if (state->regs[reg].has_pp_off) {
|
||||
char *msg = r_str_newf ("dart: indirect call via PP+0x%" PFMT64x, state->regs[reg].pp_off);
|
||||
flutter_append_comment (core, at, msg, stats);
|
||||
free (msg);
|
||||
// The call target is the object held in the register (a Code/Function
|
||||
// loaded from a PP slot), possibly after an entry-point deref. Use the slot
|
||||
// that value came from, not a PP-pointer offset.
|
||||
if (state->regs[reg].has_pp_slot) {
|
||||
ut64 slot = state->regs[reg].pp_slot;
|
||||
// A call through a resolved code slot gets a real CALL xref and the
|
||||
// target name; otherwise keep the plain via-PP note.
|
||||
if (!flutter_annotate_pp_ref (core, pp_refs, at, slot, true, stats)) {
|
||||
char *msg = r_str_newf ("dart: indirect call via PP+0x%" PFMT64x, slot);
|
||||
flutter_append_comment (core, at, msg, stats);
|
||||
free (msg);
|
||||
}
|
||||
stats->pp_refs++;
|
||||
}
|
||||
}
|
||||
|
||||
static void flutter_scan_function(RCore *core, FlutterAnalModel *model, RAnalFunction *fcn, FlutterAnalStats *stats) {
|
||||
static void flutter_scan_function(RCore *core, FlutterAnalModel *model, FlutterPpStringMap *pp_strings, FlutterPpRefMap *pp_refs, RAnalFunction *fcn, FlutterAnalStats *stats) {
|
||||
if (!core || !model || !fcn || !stats) {
|
||||
return;
|
||||
}
|
||||
@@ -855,9 +1370,15 @@ static void flutter_scan_function(RCore *core, FlutterAnalModel *model, RAnalFun
|
||||
}
|
||||
|
||||
HtUP *seen_ops = ht_up_new0 ();
|
||||
RListIter *it;
|
||||
RAnalBlock *bb;
|
||||
#if R2_ABIVERSION >= 168
|
||||
RAnalBlock **it;
|
||||
R_VEC_FOREACH (&fcn->bbs, it) {
|
||||
bb = *it;
|
||||
#else
|
||||
RListIter *it;
|
||||
r_list_foreach (fcn->bbs, it, bb) {
|
||||
#endif
|
||||
if (!bb || bb->size == 0) {
|
||||
continue;
|
||||
}
|
||||
@@ -866,7 +1387,7 @@ static void flutter_scan_function(RCore *core, FlutterAnalModel *model, RAnalFun
|
||||
at += 4;
|
||||
continue;
|
||||
}
|
||||
RAnalOp *op = r_core_anal_op (core, at, R_ARCH_OP_MASK_BASIC | R_ARCH_OP_MASK_OPEX | R_ARCH_OP_MASK_DISASM);
|
||||
RAnalOp *op = r_core_anal_op (core, at, R_ARCH_OP_MASK_BASIC | R_ARCH_OP_MASK_VAL | R_ARCH_OP_MASK_DISASM);
|
||||
if (!op || op->size <= 0) {
|
||||
r_anal_op_free (op);
|
||||
break;
|
||||
@@ -884,8 +1405,13 @@ static void flutter_scan_function(RCore *core, FlutterAnalModel *model, RAnalFun
|
||||
if (has_info && info.has_imm) {
|
||||
flutter_process_direct_ref (core, model, at, info.imm, stats);
|
||||
}
|
||||
if (has_info) {
|
||||
flutter_annotate_null_reg (core, at, op->type & R_ANAL_OP_TYPE_MASK, &info, stats);
|
||||
}
|
||||
|
||||
switch (op->type & R_ANAL_OP_TYPE_MASK) {
|
||||
// Use the effective type so x86-64 memory MOVs dispatch as LOAD/STORE.
|
||||
int etype = has_info? info.etype: (int)(op->type & R_ANAL_OP_TYPE_MASK);
|
||||
switch (etype) {
|
||||
case R_ANAL_OP_TYPE_MOV:
|
||||
if (!flutter_process_mov (&state, &info) && has_info && info.n_regs > 0) {
|
||||
int dst = flutter_reg_index (info.regs[0]);
|
||||
@@ -898,9 +1424,10 @@ static void flutter_scan_function(RCore *core, FlutterAnalModel *model, RAnalFun
|
||||
(void)flutter_process_add (&state, &info);
|
||||
break;
|
||||
case R_ANAL_OP_TYPE_LOAD:
|
||||
if (!flutter_process_pp_load (core, &state, at, &info, stats) &&
|
||||
if (!flutter_process_pp_load (core, &state, pp_strings, pp_refs, at, &info, stats) &&
|
||||
!flutter_process_stack_load (&state, &info) &&
|
||||
!flutter_process_field_load (core, model, &state, at, &info, stats) &&
|
||||
!flutter_process_pp_slot_deref (&state, &info) &&
|
||||
has_info && info.n_regs > 0) {
|
||||
int dst = flutter_reg_index (info.regs[0]);
|
||||
if (dst >= 0) {
|
||||
@@ -923,7 +1450,7 @@ static void flutter_scan_function(RCore *core, FlutterAnalModel *model, RAnalFun
|
||||
case R_ANAL_OP_TYPE_ICALL:
|
||||
case R_ANAL_OP_TYPE_IRCALL:
|
||||
if (has_info) {
|
||||
flutter_process_indirect_call (core, &state, at, &info, stats);
|
||||
flutter_process_indirect_call (core, &state, pp_refs, at, &info, stats);
|
||||
}
|
||||
flutter_state_clobber_callers (&state);
|
||||
break;
|
||||
@@ -938,7 +1465,7 @@ static void flutter_scan_function(RCore *core, FlutterAnalModel *model, RAnalFun
|
||||
flutter_state_fini (&state);
|
||||
}
|
||||
|
||||
static void flutter_scan_functions(RCore *core, FlutterAnalModel *model, RVecFlutterEntry *entries, FlutterAnalStats *stats) {
|
||||
static void flutter_scan_functions(RCore *core, FlutterAnalModel *model, FlutterPpStringMap *pp_strings, FlutterPpRefMap *pp_refs, RVecFlutterEntry *entries, FlutterAnalStats *stats) {
|
||||
if (!core || !model || !entries || !stats) {
|
||||
return;
|
||||
}
|
||||
@@ -953,11 +1480,67 @@ static void flutter_scan_functions(RCore *core, FlutterAnalModel *model, RVecFlu
|
||||
continue;
|
||||
}
|
||||
ht_up_insert (seen_fcns, fcn->addr, fcn);
|
||||
flutter_scan_function (core, model, fcn, stats);
|
||||
flutter_scan_function (core, model, pp_strings, pp_refs, fcn, stats);
|
||||
}
|
||||
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;
|
||||
// Dart's PP register is x27 on arm64 and r15 on x86-64.
|
||||
const char *arch = r_config_get (core->config, "asm.arch");
|
||||
const int bits = (int)r_config_get_i (core->config, "asm.bits");
|
||||
const bool is_x64 = arch && bits == 64 && (!strcmp (arch, "x86") || !strcmp (arch, "x64"));
|
||||
const char *pp_reg = is_x64? "r15": "x27";
|
||||
DartPpInfo pp = { 0 };
|
||||
if (dart_resolve_pp_info (&probe, &pp) && pp.base) {
|
||||
char roregs[32];
|
||||
snprintf (roregs, sizeof (roregs), "%s,gp,zero", pp_reg);
|
||||
r_config_set_i (core->config, "anal.gp", pp.base);
|
||||
r_config_set (core->config, "anal.gpseed", pp_reg);
|
||||
r_config_set (core->config, "anal.roregs", roregs);
|
||||
r_flag_set (core->flags, "PP", pp.base, 1);
|
||||
}
|
||||
dart_pp_info_fini (&pp);
|
||||
dart_read_cache_free (probe.rmem);
|
||||
}
|
||||
|
||||
// Apply the recovered ObjectPool xref graph (including data-image references
|
||||
// such as a const object pointing at a string) so `axt` resolves pool strings
|
||||
// that no instruction loads directly. Runs on a clean copy of the context:
|
||||
// extraction needs the pristine state the standalone `-x` command sees, and the
|
||||
// copy shares heap-owned pointers so only its own read cache is freed. Cheap
|
||||
// relative to the function scan, which is why -AA can afford it. Returns the
|
||||
// number of xrefs applied.
|
||||
int r2flutter_apply_pool_xrefs(RCore *core, DartCtx *dctx) {
|
||||
if (!core || !dctx) {
|
||||
return 0;
|
||||
}
|
||||
DartCtx xrctx = *dctx;
|
||||
xrctx.core = core;
|
||||
xrctx.rmem = NULL;
|
||||
xrctx.layout = NULL;
|
||||
xrctx.dump_string_refs = true;
|
||||
int applied = dart_pool_apply_xrefs (&xrctx);
|
||||
dart_read_cache_free (xrctx.rmem);
|
||||
return applied;
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -966,7 +1549,7 @@ bool r2flutter_analysis_run(RCore *core, DartCtx *dctx, bool quiet) {
|
||||
return false;
|
||||
}
|
||||
dart_app_load_info (app);
|
||||
dart_dumper_apply_to_core (app);
|
||||
dart_dumper_apply_to_core (app, true);
|
||||
|
||||
FlutterAnalModel model;
|
||||
if (!flutter_model_load (&model, core, &app->dctx)) {
|
||||
@@ -979,22 +1562,36 @@ bool r2flutter_analysis_run(RCore *core, DartCtx *dctx, bool quiet) {
|
||||
flutter_apply_app_methods_to_core (core, app, &stats);
|
||||
flutter_apply_model_to_core (&model, core, &stats);
|
||||
|
||||
FlutterPpStringMap pp_strings;
|
||||
flutter_pp_string_map_init (&pp_strings);
|
||||
flutter_pp_string_map_load (&app->dctx, NULL, &pp_strings);
|
||||
|
||||
FlutterPpRefMap pp_refs;
|
||||
flutter_pp_ref_map_init (&pp_refs);
|
||||
flutter_pp_ref_map_load (&app->dctx, &pp_refs);
|
||||
|
||||
RVecFlutterEntry *entries = flutter_collect_entries (core, app, &model);
|
||||
flutter_ensure_functions (core, entries);
|
||||
flutter_scan_functions (core, &model, entries, &stats);
|
||||
dart_dumper_name_functions (app);
|
||||
flutter_scan_functions (core, &model, &pp_strings, &pp_refs, entries, &stats);
|
||||
|
||||
int graph_xrefs = r2flutter_apply_pool_xrefs (core, dctx);
|
||||
|
||||
if (!quiet) {
|
||||
R_LOG_INFO ("Flutter analysis: %d functions, %d calls, %d fields, %d classes, %d types, %d strings, %d PP refs",
|
||||
R_LOG_INFO ("Flutter analysis: %d functions, %d calls, %d fields, %d classes, %d types, %d strings, %d PP refs, %d graph xrefs",
|
||||
(int)stats.functions,
|
||||
(int)stats.call_xrefs,
|
||||
(int)stats.field_refs,
|
||||
(int)stats.class_refs,
|
||||
(int)stats.type_refs,
|
||||
(int)stats.string_refs,
|
||||
(int)stats.pp_refs);
|
||||
(int)stats.pp_refs,
|
||||
graph_xrefs);
|
||||
}
|
||||
|
||||
RVecFlutterEntry_free (entries);
|
||||
flutter_pp_string_map_fini (&pp_strings);
|
||||
flutter_pp_ref_map_fini (&pp_refs);
|
||||
flutter_model_fini (&model);
|
||||
dart_app_free (app);
|
||||
return true;
|
||||
|
||||
@@ -5,5 +5,7 @@
|
||||
#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);
|
||||
int r2flutter_apply_pool_xrefs(RCore *core, DartCtx *dctx);
|
||||
|
||||
#endif
|
||||
+41
-23
@@ -10,7 +10,7 @@
|
||||
#include "../r2/flutter_analysis.h"
|
||||
|
||||
static const char usage_text[] =
|
||||
"Usage: %s [options] <libapp_path_or_dir>\n"
|
||||
"Usage: %s [-jrv*] <libapp_path_or_dir>\n"
|
||||
"Modifiers:\n"
|
||||
" -h Show help\n"
|
||||
" -j Output in JSON format\n"
|
||||
@@ -21,23 +21,20 @@ static const char usage_text[] =
|
||||
" -vv More verbose (dump headers)\n"
|
||||
" -V Show version\n"
|
||||
"Actions:\n"
|
||||
" -A Analyze Dart snapshot and apply flags/comments\n"
|
||||
" -AA Analyze with field extraction enabled\n"
|
||||
" -AAA Run Dart-aware code analysis and recover code refs\n"
|
||||
" -c Print extracted class information\n"
|
||||
" -f Print all extracted functions (addr name)\n"
|
||||
" -H Print Dart AOT snapshot header info\n"
|
||||
" -HH Print extended snapshot header and cluster layout\n"
|
||||
" -HHH Decode selected cluster payloads for diagnostics\n"
|
||||
" -i Print instruction table entries to stdout\n"
|
||||
" -O <addr|pp+off> Decode Dart tagged/object pointer or ObjectPool PP slot\n"
|
||||
" -p Print reconstructed ObjectPool PP value\n"
|
||||
" -A[AA] Analyze Dart snapshot and apply flags/comments\n"
|
||||
" -c[jr*] Print extracted class information\n"
|
||||
" -f[jr*] Print all extracted functions (addr name)\n"
|
||||
" -H[HH] Print Dart AOT snapshot header info\n"
|
||||
" -i[jr*] Print instruction table entries (-ii same as -i)\n"
|
||||
" -ie[jr*], -E[jr*] Print Dart code entrypoint\n"
|
||||
" -O <addr|pp+off|ref:N> Decode a tagged pointer, PP slot, or snapshot reference\n"
|
||||
" -p[jr*] Print reconstructed ObjectPool PP value\n"
|
||||
" -R Print radare2 script for snapshot analysis\n"
|
||||
" -S Print best-effort recovered SBOM/components\n"
|
||||
" -T Print string-based type names\n"
|
||||
" -x Print metadata/data-image xrefs; with -z, include string refs/ax in -r\n"
|
||||
" -z Print reliable ObjectPool-referenced strings (-q prints values only)\n"
|
||||
" -zz Print all fuzzy/carved extracted strings (-xzz includes refs/ax in -r)\n"
|
||||
" -S[jr*] Print best-effort recovered SBOM/components\n"
|
||||
" -T[jr*] Print string-based type names\n"
|
||||
" -x[jr*] Print metadata/data-image xrefs; with -z, include string refs/ax in -r\n"
|
||||
" -z[jr*] Print reliable ObjectPool-referenced strings (-q prints values only)\n"
|
||||
" -zz[jr*] Print all fuzzy/carved extracted strings (-xzz includes refs/ax in -r)\n"
|
||||
"Options:\n"
|
||||
" -D <hash|version> Override Dart snapshot profile for analysis\n"
|
||||
" -l <N> Limit output to N items\n"
|
||||
@@ -85,8 +82,8 @@ static char *resolve_input_path(const char *s, DartCtx *dctx, bool *extracted_in
|
||||
return NULL;
|
||||
}
|
||||
DartAppEmbeddedPayload payload;
|
||||
if (dart_app_find_macho_embedded_dart (s, &payload)) {
|
||||
char *inner = dart_app_extract_embedded_payload (s, &payload);
|
||||
if (dart_app_find_macho_dart (s, &payload)) {
|
||||
char *inner = dart_app_extract_payload (s, &payload);
|
||||
if (!inner) {
|
||||
R_LOG_ERROR ("Failed to extract embedded Dart Mach-O from: %s", s);
|
||||
return NULL;
|
||||
@@ -118,7 +115,7 @@ int main(int argc, char **argv) {
|
||||
.no_stubs = true
|
||||
};
|
||||
RGetopt opt;
|
||||
r_getopt_init (&opt, argc, (const char **)argv, "AcD:fhHijnm:O:pqrRSzTvVxl:");
|
||||
r_getopt_init (&opt, argc, (const char **)argv, "AcD:eEfHhijnm:O:pqrRSzTvVxl:");
|
||||
int c;
|
||||
while ((c = r_getopt_next (&opt)) != -1) {
|
||||
switch (c) {
|
||||
@@ -129,6 +126,9 @@ int main(int argc, char **argv) {
|
||||
case 'c':
|
||||
action = c;
|
||||
break;
|
||||
case 'E':
|
||||
action = 'e';
|
||||
break;
|
||||
case 'f':
|
||||
action = c;
|
||||
break;
|
||||
@@ -143,6 +143,14 @@ int main(int argc, char **argv) {
|
||||
action = c;
|
||||
dctx.dump_it = true;
|
||||
break;
|
||||
case 'e':
|
||||
if (action == 'i') {
|
||||
action = c;
|
||||
} else {
|
||||
R_LOG_ERROR ("Invalid flag: -%c", c);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 'O':
|
||||
action = c;
|
||||
object_spec = opt.arg;
|
||||
@@ -161,6 +169,7 @@ int main(int argc, char **argv) {
|
||||
break;
|
||||
case 'l':
|
||||
dctx.dump_fns_limit = atoi (opt.arg);
|
||||
dctx.str_limit = r_num_get (NULL, opt.arg);
|
||||
break;
|
||||
case 'n':
|
||||
dctx.use_name_pool = true;
|
||||
@@ -207,6 +216,7 @@ int main(int argc, char **argv) {
|
||||
print_usage (argv[0]);
|
||||
return 1;
|
||||
default:
|
||||
R_LOG_ERROR ("Invalid flag: -%c", opt.opt);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -244,6 +254,11 @@ int main(int argc, char **argv) {
|
||||
free_resolved_path (libapp_path, extracted_inner);
|
||||
return 1;
|
||||
}
|
||||
// r2flutter does its own Dart-aware string/symbol extraction and never
|
||||
// consumes RBin's string list, so skip the costly RBin string scan that
|
||||
// r_core_bin_load runs over the whole binary (dominant load-time cost on
|
||||
// large libapp.so / App images).
|
||||
r_config_set_b (core->config, "bin.strings", false);
|
||||
r_core_bin_load (core, NULL, 0);
|
||||
|
||||
dctx.core = core;
|
||||
@@ -293,7 +308,7 @@ int main(int argc, char **argv) {
|
||||
R_LOG_INFO ("Loaded %zu functions from Dart snapshot", count);
|
||||
}
|
||||
}
|
||||
dart_dumper_apply_to_core (app);
|
||||
dart_dumper_apply_to_core (app, false);
|
||||
break;
|
||||
case 'z':
|
||||
output = string_depth >= 2? dart_pool_dump_strings_fuzzy (&dctx, fmt): dart_pool_dump_strings (&dctx, fmt);
|
||||
@@ -303,6 +318,9 @@ int main(int argc, char **argv) {
|
||||
dctx.dump_fields = 1;
|
||||
output = dart_pool_dump_classes (&dctx, fmt);
|
||||
break;
|
||||
case 'e':
|
||||
output = dart_pool_dump_entry (&app->dctx, fmt);
|
||||
break;
|
||||
case 'T':
|
||||
dctx.dump_classes = 3;
|
||||
output = dart_pool_dump_classes (&dctx, fmt);
|
||||
@@ -322,7 +340,7 @@ int main(int argc, char **argv) {
|
||||
output = dart_pool_dump_object (&app->dctx, object_spec, fmt);
|
||||
break;
|
||||
case 'p':
|
||||
output = dart_pool_dump_pp (&app->dctx, fmt);
|
||||
output = dart_pool_dump_pool (&app->dctx, fmt);
|
||||
break;
|
||||
case 'x':
|
||||
output = dart_pool_dump_xrefs (&dctx, fmt);
|
||||
@@ -332,7 +350,7 @@ int main(int argc, char **argv) {
|
||||
if (dctx.verbose) {
|
||||
R_LOG_ERROR ("Dumping radare2 script");
|
||||
}
|
||||
char *s = dart_dumper_dump4radare2 (app);
|
||||
char *s = dart_dumper_dump_r2 (app);
|
||||
printf ("%s\n", s);
|
||||
free (s);
|
||||
break;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "appstrings-android-w0rdle",
|
||||
"cwd": "test",
|
||||
"cmd": ["../bin/r2flutter", "-q", "-z", "bins/android/w0rdle/libapp.so"],
|
||||
"timeout": 30,
|
||||
"expect": {
|
||||
"contains": [
|
||||
"assets/allowed_guesses.txt",
|
||||
"ph0wn CTF is awesome"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "dump-classes-macos",
|
||||
"cwd": "test",
|
||||
"cmd": ["../bin/r2flutter", "-c", "bins/macos/hello/hello.aot"],
|
||||
"timeout": 30,
|
||||
"expect": {
|
||||
"contains": [
|
||||
"class _Closure@0150898 (size=64)",
|
||||
"+0x28 dynamic _context@0150898",
|
||||
"class _Uri@0150898 (size=96)",
|
||||
"+0x0 dynamic _portMap@1026248 static final"
|
||||
],
|
||||
"not_contains": [
|
||||
"class _Uint16List (size=0)",
|
||||
"class FutureOr (size=0)"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "dump-funcs-android-w0rdle",
|
||||
"cwd": "test",
|
||||
"cmd": ["../bin/r2flutter", "-q", "-f", "bins/android/w0rdle/libapp.so"],
|
||||
"timeout": 30,
|
||||
"expect": {
|
||||
"contains": [
|
||||
"method.WordService.getWordToFind",
|
||||
"method.Flutterdle.updateAfterSuccessfulGuess"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
"timeout": 30,
|
||||
"expect": {
|
||||
"contains": [
|
||||
"func.0000fe28",
|
||||
"method._HashMap_3220832.addAll",
|
||||
"0xfe28"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"timeout": 30,
|
||||
"expect": {
|
||||
"contains": [
|
||||
"func.0000e768",
|
||||
"method.PointerEvent.get:radiusMinor",
|
||||
"0xe768"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
"cmd": ["../bin/r2flutter", "-l", "3", "-i", "bins/ios/Runner.app"],
|
||||
"timeout": 20,
|
||||
"expect": {
|
||||
"equals": "# length=7670 first_entry_with_code=0 canonical_stack_map_entries_offset=160751\n0 0xe768 code func.0000e768\n1 0xe770 code func.0000e770\n2 0xe7a0 code func.0000e7a0"
|
||||
"equals": "# length=7670 first_entry_with_code=0 canonical_stack_map_entries_offset=160751\n0 0xe768 code method.PointerEvent.get:radiusMinor\n1 0xe770 code func.0000e770\n2 0xe7a0 code method.PointerEvent.get:distance"
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,6 @@
|
||||
"cmd": ["../bin/r2flutter", "-j", "-l", "2", "-i", "bins/ios/Runner.app"],
|
||||
"timeout": 20,
|
||||
"expect": {
|
||||
"equals": "{\"length\":7670,\"first_entry_with_code\":0,\"canonical_stack_map_entries_offset\":160751,\"entries\":[{\"index\":0,\"code_index\":0,\"address\":59240,\"pc_offset\":104,\"stack_map_offset\":61376,\"kind\":\"code\",\"name\":\"func.0000e768\"},{\"index\":1,\"code_index\":1,\"address\":59248,\"pc_offset\":112,\"stack_map_offset\":61376,\"kind\":\"code\",\"name\":\"func.0000e770\"}]}"
|
||||
"equals": "{\"length\":7670,\"first_entry_with_code\":0,\"canonical_stack_map_entries_offset\":160751,\"entries\":[{\"index\":0,\"code_index\":0,\"address\":59240,\"pc_offset\":104,\"stack_map_offset\":61376,\"kind\":\"code\",\"name\":\"method.PointerEvent.get:radiusMinor\"},{\"index\":1,\"code_index\":1,\"address\":59248,\"pc_offset\":112,\"stack_map_offset\":61376,\"kind\":\"code\",\"name\":\"func.0000e770\"}]}"
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,6 @@
|
||||
"cmd": ["../bin/r2flutter", "-r", "-l", "2", "-i", "bins/ios/Runner.app"],
|
||||
"timeout": 20,
|
||||
"expect": {
|
||||
"equals": "# Dart InstructionTable entries\n# length=7670 first_entry_with_code=0 canonical_stack_map_entries_offset=160751\n# it[0] func.0000e768\nf it.code_0 = 0xe768\n# it[1] func.0000e770\nf it.code_1 = 0xe770"
|
||||
"equals": "# Dart InstructionTable entries\n# length=7670 first_entry_with_code=0 canonical_stack_map_entries_offset=160751\n# it[0] method.PointerEvent.get:radiusMinor\nf it.code_0 = 0xe768\n# it[1] func.0000e770\nf it.code_1 = 0xe770"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "dump-strings-android-w0rdle",
|
||||
"cwd": "test",
|
||||
"cmd": ["../bin/r2flutter", "-q", "-z", "bins/android/w0rdle/libapp.so"],
|
||||
"timeout": 30,
|
||||
"expect": {
|
||||
"contains": [
|
||||
"ph0wn",
|
||||
"y@cht",
|
||||
"answer",
|
||||
"ph0wn{you_w1n_darts_stage1}"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "header-android-w0rdle",
|
||||
"cwd": "test",
|
||||
"cmd": ["../bin/r2flutter", "-H", "bins/android/w0rdle/libapp.so"],
|
||||
"timeout": 30,
|
||||
"expect": {
|
||||
"contains": [
|
||||
"dart_version: 2.18.0",
|
||||
"tag_style: CID_SHIFT1",
|
||||
"cid_string: 90",
|
||||
"cid_object_pool: 21"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,8 @@
|
||||
"contains": [
|
||||
"snapshot_hash=78da37fed6bf1489361a312568249f3f dart_version=3.11.5",
|
||||
"detail: object_pool_entries",
|
||||
"34 cid=23 alloc=object_pool fill=none count=1",
|
||||
"object_pool_decode: fill_not_parsed"
|
||||
"34 cid=23 alloc=object_pool fill=object_pool count=1",
|
||||
"object_pool ref=9145 index=0 length=1380"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "header-json-android-w0rdle",
|
||||
"cwd": "test",
|
||||
"cmd": ["../bin/r2flutter", "-Hj", "bins/android/w0rdle/libapp.so"],
|
||||
"timeout": 30,
|
||||
"expect": {
|
||||
"json": {
|
||||
"dart_version": "2.18.0",
|
||||
"hash": "b0e899ec5a90e4661501f0b69e9dd70f",
|
||||
"cws": 4,
|
||||
"tag_style": "CID_SHIFT1"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,13 +7,10 @@
|
||||
"contains": [
|
||||
"Usage:",
|
||||
"-i",
|
||||
"-H",
|
||||
"-HH",
|
||||
"-HHH",
|
||||
"-H[HH]",
|
||||
"-p",
|
||||
"-S",
|
||||
"-AA",
|
||||
"-AAA"
|
||||
"-A[AA]"
|
||||
],
|
||||
"not_contains": [
|
||||
"--no-stubs"
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "methods-android-w0rdle",
|
||||
"cwd": "test",
|
||||
"cmd": ["../bin/r2flutter", "-q", "-f", "bins/android/w0rdle/libapp.so"],
|
||||
"timeout": 30,
|
||||
"expect": {
|
||||
"contains": [
|
||||
"method.WordService.getWordToFind",
|
||||
"method.WordService._readFile_746434692",
|
||||
"method.Flutterdle.didWin",
|
||||
"method.CachingAssetBundle.loadString"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,6 @@
|
||||
"cmd": ["../bin/r2flutter", "-j", "-f", "-l", "3", "-m", "custom/obf-map.txt", "bins/ios/Runner.app"],
|
||||
"timeout": 30,
|
||||
"expect": {
|
||||
"equals": "[{\"addr\":59240,\"name\":\"func.0000e768\"},{\"addr\":59248,\"name\":\"func.0000e770\"},{\"addr\":59296,\"name\":\"func.0000e7a0\"}]"
|
||||
"equals": "[{\"addr\":59240,\"name\":\"method.PointerEvent.get:radiusMinor\",\"signature\":\"dynamic(PointerEvent)\"},{\"addr\":59248,\"name\":\"func.0000e770\"},{\"addr\":59296,\"name\":\"method.PointerEvent.get:distance\",\"signature\":\"dynamic(PointerEvent)\"}]"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "objdecode-android-w0rdle",
|
||||
"cwd": "test",
|
||||
"cmd": ["../bin/r2flutter", "-O", "pp+36864", "bins/android/w0rdle/libapp.so"],
|
||||
"timeout": 30,
|
||||
"expect": {
|
||||
"contains": [
|
||||
"target.kind: string",
|
||||
"target.name: \"floatingActionButtonTheme\""
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "objdecode-func-android-w0rdle",
|
||||
"cwd": "test",
|
||||
"cmd": ["../bin/r2flutter", "-O", "pp+1240", "bins/android/w0rdle/libapp.so"],
|
||||
"timeout": 30,
|
||||
"expect": {
|
||||
"contains": [
|
||||
"target.kind: function",
|
||||
"target.name: \"method."
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "pool-android-w0rdle",
|
||||
"cwd": "test",
|
||||
"cmd": ["../bin/r2flutter", "-p", "bins/android/w0rdle/libapp.so"],
|
||||
"timeout": 30,
|
||||
"expect": {
|
||||
"contains": [
|
||||
"length: 12351 entries"
|
||||
],
|
||||
"not_contains": [
|
||||
"PP not resolved"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "pool-ios-authpass-shift1",
|
||||
"cwd": "test",
|
||||
"cmd": ["../bin/r2flutter", "-p", "bins/ios/AuthPass.app/Frameworks/App.framework/App"],
|
||||
"timeout": 30,
|
||||
"expect": {
|
||||
"contains": [
|
||||
"length: 43268 entries"
|
||||
],
|
||||
"not_contains": [
|
||||
"PP not resolved"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "signatures-android-w0rdle",
|
||||
"cwd": "test",
|
||||
"cmd": ["../bin/r2flutter", "-q", "-j", "-f", "bins/android/w0rdle/libapp.so"],
|
||||
"timeout": 30,
|
||||
"expect": {
|
||||
"contains": [
|
||||
"\"signature\":\"String(_StringBase@0150898,dynamic)\"",
|
||||
"\"signature\":\"dynamic(WordService)\""
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
NAME=r2-analysis-string-comment
|
||||
FILE=bins/android/first/libapp.so
|
||||
ARGS=-NN -e scr.color=0 -e bin.relocs.apply=true
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.so
|
||||
L ../src/r2/core_flutter.dylib
|
||||
r2flutter -AAA
|
||||
pd 1 @ 0x8b914~&_nativeGetFloat64,dart: string[0]
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0x0008b914
|
||||
EOF
|
||||
RUN
|
||||
@@ -0,0 +1,13 @@
|
||||
NAME=r2-analysis-pp-string-comment-ios
|
||||
FILE=bins/ios/Runner.app/Frameworks/App.framework/App
|
||||
ARGS=-NN -e scr.color=0 -e bin.relocs.apply=true
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.so
|
||||
L ../src/r2/core_flutter.dylib
|
||||
r2flutter -AAA
|
||||
pd 1 @ 0x176d0~&PP slot,string,https[0]
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0x000176d0
|
||||
EOF
|
||||
RUN
|
||||
@@ -0,0 +1,102 @@
|
||||
NAME=r2-authpass-tls-flags
|
||||
FILE=bins/ios/AuthPass.app/Frameworks/App.framework/App
|
||||
ARGS=-NN -e bin.relocs.apply=true
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -A
|
||||
f~onBadCertificateWrapper~method
|
||||
f~registerBadCertificateCallback~method~!_registerBadCertificateCallback_
|
||||
f~_registerBadCertificateCallback_~method
|
||||
f~_secureHandshake_~method
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0x003199d4 0 method._RawSecureSocket_14069316._onBadCertificateWrapper_14069316
|
||||
0x003194ec 0 method._SecureFilterImpl_14069316.registerBadCertificateCallback
|
||||
0x0031954c 0 method._SecureFilterImpl_14069316._registerBadCertificateCallback_14069316
|
||||
0x0031749c 0 method._RawSecureSocket_14069316._secureHandshake_14069316
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=r2-authpass-callsite-xrefs
|
||||
FILE=bins/ios/AuthPass.app/Frameworks/App.framework/App
|
||||
ARGS=-NN -e bin.relocs.apply=true
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -A
|
||||
pd 1 @ 0x3199bc~onBadCertificateWrapper[0]
|
||||
pd 1 @ 0x316cd8~registerBadCertificateCallback[0]
|
||||
pd 1 @ 0x319530~_registerBadCertificateCallback_[0]
|
||||
pd 1 @ 0x317028~_secureHandshake_[0]
|
||||
pd 1 @ 0x317514~_secureHandshake_[0]
|
||||
pd 1 @ 0x3178b4~_secureHandshake_[0]
|
||||
pd 1 @ 0x317e04~_secureHandshake_[0]
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0x003199bc
|
||||
0x00316cd8
|
||||
0x00319530
|
||||
0x00317028
|
||||
0x00317514
|
||||
0x003178b4
|
||||
0x00317e04
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=r2-authpass-handshake-disasm
|
||||
FILE=bins/ios/AuthPass.app/Frameworks/App.framework/App
|
||||
ARGS=-NN -e bin.relocs.apply=true
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -A
|
||||
pd 1 @ 0x3174e4
|
||||
pd 1 @ 0x31752c
|
||||
pd 1 @ 0x317554
|
||||
pd 1 @ 0x3175ac
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0x003174e4 3f060094 bl sym.func.00318de0 ; method._SecureFilterImpl_14069316.handshake ; method._SecureFilterImpl_14069316.handshake(0x0, 0x0, 0x0, 0x0)
|
||||
0x0031752c c1c20091 add x1, x22, 0x30
|
||||
0x00317554 13000094 bl sym.func.003175a0 ; method._RawSecureSocket_14069316._scheduleFilter_14069316 ; method._RawSecureSocket_14069316._scheduleFilter_14069316(0x0, 0x0, 0x0, 0x0)
|
||||
0x003175ac c0820091 add x0, x22, 0x20 ; mach0_segment64_0
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=r2-authpass-wrapper-disasm
|
||||
FILE=bins/ios/AuthPass.app/Frameworks/App.framework/App
|
||||
ARGS=-NN -e bin.relocs.apply=true
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -A
|
||||
pd 1 @ 0x3199f0
|
||||
pd 1 @ 0x319a10
|
||||
pd 1 @ 0x319a1c
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0x003199f0 01f045f8 ldur x1, [x0, 0x5f]
|
||||
0x00319a10 40003fd6 blr x2 ; app.base(0x0, 0x0, 0x0, 0x0)
|
||||
0x00319a1c c0035fd6 ret
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=r2-authpass-qjs-bypass
|
||||
FILE=bins/ios/AuthPass.app/Frameworks/App.framework/App
|
||||
ARGS=-NN -e bin.relocs.apply=true
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -A
|
||||
f~onBadCertificateWrapper~method
|
||||
e io.cache=true
|
||||
s 0x3199d4
|
||||
wx c0820091c0035fd6
|
||||
p8 8
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0x003199d4 0 method._RawSecureSocket_14069316._onBadCertificateWrapper_14069316
|
||||
c0820091c0035fd6
|
||||
EOF
|
||||
RUN
|
||||
@@ -0,0 +1,36 @@
|
||||
NAME=serialized-class-layout-metadata
|
||||
FILE=malloc://0x94
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
wx 50414421f5f5dcdc8c000000000000000200000000000000303435313930376332656161383436376538343863303036376266653865643470726f647563742061726d3634206e6f2d636f6d707265737365642d706f696e746572730086828180800020c182818080808080808280838080807022c4c4c3c2c1c0c08584808080808080858086808080070040c0c5c4c0c0c0c0
|
||||
r2flutter -q -j -c~{=}ref,class_id,layout.,flags.toplevel
|
||||
o malloc://0x7c
|
||||
wx 50414421f5f5dcdc74000000000000000200000000000000303435313930376332656161383436376538343863303036376266653865643470726f647563742061726d3634206e6f2d636f6d707265737365642d706f696e746572730086818180800020c181818080808080808280838080807022c4c4c3c2c1c0c0
|
||||
r2flutter -q -j -c
|
||||
r2flutter -q -j -HHH~{snapshots[0].clusters[0].fill_status}
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
json[0].ref = 7;
|
||||
json[0].class_id = 70000;
|
||||
json[0].library.ref = 2;
|
||||
json[0].super.type_ref = 3;
|
||||
json[0].layout.instance_size = 32;
|
||||
json[0].layout.next_field_offset = 24;
|
||||
json[0].layout.type_params = 1;
|
||||
json[0].layout.type_arg_offset = 16;
|
||||
json[0].layout.field_bitmap = 5;
|
||||
json[0].flags.toplevel = false;
|
||||
json[1].ref = 8;
|
||||
json[1].class_id = 1048583;
|
||||
json[1].library.ref = 5;
|
||||
json[1].super.type_ref = 6;
|
||||
json[1].layout.instance_size = 40;
|
||||
json[1].layout.next_field_offset = 32;
|
||||
json[1].layout.type_params = 0;
|
||||
json[1].layout.type_arg_offset = 0;
|
||||
json[1].flags.toplevel = true;
|
||||
[]
|
||||
failed
|
||||
EOF
|
||||
RUN
|
||||
@@ -1,7 +1,9 @@
|
||||
NAME=r2-classes-android
|
||||
FILE=-
|
||||
FILE=bins/android/first/libapp.so
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -r -c bins/android/first 2>/dev/null | sed -n '1,7p'
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -r -c~:0..7
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
# Dart classes extracted from snapshot
|
||||
@@ -15,9 +17,11 @@ EOF
|
||||
RUN
|
||||
|
||||
NAME=text-classes-android
|
||||
FILE=-
|
||||
FILE=bins/android/first/libapp.so
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -c bins/android/first 2>/dev/null | head -6
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -c~:0..6
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
class _Uint16List (size=0)
|
||||
|
||||
+20
-16
@@ -1,30 +1,34 @@
|
||||
NAME=text-classes-ios
|
||||
FILE=-
|
||||
FILE=bins/ios/Runner.app/Frameworks/App.framework/App
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -c bins/ios/Runner.app 2>/dev/null | sed -n '1,6p'
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -c~:0..6
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
class _Uint16List (size=0)
|
||||
class _ExternalInt64Array (size=0)
|
||||
class FutureOr (size=0)
|
||||
class _Set (size=0)
|
||||
class Int32List. (size=0)
|
||||
class Float32List (size=0)
|
||||
class Pointer (size=32)
|
||||
methods:
|
||||
0x00115f2c get:hashCode (GetterFunction)
|
||||
0x001299fc == (RegularFunction)
|
||||
class NativeType (size=16)
|
||||
class NativeFunction (size=16)
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=r2-classes-ios
|
||||
FILE=-
|
||||
FILE=bins/ios/Runner.app/Frameworks/App.framework/App
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -r -c bins/ios/Runner.app 2>/dev/null | head -7
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -r -c~:0..7
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
# Dart classes extracted from snapshot
|
||||
# class _Uint16List (size=0)
|
||||
"td struct dart__Uint16List { };"
|
||||
# class _ExternalInt64Array (size=0)
|
||||
"td struct dart__ExternalInt64Array { };"
|
||||
# class FutureOr (size=0)
|
||||
"td struct dart_FutureOr { };"
|
||||
# class Pointer (size=32)
|
||||
# type_params: 1
|
||||
"td struct dart_Pointer { };"
|
||||
# method 0x00115f2c get:hashCode (GetterFunction)
|
||||
# method 0x001299fc == (RegularFunction)
|
||||
# class NativeType (size=16)
|
||||
EOF
|
||||
RUN
|
||||
+27
-12
@@ -1,35 +1,50 @@
|
||||
NAME=text-classes-mafia
|
||||
FILE=-
|
||||
CMDS=!!../bin/r2flutter -c bins/android/mafia 2>/dev/null | head -6
|
||||
FILE=bins/android/mafia/libapp.so
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -c~:0..6
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
class Pointer (size=32)
|
||||
methods:
|
||||
0x00000000 get:hashCode (GetterFunction)
|
||||
0x00000000 == (RegularFunction)
|
||||
0x00c6ab64 get:hashCode (GetterFunction)
|
||||
0x00d8bde8 == (RegularFunction)
|
||||
class NativeType (size=16)
|
||||
class NativeFunction (size=16)
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=text-classes-mafia-modern-members
|
||||
FILE=-
|
||||
CMDS=!!../bin/r2flutter -c bins/android/mafia 2>/dev/null | awk '/class _RawReceivePort@1026248/{raw=1} raw && /_portMap@1026248 static final/{field=1} /firstMatch/{method=1} END { print "field=" field + 0; print "method=" method + 0 }'
|
||||
FILE=bins/android/mafia/libapp.so
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -c~?&_RawReceivePort@1026248,size=32
|
||||
r2flutter -c~?_portMap@1026248
|
||||
r2flutter -c~?firstMatch
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
field=1
|
||||
method=1
|
||||
1
|
||||
1
|
||||
1
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=r2-classes-mafia
|
||||
FILE=-
|
||||
CMDS=!!../bin/r2flutter -r -c bins/android/mafia 2>/dev/null | head -7
|
||||
FILE=bins/android/mafia/libapp.so
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -r -c~:0..7
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
# Dart classes extracted from snapshot
|
||||
# class Pointer (size=32)
|
||||
# type_params: 1
|
||||
"td struct dart_Pointer { };"
|
||||
# method 0x00000000 get:hashCode (GetterFunction)
|
||||
# method 0x00000000 == (RegularFunction)
|
||||
# method 0x00c6ab64 get:hashCode (GetterFunction)
|
||||
# method 0x00d8bde8 == (RegularFunction)
|
||||
# class NativeType (size=16)
|
||||
EOF
|
||||
RUN
|
||||
@@ -1,19 +1,46 @@
|
||||
NAME=r2-plugin-C-mafia-future
|
||||
FILE=-
|
||||
FILE=bins/android/mafia/libapp.so
|
||||
ARGS=-NN -e bin.relocs.apply=true
|
||||
CMDS=<<EOF
|
||||
!!sh -c "p=../src/r2/core_flutter.dylib; [ -f \"\$p\" ] || p=../src/r2/core_flutter.so; r2 -NN -q -escr.color=0 -e bin.relocs.apply=true -i \"\$p\" -qc \"r2flutter -C\" -qc \"ic dart_Future\" -qc q bins/android/mafia/libapp.so 2>&1"
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -C
|
||||
icqq~dart_Future
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
INFO: Applied 8986 Dart classes, 1756 fields, 26609 methods
|
||||
dart_FutureOr
|
||||
dart_FutureProviderRef
|
||||
dart_FutureProvider
|
||||
dart_FutureBuilder
|
||||
dart_FutureProviderFamilyBuilder
|
||||
dart_FutureProviderFamily
|
||||
dart_FutureHandlerProviderElementMixin
|
||||
dart_FutureProviderElement
|
||||
dart_Future
|
||||
EOF
|
||||
RUN
|
||||
|
||||
# Remove BROKEN=1 when radare2 6.2.2 is released.
|
||||
NAME=r2-plugin-C-mafia-future-method-attributes
|
||||
BROKEN=1
|
||||
FILE=bins/android/mafia/libapp.so
|
||||
ARGS=-NN -e bin.relocs.apply=true
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -C
|
||||
ic dart_Future
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
class dart_Future
|
||||
0x00000000 method dart_Future Future_error
|
||||
0x00000000 method dart_Future Future_
|
||||
0x00000000 method dart_Future _anonymous_closure_
|
||||
0x00000000 method dart_Future Future_value
|
||||
0x00000000 method dart_Future wait
|
||||
0x00000000 method dart_Future handleError
|
||||
0x00000000 method dart_Future Future_delayed
|
||||
0x00000000 method dart_Future Future_microtask
|
||||
0x00000000 method dart_Future Future_sync
|
||||
0x005d4f54 method dart_Future sC Future_error
|
||||
0x005d70cc method dart_Future sC Future_
|
||||
0x005d71b8 method dart_Future s _anonymous_closure_
|
||||
0x005e4a98 method dart_Future sC Future_value
|
||||
0x0060d7d4 method dart_Future s wait
|
||||
0x0060dfac method dart_Future s handleError
|
||||
0x0062edcc method dart_Future sC Future_delayed
|
||||
0x006d5018 method dart_Future sC Future_microtask
|
||||
0x00943c08 method dart_Future sC Future_sync
|
||||
EOF
|
||||
RUN
|
||||
@@ -0,0 +1,26 @@
|
||||
NAME=versioned-code-cluster-layouts
|
||||
FILE=malloc://0x75
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
wx 50414421f5f5dcdc6d000000000000000300000000000000356239373239326232356630613731353631336237613238653037333466373770726f647563742061726d3634206e6f2d636f6d707265737365642d706f696e74657273008284818187ce8180d1818184878080808080808081002c82
|
||||
r2flutter -q -HHH -l 3~alloc=code:0
|
||||
r2flutter -q -HHH -l 3~bits=:0
|
||||
o malloc://0x76
|
||||
wx 50414421f5f5dcdc6e000000000000000300000000000000323464396434313163326639306338666265383930376639396538396434623070726f647563742061726d3634206e6f2d636f6d707265737365642d706f696e74657273008284828785e281c080ea818184878080808080808081002c82
|
||||
r2flutter -q -HHH -l 3~alloc=code:0
|
||||
r2flutter -q -HHH -l 3~bits=:0
|
||||
o malloc://0x75
|
||||
wx 50414421f5f5dcdc6d000000000000000300000000000000313434316436623133623836323366613766626636313433336162656264333170726f647563742061726d3634206e6f2d636f6d707265737365642d706f696e7465727300828482878589e281c080ea81818780808080808081002c82
|
||||
r2flutter -q -HHH -l 3~alloc=code:0
|
||||
r2flutter -q -HHH -l 3~bits=:0
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0 cid=14 alloc=code fill=code count=1 refs=3..4 flags=canonical alloc=0x63..0x65 fill=0x68..0x71 fill_status=ok main=1
|
||||
[0] pool_off=0xf pp_off=0x10 bits=0x00 type=tagged_object patch=patchable behavior=snapshotable stream=0x72 value=0x73 ref=300 resolved_kind=out_of_range
|
||||
0 cid=17 alloc=code fill=code count=1 refs=3..4 flags=- alloc=0x63..0x66 fill=0x69..0x72 fill_status=ok main=1
|
||||
[0] pool_off=0xf pp_off=0x10 bits=0x00 type=tagged_object patch=patchable behavior=snapshotable stream=0x73 value=0x74 ref=300 resolved_kind=out_of_range
|
||||
0 cid=17 alloc=code fill=code count=1 refs=3..4 flags=- alloc=0x64..0x67 fill=0x6a..0x71 fill_status=ok main=1
|
||||
[0] pool_off=0xf pp_off=0x10 bits=0x00 type=tagged_object patch=patchable behavior=snapshotable stream=0x72 value=0x73 ref=300 resolved_kind=out_of_range
|
||||
EOF
|
||||
RUN
|
||||
@@ -0,0 +1,21 @@
|
||||
NAME=discovery-stripped-ios-structural-instr
|
||||
FILE=bins/ios/Runner.app/Frameworks/App.framework/App
|
||||
CMDS=<<EOF
|
||||
s 0
|
||||
o: 0x321920
|
||||
wv4 0 @ 0x298
|
||||
wv4 0 @ 0x29c
|
||||
wv4 0 @ 0x2ac
|
||||
ooi
|
||||
is~?kDart
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -j -H~{=}vm_instr,iso_data,iso_instr
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0
|
||||
json.vm_instr = 16384;
|
||||
json.iso_data = 1807872;
|
||||
json.iso_instr = 59136;
|
||||
EOF
|
||||
RUN
|
||||
@@ -0,0 +1,30 @@
|
||||
NAME=dart-313-early-base-object-clusters
|
||||
FILE=malloc://0x8d
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
wx 50414421f5f5dcdc85000000000000000200000000000000303435313930376332656161383436376538343863303036376266653865643470726f647563742061726d3634206e6f2d636f6d707265737365642d706f696e746572730082858585890060c581800060c681820020ca810000cb810060c5818180828181c1bfbfbfc0c2c5c6c7c3818101810181
|
||||
r2flutter -q -j -HHH -l 8~{snapshots[0].clusters_parsed}
|
||||
r2flutter -q -j -HHH -l 8~{snapshots[0].clusters[0].fill_status}
|
||||
r2flutter -q -j -HHH -l 8~{snapshots[0].clusters[1].alloc}
|
||||
r2flutter -q -j -HHH -l 8~{snapshots[0].clusters[1].fill}
|
||||
r2flutter -q -j -HHH -l 8~{snapshots[0].clusters[1].fill_status}
|
||||
r2flutter -q -j -HHH -l 8~{snapshots[0].clusters[2].fill_status}
|
||||
r2flutter -q -j -HHH -l 8~{snapshots[0].clusters[3].fill_status}
|
||||
r2flutter -q -j -HHH -l 8~{snapshots[0].clusters[4].fill_status}
|
||||
r2flutter -q -j -p~{cluster_index}
|
||||
r2flutter -q -j -O PP+0x10~{pp_slot.cluster_index}
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
true
|
||||
ok
|
||||
variable
|
||||
local_var_descriptors
|
||||
ok
|
||||
ok
|
||||
ok
|
||||
ok
|
||||
4
|
||||
4
|
||||
EOF
|
||||
RUN
|
||||
@@ -0,0 +1,44 @@
|
||||
NAME=entrypoint-ios
|
||||
FILE=bins/ios/Runner.app/Frameworks/App.framework/App
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -qE
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0xe768
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=json-entrypoint-ios
|
||||
FILE=bins/ios/Runner.app/Frameworks/App.framework/App
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -jE
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
{"entrypoint":59240,"index":0,"code_index":0,"kind":"code","name":"method.PointerEvent.get:radiusMinor","vm_instr":16384,"vm_instr_size":42736,"iso_instr":59136,"iso_instr_size":1712608}
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=r2-entrypoint-ios
|
||||
FILE=bins/ios/Runner.app/Frameworks/App.framework/App
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -rE
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
# Dart AOT entrypoint and instruction snapshot data
|
||||
fs dart
|
||||
f dart.entrypoint = 0xe768
|
||||
CC Dart code entrypoint it[0] method.PointerEvent.get:radiusMinor @ 0xe768
|
||||
f dart.vm_instr = 0x4000
|
||||
Cd 4 10684 @ 0x4000
|
||||
CC Dart vm_instr snapshot instructions dword array size=0xa6f0 @ 0x4000
|
||||
f dart.iso_instr = 0xe700
|
||||
Cd 4 428152 @ 0xe700
|
||||
CC Dart iso_instr snapshot instructions dword array size=0x1a21e0 @ 0xe700
|
||||
EOF
|
||||
RUN
|
||||
@@ -1,7 +1,9 @@
|
||||
NAME=text-fields-synthetic
|
||||
FILE=-
|
||||
FILE=bins/synthetic/field_snapshot.bin
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -c bins/synthetic/field_snapshot.bin 2>/dev/null
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -c
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
class TestClass implements TestInterface (size=48)
|
||||
@@ -20,9 +22,11 @@ EOF
|
||||
RUN
|
||||
|
||||
NAME=json-fields-synthetic
|
||||
FILE=-
|
||||
FILE=bins/synthetic/field_snapshot.bin
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -j -c bins/synthetic/field_snapshot.bin 2>/dev/null
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -j -c
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
[{"ref":12,"name":"TestClass","library":{"ref":11,"name":"package:test"},"interfaces":[{"ref":23,"class_ref":13,"name":"TestInterface"}],"layout":{"instance_size":48,"type_params":1,"type_arg_offset":0},"flags":{"abstract":false,"enum":false,"mixin":false,"toplevel":false},"fields":[{"name":"fieldA","type":"int","offset":16,"flags":{"static":false,"final":false,"const":false,"late":false}},{"name":"items","type":"List<int>","offset":24,"flags":{"static":false,"final":false,"const":false,"late":false}},{"name":"genericItems","type":"List<X0>","offset":32,"flags":{"static":false,"final":false,"const":false,"late":false}},{"name":"callback","type":"(int) => String","offset":40,"flags":{"static":false,"final":false,"const":false,"late":false}}],"methods":[{"name":"compute","entry":0,"owner":"TestClass","signature":"(int) => String","kind_tag":0,"kind":"RegularFunction"}]},{"ref":13,"name":"TestInterface","library":{"ref":11,"name":"package:test"},"layout":{"instance_size":0,"type_params":0,"type_arg_offset":0},"flags":{"abstract":false,"enum":false,"mixin":false,"toplevel":false}}]
|
||||
@@ -30,9 +34,11 @@ EOF
|
||||
RUN
|
||||
|
||||
NAME=strings-fields-synthetic
|
||||
FILE=-
|
||||
FILE=bins/synthetic/field_snapshot.bin
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -zz bins/synthetic/field_snapshot.bin 2>/dev/null
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -zz
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0x00000018 74 :app "00000000000000000000000000000000product arm64 macos no-compressed-pointers"
|
||||
@@ -49,9 +55,11 @@ EOF
|
||||
RUN
|
||||
|
||||
NAME=json-strings-fields-synthetic
|
||||
FILE=-
|
||||
FILE=bins/synthetic/field_snapshot.bin
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -j -xzz bins/synthetic/field_snapshot.bin 2>/dev/null
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -j -xzz
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
[{"ref":0,"len":74,"value":"00000000000000000000000000000000product arm64 macos no-compressed-pointers","category":"app","two_byte":false,"canonical":true,"addr":24},{"ref":1,"len":9,"value":"TestClass","category":"lib","two_byte":false,"canonical":true,"addr":110,"refs":[{"obj":12,"type":"class","kind":"class.name","name":"TestClass"}]},{"ref":2,"len":12,"value":"package:test","category":"lib","two_byte":false,"canonical":true,"addr":120,"refs":[{"obj":11,"type":"library","kind":"library.name","name":"package:test"}]},{"ref":3,"len":6,"value":"fieldA","category":"lib","two_byte":false,"canonical":true,"addr":133,"refs":[{"obj":25,"type":"field","kind":"field.name","name":"TestClass.fieldA"}]},{"ref":4,"len":5,"value":"items","category":"lib","two_byte":false,"canonical":true,"addr":144,"refs":[{"obj":26,"type":"field","kind":"field.name","name":"TestClass.items"}]},{"ref":5,"len":12,"value":"genericItems","category":"lib","two_byte":false,"canonical":true,"addr":150,"refs":[{"obj":27,"type":"field","kind":"field.name","name":"TestClass.genericItems"}]},{"ref":6,"len":8,"value":"callback","category":"lib","two_byte":false,"canonical":true,"addr":163,"refs":[{"obj":28,"type":"field","kind":"field.name","name":"TestClass.callback"}]},{"ref":7,"len":7,"value":"compute","category":"lib","two_byte":false,"canonical":true,"addr":172,"refs":[{"obj":29,"type":"function","kind":"method.name","name":"TestClass.compute"}]},{"ref":8,"len":13,"value":"TestInterface","category":"lib","two_byte":false,"canonical":true,"addr":180,"refs":[{"obj":12,"type":"class","kind":"class.interface","name":"TestClass"},{"obj":13,"type":"class","kind":"class.name","name":"TestInterface"}]}]
|
||||
@@ -59,9 +67,11 @@ EOF
|
||||
RUN
|
||||
|
||||
NAME=xrefs-fields-synthetic
|
||||
FILE=-
|
||||
FILE=bins/synthetic/field_snapshot.bin
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -x bins/synthetic/field_snapshot.bin 2>/dev/null
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -x
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
metadata class.name class TestClass [ref=12] -> string TestClass [ref=2] @ 0x6e
|
||||
@@ -88,46 +98,64 @@ EOF
|
||||
RUN
|
||||
|
||||
NAME=r2-fields-synthetic-class-metadata
|
||||
FILE=-
|
||||
FILE=bins/synthetic/field_snapshot.bin
|
||||
ARGS=-NN
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -r -c bins/synthetic/field_snapshot.bin 2>/dev/null | grep -E '^(# Dart class symbols|ic\+dart_TestClass @ 0|ic\+dart_TestInterface @ 0|ic\+dart_TestClass:dart_TestInterface|ac dart_TestClass$|acb dart_TestClass dart_TestInterface 0|ic\+dart_TestClass\.\.callback void \* @ 0x28|ic\+dart_TestClass.compute @ 0x0|# field dart_TestClass.callback|# method dart_TestClass.compute)'
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -C
|
||||
e bin.lang
|
||||
ic
|
||||
t dart_TestClass
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
# Dart class symbols and analysis metadata
|
||||
ic+dart_TestClass @ 0
|
||||
ac dart_TestClass
|
||||
ic+dart_TestInterface @ 0
|
||||
ic+dart_TestClass:dart_TestInterface
|
||||
acb dart_TestClass dart_TestInterface 0
|
||||
# field dart_TestClass.callback +0x28 type=(int) => String instance
|
||||
ic+dart_TestClass..callback void * @ 0x28
|
||||
ic+dart_TestClass.compute @ 0x0
|
||||
# method dart_TestClass.compute kind=RegularFunction mode=instance cc=dyncc:x0+8,^:x0!T0 signature=(int) => String
|
||||
ic+dart_TestInterface @ 0
|
||||
dart
|
||||
0x00000000 [0x00000000 - 0x00000000] 0 dart class 0 dart_TestClass :: dart_TestInterface
|
||||
0x00000000 dart method 0 compute
|
||||
0x00000010 dart field 0 fieldA
|
||||
0x00000018 dart field 1 items
|
||||
0x00000020 dart field 2 genericItems
|
||||
0x00000028 dart field 3 callback
|
||||
0x00000000 [0x00000000 - 0x00000000] 0 dart class 1 dart_TestInterface
|
||||
'pf [16].qppp fieldA items genericItems callback
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=r2-fields-synthetic-safe-type-script
|
||||
FILE=-
|
||||
FILE=bins/synthetic/field_snapshot.bin
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -r -c bins/synthetic/field_snapshot.bin 2>/dev/null | awk 'BEGIN { td = 0; bad = 0 } /^"td struct dart_TestClass \{ uint8_t _pad0\[0x10\]; int64_t fieldA; void \* items; void \* genericItems; void \* callback; \};"$/ { td = 1 } /^af @/ || /^afc / || /td struct\.dart/ || /\(int\) => String callback @/ { bad = 1 } END { print "td=" td; print "bad=" bad }'
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -r -c~?td struct dart_TestClass
|
||||
r2flutter -r -c~?^af
|
||||
r2flutter -r -c~?struct.dart
|
||||
r2flutter -r -c~?callback
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
td=1
|
||||
bad=0
|
||||
1
|
||||
0
|
||||
0
|
||||
3
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=r2-plugin-C-fields-synthetic
|
||||
FILE=-
|
||||
FILE=bins/synthetic/field_snapshot.bin
|
||||
CMDS=<<EOF
|
||||
!!sh -c "p=../src/r2/core_flutter.dylib; [ -f \"\$p\" ] || p=../src/r2/core_flutter.so; r2 -NN -q -i \"\$p\" -qc \"r2flutter -C\" -qc ic -qc \"t dart_TestClass\" bins/synthetic/field_snapshot.bin 2>/dev/null" | awk '/class .* dart_TestClass( :: dart_TestInterface)?$/ { cls = 1 } /class .* dart_TestClass :: dart_TestInterface$/ { super = 1 } /method.* compute$/ { method = 1 } /field.* fieldA$/ { field = 1 } /^pf \[16\]\.qppp fieldA items genericItems callback$/ { type = 1 } END { print "class=" cls + 0; print "super=" super + 0; print "method=" method + 0; print "field=" field + 0; print "type=" type + 0 }'
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -C
|
||||
ic~?&class,dart_TestClass
|
||||
ic~?&dart_TestClass,dart_TestInterface
|
||||
ic~?&method,compute
|
||||
ic~?&field,fieldA
|
||||
t dart_TestClass~?fieldA items genericItems callback
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
class=1
|
||||
super=1
|
||||
method=1
|
||||
field=1
|
||||
type=1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
EOF
|
||||
RUN
|
||||
@@ -1,7 +1,9 @@
|
||||
NAME=text-funcs-android
|
||||
FILE=-
|
||||
FILE=bins/android/first/libapp.so
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -f -l 3 bins/android/first 2>/dev/null
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -f -l 3
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0x66768 Object.noSuchMethod
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
NAME=text-funcs-authpass
|
||||
FILE=-
|
||||
FILE=bins/ios/AuthPass.app/Frameworks/App.framework/App
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -f -l 3 bins/ios/AuthPass.app
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -f -l 3
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0xfe28 func.0000fe28
|
||||
0xfed4 func.0000fed4
|
||||
0xff50 func.0000ff50
|
||||
0xfe28 method._HashMap_3220832.addAll
|
||||
0xfed4 method._HashMap_3220832.dyn:forEach
|
||||
0xff50 method.MapBase.dyn:map
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=json-funcs-authpass
|
||||
FILE=-
|
||||
FILE=bins/ios/AuthPass.app/Frameworks/App.framework/App
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -j -f -l 3 bins/ios/AuthPass.app
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -j -f -l 3
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
[{"addr":65064,"name":"func.0000fe28"},{"addr":65236,"name":"func.0000fed4"},{"addr":65360,"name":"func.0000ff50"}]
|
||||
[{"addr":65064,"name":"method._HashMap_3220832.addAll","signature":"dynamic(_HashMap@3220832)"},{"addr":65236,"name":"method._HashMap_3220832.dyn:forEach","signature":"dynamic(_HashMap@3220832,dynamic)"},{"addr":65360,"name":"method.MapBase.dyn:map","signature":"dynamic(MapBase,dynamic)"}]
|
||||
EOF
|
||||
RUN
|
||||
@@ -0,0 +1,14 @@
|
||||
NAME=text-funcs-authpass-semantic
|
||||
FILE=bins/ios/AuthPass.app/Frameworks/App.framework/App
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -q -f~_secureHandshake_,registerBadCertificateCallback,_registerBadCertificateCallback_,_onBadCertificateWrapper_
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0x31749c method._RawSecureSocket_14069316._secureHandshake_14069316
|
||||
0x3194ec method._SecureFilterImpl_14069316.registerBadCertificateCallback
|
||||
0x31954c method._SecureFilterImpl_14069316._registerBadCertificateCallback_14069316
|
||||
0x3199d4 method._RawSecureSocket_14069316._onBadCertificateWrapper_14069316
|
||||
EOF
|
||||
RUN
|
||||
+11
-7
@@ -1,21 +1,25 @@
|
||||
NAME=json-funcs-ios
|
||||
FILE=-
|
||||
FILE=bins/ios/Runner.app/Frameworks/App.framework/App
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -j -f -l 3 bins/ios/Runner.app 2>/dev/null
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -j -f -l 3
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
[{"addr":59240,"name":"func.0000e768"},{"addr":59248,"name":"func.0000e770"},{"addr":59296,"name":"func.0000e7a0"}]
|
||||
[{"addr":59240,"name":"method.PointerEvent.get:radiusMinor","signature":"dynamic(PointerEvent)"},{"addr":59248,"name":"func.0000e770"},{"addr":59296,"name":"method.PointerEvent.get:distance","signature":"dynamic(PointerEvent)"}]
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=r2-funcs-ios
|
||||
FILE=-
|
||||
FILE=bins/ios/Runner.app/Frameworks/App.framework/App
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -r -f -l 2 bins/ios/Runner.app 2>/dev/null
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -r -f -l 2
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
f method.func.0000e768 = 0xe768
|
||||
'@0xe768'CC func.0000e768
|
||||
f method.PointerEvent.get:radiusMinor = 0xe768
|
||||
'@0xe768'CC method.PointerEvent.get:radiusMinor
|
||||
f method.func.0000e770 = 0xe770
|
||||
'@0xe770'CC func.0000e770
|
||||
EOF
|
||||
|
||||
+33
-17
@@ -1,7 +1,9 @@
|
||||
NAME=text-funcs-mafia
|
||||
FILE=-
|
||||
FILE=bins/android/mafia/libapp.so
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -f -l 3 bins/android/mafia 2>/dev/null
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -f -l 3
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0x5b69c0 method.DateTime.compareTo
|
||||
@@ -11,9 +13,11 @@ EOF
|
||||
RUN
|
||||
|
||||
NAME=r2-funcs-mafia
|
||||
FILE=-
|
||||
FILE=bins/android/mafia/libapp.so
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -r -f -l 2 bins/android/mafia 2>/dev/null
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -r -f -l 2
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
f method.DateTime.compareTo = 0x5b69c0
|
||||
@@ -24,9 +28,11 @@ EOF
|
||||
RUN
|
||||
|
||||
NAME=quiet-r2-funcs-mafia
|
||||
FILE=-
|
||||
FILE=bins/android/mafia/libapp.so
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -qrf -l 2 bins/android/mafia 2>/dev/null
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -qrf -l 2
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
f method.DateTime.compareTo = 0x5b69c0
|
||||
@@ -35,12 +41,14 @@ EOF
|
||||
RUN
|
||||
|
||||
NAME=json-funcs-mafia
|
||||
FILE=-
|
||||
FILE=bins/android/mafia/libapp.so
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -j -f -l 3 bins/android/mafia 2>/dev/null
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -j -f -l 3
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
[{"addr":5990848,"name":"method.DateTime.compareTo"},{"addr":5991076,"name":"method.Duration.compareTo"},{"addr":5991268,"name":"method.Duration.dyn:_"}]
|
||||
[{"addr":5990848,"name":"method.DateTime.compareTo","signature":"dynamic(DateTime)"},{"addr":5991076,"name":"method.Duration.compareTo","signature":"dynamic(Duration)"},{"addr":5991268,"name":"method.Duration.dyn:_","signature":"dynamic(Duration,dynamic)"}]
|
||||
EOF
|
||||
RUN
|
||||
|
||||
@@ -48,9 +56,11 @@ RUN
|
||||
# stub.Allocate<ClassName>Stub, not shifted into a neighboring method name by
|
||||
# the name-pool fallback. 0x5b6bf4 is the AllocateDurationStub in this fixture.
|
||||
NAME=funcs-mafia-allocate-stub
|
||||
FILE=-
|
||||
FILE=bins/android/mafia/libapp.so
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -f -q bins/android/mafia 2>/dev/null | grep -E '^0x5b6bf4 '
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -f -q~0x5b6bf4
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0x5b6bf4 stub.AllocateDurationStub
|
||||
@@ -60,9 +70,11 @@ RUN
|
||||
# The same address must NOT appear under any method.fn_* fallback name, which
|
||||
# was the bug before the Code-owner-cid fix.
|
||||
NAME=funcs-mafia-allocate-stub-not-fallback
|
||||
FILE=-
|
||||
FILE=bins/android/mafia/libapp.so
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -f -q bins/android/mafia 2>/dev/null | grep -c '^0x5b6bf4 method.fn_'
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -f -q~?&0x5b6bf4,method.fn_
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0
|
||||
@@ -72,9 +84,11 @@ RUN
|
||||
# Type-test stubs (Code owner is an AbstractType) must be synthesized as
|
||||
# stub.typetest_* rather than pulling from the name pool.
|
||||
NAME=funcs-mafia-typetest-stub-present
|
||||
FILE=-
|
||||
FILE=bins/android/mafia/libapp.so
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -f -q bins/android/mafia 2>/dev/null | grep -c ' stub.typetest_'
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -f -q~?stub.typetest_
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
1277
|
||||
@@ -85,9 +99,11 @@ RUN
|
||||
# the count is 4096; if it drops noticeably (e.g. back to zero) the owner-cid
|
||||
# lookup has regressed.
|
||||
NAME=funcs-mafia-allocate-stub-count
|
||||
FILE=-
|
||||
FILE=bins/android/mafia/libapp.so
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -f -q bins/android/mafia 2>/dev/null | grep -c ' stub.Allocate'
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -f -q~?stub.Allocate
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
4096
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
NAME=text-funcs-mafia-closure
|
||||
FILE=-
|
||||
FILE=bins/android/mafia/libapp.so
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -f -q bins/android/mafia 2>/dev/null | grep -E '0x5b72b0|0x5b74cc'
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -f -q~0x5b72b0,0x5b74cc
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0x5b72b0 method.FlutterError._anon_closure
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
NAME=funcs-mafia-name-pool-window
|
||||
FILE=-
|
||||
FILE=bins/android/mafia/libapp.so
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -f -q -n -l 6 bins/android/mafia 2>/dev/null
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -f -q -n -l 6
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0x5b69c0 method.DateTime.compareTo
|
||||
@@ -14,15 +16,19 @@ EOF
|
||||
RUN
|
||||
|
||||
NAME=funcs-mafia-name-pool-stub-counts
|
||||
FILE=-
|
||||
FILE=bins/android/mafia/libapp.so
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -f -q -n bins/android/mafia 2>/dev/null | awk '$1=="0x5b6bf4" || $1=="0x5b6f6c" || $1=="0x5bb750" {print} / stub\.Allocate/ {alloc++} / stub\.typetest_/ {type++} END {print "alloc=" alloc; print "typetest=" type}'
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -f -q -n~0x5b6bf4,0x5b6f6c,0x5bb750
|
||||
r2flutter -f -q -n~?stub.Allocate
|
||||
r2flutter -f -q -n~?stub.typetest_
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0x5b6bf4 stub.AllocateDurationStub
|
||||
0x5b6f6c stub.typetest_13
|
||||
0x5bb750 stub.AllocateStringBufferStub
|
||||
alloc=4096
|
||||
typetest=1277
|
||||
4096
|
||||
1277
|
||||
EOF
|
||||
RUN
|
||||
@@ -1,7 +1,9 @@
|
||||
NAME=funcs-mafia-vm-symbol-operators
|
||||
FILE=-
|
||||
FILE=bins/android/mafia/libapp.so
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -f -q bins/android/mafia 2>/dev/null | awk '$1=="0xe11f60" || $1=="0xe1205c" || $1=="0xe12174" || $1=="0xe12220" || $1=="0xe122a8" {print} $1=="0xe12220" || $1=="0xe122a8" {names[$2]=1} END {n=0; for (name in names) {n++} print "unique_greaterthan=" n}'
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -f -q~0xe11f60,0xe1205c,0xe12174,0xe12220,0xe122a8
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0xe11f60 method._Double_0150898.op_add
|
||||
@@ -9,6 +11,5 @@ EXPECT=<<EOF
|
||||
0xe12174 method._Double_0150898.op_gt
|
||||
0xe12220 method._Double_0150898._greaterThan_0150898
|
||||
0xe122a8 method._IntegerImplementation_0150898.op_gt
|
||||
unique_greaterthan=2
|
||||
EOF
|
||||
RUN
|
||||
@@ -1,9 +1,11 @@
|
||||
NAME=json-header-android
|
||||
FILE=-
|
||||
FILE=bins/android/first/libapp.so
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -j -H bins/android/first 2>/dev/null
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -j -H
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
{"kind":3,"hash":"f91b8b03bf7f30a5e983fd19b23d978d","vm_data":512,"vm_instr":327680,"iso_data":36544,"iso_instr":419584,"cluster":{"base":1154,"objs":11342,"clusters":47,"it_len":1311,"it_off":64,"total":91657},"cws":8,"dart_version":"2.18.2","tag_style":"OBJECT_HEADER","alignment":16,"header_fields":5,"it_capacity":20000,"cid_table":{"cid_class":5,"cid_function":7,"cid_code":18,"cid_string":93,"cid_one_byte_string":94,"cid_two_byte_string":95,"cid_array":90,"cid_mint":61,"cid_object_pool":23,"num_predefined":175}}
|
||||
{"kind":3,"hash":"f91b8b03bf7f30a5e983fd19b23d978d","vm_data":512,"vm_instr":327680,"iso_data":36544,"iso_instr":419584,"cluster":{"base":1154,"objs":11342,"clusters":47,"it_len":1311,"it_off":64,"total":91657},"cws":8,"dart_version":"2.18.2","version_source":"exact-hash","tag_style":"OBJECT_HEADER","alignment":16,"header_fields":5,"it_capacity":20000,"cid_table":{"cid_class":5,"cid_function":7,"cid_code":18,"cid_string":93,"cid_one_byte_string":94,"cid_two_byte_string":95,"cid_array":90,"cid_mint":61,"cid_object_pool":23,"num_predefined":175}}
|
||||
EOF
|
||||
RUN
|
||||
@@ -1,9 +1,11 @@
|
||||
NAME=json-header-authpass
|
||||
FILE=-
|
||||
FILE=bins/ios/AuthPass.app/Frameworks/App.framework/App
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -j -H bins/ios/AuthPass.app
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -j -H
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
{"kind":3,"hash":"f71c76320d35b65f1164dbaa6d95fe09","vm_data":7333504,"vm_instr":23168,"iso_data":7368576,"iso_instr":64960,"cluster":{"base":1129,"objs":277373,"clusters":1044,"it_len":37092,"it_off":64,"total":3159106},"cws":8,"dart_version":"3.2.5","tag_style":"CID_SHIFT1","alignment":16,"header_fields":5,"it_capacity":20000,"cid_table":{"cid_class":5,"cid_function":7,"cid_code":18,"cid_string":92,"cid_one_byte_string":93,"cid_two_byte_string":94,"cid_array":89,"cid_mint":60,"cid_object_pool":22,"num_predefined":176}}
|
||||
{"kind":3,"hash":"f71c76320d35b65f1164dbaa6d95fe09","vm_data":7333504,"vm_instr":23168,"iso_data":7368576,"iso_instr":64960,"cluster":{"base":1129,"objs":277373,"clusters":1044,"it_len":37092,"it_off":64,"total":3159106},"cws":8,"dart_version":"3.2.5","version_source":"exact-hash","tag_style":"CID_SHIFT1","alignment":16,"header_fields":5,"it_capacity":20000,"cid_table":{"cid_class":5,"cid_function":7,"cid_code":18,"cid_string":92,"cid_one_byte_string":93,"cid_two_byte_string":94,"cid_array":89,"cid_mint":60,"cid_object_pool":22,"num_predefined":176}}
|
||||
EOF
|
||||
RUN
|
||||
@@ -1,12 +1,14 @@
|
||||
NAME=header-deep-macos
|
||||
FILE=-
|
||||
FILE=bins/macos/hello/hello.aot
|
||||
CMDS=<<EOF
|
||||
!!../bin/r2flutter -q -HHH -l 36 bins/macos/hello/hello.aot 2>/dev/null | grep -Eo '^( detail: object_pool_entries| 34 cid=23 alloc=object_pool fill=none count=1| object_pool_decode: fill_not_parsed)'
|
||||
L ../src/r2/core_flutter.dylib
|
||||
L ../src/r2/core_flutter.so
|
||||
r2flutter -q -HHH -l 36~object_pool_entries,34 cid=23,object_pool ref=9145
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
detail: object_pool_entries
|
||||
detail: object_pool_entries
|
||||
34 cid=23 alloc=object_pool fill=none count=1
|
||||
object_pool_decode: fill_not_parsed
|
||||
34 cid=23 alloc=object_pool fill=object_pool count=1 refs=9145..9146 flags=- alloc=0x5ee7b..0x5ee7e fill=0x6e81a..0x6f56e fill_status=ok items=count:1,first:1380,min:1380,max:1380,total:1380
|
||||
object_pool ref=9145 index=0 length=1380 stream=0x6e81a
|
||||
EOF
|
||||
RUN
|
||||
Loaded 100 of 164 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user