[GH-ISSUE #8665] [Feature] IL2CPP binary_path: Unity WebGL wasm32 CodeGenModule → function index #7506

Open
opened 2026-11-03 00:30:21 +00:00 by bsakaguchi · 2 comments
Owner

Originally created by @Garrag on GitHub (Nov 2, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/8665

Summary

Stock REA can decode global-metadata.dat and, with --binary, link MethodDef rows to native code addresses for ELF / PE / thin Mach-O only (readBinaryContainer → unsupported-container for anything else).

Unity WebGL IL2CPP ships a .wasm GameAssembly. Method slots are not file VAs: on wasm32 they are 4-byte table indices into the module’s funcref table; call_indirect uses those indices. REA currently cannot emit method_addresses (or a wasm analogue) for that shape.

This is complementary to #2298 (generic wasm → labeled pseudocode). #2298 does not define IL2CPP CodeRegistration / Il2CppCodeGenModule linking. Linking without decompile still gives stable MethodDef → function index seeds; decompile without linking cannot name C# methods.

  • #2298 — Decompile WebAssembly functions into labeled pseudocode
  • docs: docs/il2cpp-metadata.md (binary_path whitelist), docs/wasm-artifacts.md (static WABT lane, 8 MiB input cap)

Observed hard limits (REA 6.8.0 / main)

  1. inspect-il2cpp-metadata … --binary game.wasm → unsupported-container: Only ELF, PE, and thin Mach-O native images are supported.
  2. Native open / analyze on wasm → wasm_requires_static_inspection.
  3. inspect_wasm_artifact refuses inputs > 8 MiB with no runtime override — typical Unity WebGL payloads are tens of MiB, so the WABT lane cannot even produce WAT for those modules.

Proposal

  1. Acknowledge wasm as an IL2CPP binary container (new format branch alongside elf/pe/macho), or a dedicated wasm32 image opener used only by the IL2CPP linker.
  2. Pointer semantics: 4-byte little-endian pointers into reconstructed linear memory (active data segments); do not reuse 8-byte VA / executable-mapping checks unchanged.
  3. Link algorithm (same ownership rules as today): locate Il2CppCodeGenModule by moduleName + methodPointerCount matching metadata image method counts; index methodPointers by MethodDef row (token & 0xffffff); treat nonzero slots as table indices; resolve through the module’s elem / table0 to a function index (report table index + function index in Evidence).
  4. Tokens: continue reading MethodDef tokens from metadata (rows are not guaranteed to equal sequential order within an image’s type ranges).
  5. Size limit: raise or make configurable the 8 MiB inspect_wasm_artifact cap for Unity WebGL-sized modules (separate from, but blocking for, end-to-end workflows).
  6. Non-goals for the first PR: full wasm pseudocode (stay on #2298 / Ghidra WASM); inventing ELF wrappers for wasm.

Local PoC (out of tree)

A local fork PoC reconstructed linear memory + wasm32 CodeGenModule scan + table0 resolve and stably mapped multiple gameplay Game.dll methods to function indices on one Unity 2022.3 WebGL build (metadata v31). Happy to trim that into a fixture-sized public sample if maintainers want a regression test shape — no proprietary game bytes in this issue.

Ask

  • Scope feedback: is a wasm32 IL2CPP link backend in-scope for inspect-il2cpp-metadata --binary, or should it be a sibling operation?
  • Preferred Evidence field names for table_index vs function_index vs today’s method_addresses VA list.
  • Whether bumping the WABT input ceiling can ship in the same track or needs its own issue.
Originally created by @Garrag on GitHub (Nov 2, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/8665 ### Summary Stock REA can decode `global-metadata.dat` and, with `--binary`, link MethodDef rows to **native** code addresses for ELF / PE / thin Mach-O only (`readBinaryContainer` → `unsupported-container` for anything else). Unity **WebGL IL2CPP** ships a `.wasm` GameAssembly. Method slots are **not** file VAs: on wasm32 they are **4-byte table indices** into the module’s funcref table; `call_indirect` uses those indices. REA currently cannot emit `method_addresses` (or a wasm analogue) for that shape. This is **complementary** to #2298 (generic wasm → labeled pseudocode). #2298 does not define IL2CPP `CodeRegistration` / `Il2CppCodeGenModule` linking. Linking without decompile still gives stable `MethodDef → function index` seeds; decompile without linking cannot name C# methods. ### Related - #2298 — Decompile WebAssembly functions into labeled pseudocode - docs: `docs/il2cpp-metadata.md` (`binary_path` whitelist), `docs/wasm-artifacts.md` (static WABT lane, **8 MiB** input cap) ### Observed hard limits (REA 6.8.0 / main) 1. `inspect-il2cpp-metadata … --binary game.wasm` → `unsupported-container: Only ELF, PE, and thin Mach-O native images are supported`. 2. Native open / `analyze` on wasm → `wasm_requires_static_inspection`. 3. `inspect_wasm_artifact` refuses inputs **> 8 MiB** with no runtime override — typical Unity WebGL payloads are tens of MiB, so the WABT lane cannot even produce WAT for those modules. ### Proposal 1. **Acknowledge wasm as an IL2CPP binary container** (new format branch alongside elf/pe/macho), or a dedicated wasm32 image opener used only by the IL2CPP linker. 2. **Pointer semantics:** 4-byte little-endian pointers into reconstructed linear memory (active `data` segments); do not reuse 8-byte VA / executable-mapping checks unchanged. 3. **Link algorithm (same ownership rules as today):** locate `Il2CppCodeGenModule` by `moduleName` + `methodPointerCount` matching metadata image method counts; index `methodPointers` by MethodDef row (`token & 0xffffff`); treat nonzero slots as **table indices**; resolve through the module’s **elem / table0** to a **function index** (report table index + function index in Evidence). 4. **Tokens:** continue reading MethodDef tokens from metadata (rows are not guaranteed to equal sequential order within an image’s type ranges). 5. **Size limit:** raise or make configurable the 8 MiB `inspect_wasm_artifact` cap for Unity WebGL-sized modules (separate from, but blocking for, end-to-end workflows). 6. **Non-goals for the first PR:** full wasm pseudocode (stay on #2298 / Ghidra WASM); inventing ELF wrappers for wasm. ### Local PoC (out of tree) A local fork PoC reconstructed linear memory + wasm32 `CodeGenModule` scan + table0 resolve and stably mapped multiple gameplay `Game.dll` methods to function indices on one Unity 2022.3 WebGL build (metadata v31). Happy to trim that into a fixture-sized public sample if maintainers want a regression test shape — **no proprietary game bytes in this issue**. ### Ask - Scope feedback: is a wasm32 IL2CPP link backend in-scope for `inspect-il2cpp-metadata --binary`, or should it be a sibling operation? - Preferred Evidence field names for `table_index` vs `function_index` vs today’s `method_addresses` VA list. - Whether bumping the WABT input ceiling can ship in the same track or needs its own issue.
Author
Owner

@8rulerstar commented on GitHub (Nov 3, 2026):

Thanks for writing this up so clearly, and for the PoC.

I have an unmerged branch (no PR) taking the same approach: reconstructed linear memory from active data segments, the 4-byte Il2CppCodeGenModule layout matched by moduleName and methodPointerCount, and table0 slot to function index. On two uncompressed WebGL builds I made myself with Unity 2022.3.62f3 (metadata v31), the linked rows matched Il2CppDumper by slot and name: 9,557/9,557 (release) and 9,643/9,643 (development). In the development build, the name section name of each of the 9,643 function indices also started with the expected IL2CPP method name. Generic method instances were out of scope, and the port plans to refuse --generic-instances for wasm. I started porting it to current main, but will hold the wasm part until we agree on a split.

To avoid duplicate work, how would you like to split this? For example, if you prefer, your PR goes first and I provide the samples and cross-check numbers; or I open the PR and ask you to verify it on your build. Either is fine with me.

On your three questions, just the facts from my side, since the decisions are the maintainers':

  • Scope: my branch keeps it inside inspect-il2cpp-metadata --binary.
  • Fields: Il2CppDumper reports the table slot as Address. The prototype did the same, but since address elsewhere means a mapped virtual address, the port currently plans to leave address/rva null for wasm rows and add table_index and function_index, as you suggested. Happy to follow whatever names the maintainers prefer.
  • WABT cap: linking reads the module sections directly and does not use WABT, so it works on my 9.0 MB and 15.8 MB modules regardless of the 8 MiB cap. The cap itself is untouched.
<!-- gh-comment-id:6592331451 --> @8rulerstar commented on GitHub (Nov 3, 2026): Thanks for writing this up so clearly, and for the PoC. I have an unmerged branch (no PR) taking the same approach: reconstructed linear memory from active data segments, the 4-byte `Il2CppCodeGenModule` layout matched by `moduleName` and `methodPointerCount`, and table0 slot to function index. On two uncompressed WebGL builds I made myself with Unity 2022.3.62f3 (metadata v31), the linked rows matched Il2CppDumper by slot and name: 9,557/9,557 (release) and 9,643/9,643 (development). In the development build, the name section name of each of the 9,643 function indices also started with the expected IL2CPP method name. Generic method instances were out of scope, and the port plans to refuse `--generic-instances` for wasm. I started porting it to current main, but will hold the wasm part until we agree on a split. To avoid duplicate work, how would you like to split this? For example, if you prefer, your PR goes first and I provide the samples and cross-check numbers; or I open the PR and ask you to verify it on your build. Either is fine with me. On your three questions, just the facts from my side, since the decisions are the maintainers': - Scope: my branch keeps it inside `inspect-il2cpp-metadata --binary`. - Fields: Il2CppDumper reports the table slot as `Address`. The prototype did the same, but since `address` elsewhere means a mapped virtual address, the port currently plans to leave `address`/`rva` null for wasm rows and add `table_index` and `function_index`, as you suggested. Happy to follow whatever names the maintainers prefer. - WABT cap: linking reads the module sections directly and does not use WABT, so it works on my 9.0 MB and 15.8 MB modules regardless of the 8 MiB cap. The cap itself is untouched.
Author
Owner

@8rulerstar commented on GitHub (Nov 3, 2026):

I opened the port as a draft, #8720, so the approach and the proposed table_index/function_index row shape can be looked at in code. It is only for discussion; happy to change the split, the names, or close it in favour of your PR.

<!-- gh-comment-id:6594935637 --> @8rulerstar commented on GitHub (Nov 3, 2026): I opened the port as a draft, #8720, so the approach and the proposed `table_index`/`function_index` row shape can be looked at in code. It is only for discussion; happy to change the split, the names, or close it in favour of your PR.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#7506
No description provided.