Repository navigation
Comprehensive Decompiler, Memory & IL Restoration Improvements for Unity Android IL2CPP - #623
Open
tranthanhbinh2603 wants to merge 10 commits into
Open
tranthanhbinh2603 wants to merge 10 commits into
tranthanhbinh2603 wants to merge 10 commits into
Conversation
…ndidate in ambiguous call resolution - In ResolveAmbiguousCalls, when multiple candidates exist on a type, filter by caller method name to better handle recursive calls and base method overrides. - Use FirstOrDefault instead of breaking early with SingleOrDefault to avoid leaving calls unresolved when shared method bodies cannot be fully disambiguated.
…String to Int64 not supporter"
…compilation states
…sis and DLL output
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
1. Context & Motivation
When decompiling Unity Android applications (ARM64 ELF binaries, IL2CPP v24 through v31+), several systemic issues and architecture-specific lifting limitations cause decompilation degradation:
null: String constants referenced through Global Offset Table (GOT) slots or pointer indirection fail to resolve, causing decompiled code to passnullor unmanaged pointer math where meaningful string literals should be.MaxMethodSizeBytes.ptr = base + (index << shift)) for array and string indexing, which upstream fails to lift, producing raw unmanaged memory loads ([loc0 + 0x14]).Index was outside the bounds of the arrayorCast from String to Int64 not supported).if (x == null) throw), array bounds checks, and runtime class initialization guards clutter decompiled methods.This PR addresses these limitations with comprehensive algorithmic improvements across the entire pipeline while strictly preserving all upstream diagnostic warnings.
2. In-Depth Technical Breakdown of Changes
A. String Literal & Global Memory Resolution (
MetadataResolver.cs)null:.solibraries, string literals are frequently accessed indirectly via the Global Offset Table (GOT) or data segment pointer slots. In upstream,ResolveGlobalLoadsonly checked for Type, Method, and Field globals, completely missing string literals. Furthermore,LiteralSlotAddressonly recognized directImmediateoperands. As a result, string loads could not be resolved, leaving dereferences as unresolved pointers that degraded intonullin the final decompiled C# code.ResolveGlobalLoadsandResolveLiteralString. If an address does not directly match a global or literal, the virtual address is mapped and dereferenced (libContext.Binary.ReadPointerAtVirtualAddress(address)) to follow GOT/relocation slots.libContext.GetLiteralByAddress(slotAddress)for destinations that can hold a string or object.dest = src), substituting memory dereferences ([slotLocal]) withStringLiteraland strongly typing the target local asSystem.String.LiteralSlotAddressto handleMemoryOperand { Base: null, Index: null, Scale: 0 }, correctly matching ARM64ADRP+LDRsequences.B. Pointer Derivation & High-Level Array/String Restoration (
ArrayRecovery.cs,IlGenerator.cs)ptr = array + 0x20, orptr = array + (index << 3)). Upstream only recognizedMemoryOperandwherememory.Basewas directly the array local. Any derived pointer was left as unmanaged memory math.arrayPointersto derive(Array, ArrayType, Offset)andindexedArrayPointersto track base arrays with variable indices and shift scales (scale = 1 << shiftImm).ArrayAccess(array, index)andArrayLength(array).Nop.visitedBlocksHashSet) inRecoverAllocationto prevent infinite loops on cyclic control-flow graphs.callvirt System.String.get_Lengthwhen loading offset0x10from a string local.callvirt System.String.get_Chars(Int32)when loading offset0x14with an index from a string local.C. Large Method Decompilation & Graph Guard (
MethodAnalysisContext.cs)MaxMethodSizeBytes = 30000limit (MaxMethodSizeBytes = -1) so large methods are decompiled rather than stripped into empty stubs.blockCount > 25000) to safely stub them and prevent process termination.GC.Collect(2, ...)) after analyzing methods with >1,000 blocks to bound working memory.D. Fast$O(N)$ Iterative Dominator Algorithm (
DominatorInfo.cs)HashSet<Block>intersections (Dominates(a, b)to walk immediate dominators directly.E. ARM64 Interface Dispatch & Key Function Lifting (
InterfaceDispatchRecovery.cs,KeyFunctionRecovery.cs,NewArmV8InstructionSet.cs)VirtualInvokeData::methodPtrvia memory dereference). On ARM64 (AAPCS64), dispatch targets are passed in registers (X0or copied from aPhi). AddedChaseCopiesto follow register moves, extract interface arguments and slot indices, and resolve virtual interface invocations back tocallvirt.Il2CppClass::vtableoffset (0x130).il2cpp_vm_object_boxinto ILbox <Type>.il2cpp_object_unbox/il2cpp_vm_object_unboxinto directMove.il2cpp_vm_object_is_inst(is/asoperator) into directMove.il2cpp_runtime_class_init...).Bbranch instructions with condition codeNONEorALas valid call/branch targets.Task.GetAwaiter,ExecutionContext,CancellationTokenSource).F. Generic Type Field Layouts (
GenericInstanceFieldLayout.cs)nullfor generic classes with non-trivial base types, causing field accesses on generic classes to emit raw memory offsets.ComputeBaseTypeOffsettaking into account object headers (2 * pointer size for reference types, 0 for value types), alignment constraints, and base type hierarchies (System.Object/System.ValueType).ResolveFieldTypeto substitute generic parameters with concrete type arguments.G. Injected Check & Dead Call Elimination (
InjectedCheckRemover.cs,CallArgumentTrimmer.cs)InjectedCheckRemoverto match zero constants on either side ofCheckEqual/CheckNotEqual, handle branch jumps to shared throw blocks (up to depth 2), and handleNewobjfollowed byThrow local.CallArgumentTrimmer, unresolved calls to immediate addresses (such as dangling write barriers) with unused return values are converted toNop, removing clutter.H. CIL Code Generation & Typing Fixes (
IlGenerator.cs)CheckEqual->bool, arithmetic ->int32,Call-> return type,Newobj/NewArr-> allocated type).GetOrCreateLocalto guarantee every referenced local has a definedCilLocalVariable, preventing AsmResolver out-of-bounds errors.ldc.r4,ldc.r8, orldc.i8based onexpectedType, resolving runtime exceptions when assigning constants to 64-bit integers or floats.retorthrow), preventing invalid PE verification failures.3. Before vs. After Decompilation Comparison
Example 1: String Literal Resolution (Fixed from
null)Example 2: String Indexing & Length
Example 3: Array Access with Pointer Arithmetic
Example 4: Large Methods (> 30 KB)
Example 5: Compiler-Injected Checks & Boilerplate
Example 6: Key Function & Object Boxing
4. Diagnostics & Console Cleanliness
Diagnostic(...)calls (Unmanaged memory load,Unknown operand,Store into unknown operand,Unknown instruction,Indirect call/jump,Phi opcodes,Stack shift, etc.) are 100% preserved. Any unresolved decompilation states remain visible to consumers via comments/warnings rather than being silently hidden.Logger.InfoNewlinecalls and heartbeat timers were eliminated. Console output remains clean and parallel decompiler performance is unimpeded by lock contention.5. Verification & Test Binaries
Cpp2IL.Core.TestsandLibCpp2ILTestspass successfully (total: 82, failed: 0, succeeded: 82).6. Maintainer Collaboration Note
I welcome your review and feedback! If there are specific design decisions you would like adjusted, or if you would prefer this PR partitioned into smaller, modular pull requests (e.g., String & Array Lifting, Dominator Optimization, or Build Configuration), please let me know. Feel free to request changes, suggest modifications, or branch this PR into a dedicated review branch on upstream if you prefer to tweak it before merging into
development.