public static void DoPass(RewriteGlobalContext context, UnhollowerOptions options) { if (string.IsNullOrEmpty(options.GameAssemblyPath)) { Pass15GenerateMemberContexts.HasObfuscatedMethods = false; return; } using var mappedFile = MemoryMappedFile.CreateFromFile(options.GameAssemblyPath, FileMode.Open, null, 0, MemoryMappedFileAccess.Read); using var accessor = mappedFile.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read); IntPtr gameAssemblyPtr; unsafe { byte *fileStartPtr = null; accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref fileStartPtr); gameAssemblyPtr = (IntPtr)fileStartPtr; } context.HasGcWbarrierFieldWrite = FindByteSequence(gameAssemblyPtr, accessor.Capacity, nameof(IL2CPP.il2cpp_gc_wbarrier_set_field)); if (!Pass15GenerateMemberContexts.HasObfuscatedMethods) { return; } var methodToCallersMap = new ConcurrentDictionary <long, List <XrefInstance> >(); var methodToCalleesMap = new ConcurrentDictionary <long, List <long> >(); context.MethodStartAddresses.Sort(); // Scan xrefs context.Assemblies.SelectMany(it => it.Types).SelectMany(it => it.Methods).AsParallel().ForAll( originalTypeMethod => { var address = originalTypeMethod.FileOffset; if (address == 0) { return; } if (!options.NoXrefCache) { var pair = XrefScanMetadataGenerationUtil.FindMetadataInitForMethod(originalTypeMethod, (long)gameAssemblyPtr); originalTypeMethod.MetadataInitFlagRva = pair.FlagRva; originalTypeMethod.MetadataInitTokenRva = pair.TokenRva; } var nextMethodStart = context.MethodStartAddresses.BinarySearch(address + 1); if (nextMethodStart < 0) { nextMethodStart = ~nextMethodStart; } var length = nextMethodStart >= context.MethodStartAddresses.Count ? 1024 * 1024 : (context.MethodStartAddresses[nextMethodStart] - address); foreach (var callTargetGlobal in XrefScanner.XrefScanImpl(XrefScanner.DecoderForAddress(IntPtr.Add(gameAssemblyPtr, (int)address), (int)length), true)) { var callTarget = callTargetGlobal.RelativeToBase((long)gameAssemblyPtr + originalTypeMethod.FileOffset - originalTypeMethod.Rva); if (callTarget.Type == XrefType.Method) { var targetRelative = (long)callTarget.Pointer; methodToCallersMap.GetOrAdd(targetRelative, _ => new List <XrefInstance>()).AddLocked(new XrefInstance(XrefType.Method, (IntPtr)originalTypeMethod.Rva, callTarget.FoundAt)); methodToCalleesMap.GetOrAdd(originalTypeMethod.Rva, _ => new List <long>()).AddLocked(targetRelative); } if (!options.NoXrefCache) { originalTypeMethod.XrefScanResults.Add(callTarget); } } }); MapOfCallers = methodToCallersMap; void MarkMethodAlive(long address) { if (!NonDeadMethods.Add(address)) { return; } if (!methodToCalleesMap.TryGetValue(address, out var calleeList)) { return; } foreach (var callee in calleeList) { MarkMethodAlive(callee); } } // Now decided which of them are possible dead code foreach (var assemblyRewriteContext in context.Assemblies) { foreach (var typeRewriteContext in assemblyRewriteContext.Types) { foreach (var methodRewriteContext in typeRewriteContext.Methods) { if (methodRewriteContext.FileOffset == 0) { continue; } var originalMethod = methodRewriteContext.OriginalMethod; if (!originalMethod.Name.IsObfuscated(options) || originalMethod.IsVirtual) { MarkMethodAlive(methodRewriteContext.Rva); } } } } }