List <TypeMapModule> LoadMapModules(string filePath) { ulong moduleCount = (ulong)ELF.GetUInt32(ModuleCountSymbolName); // MUST be kept in sync with: src/monodroid/jni/xamarin-app.hh (struct TypeMapModule) ulong size; size = 16; // module_uuid size += GetPaddedSize <uint> (size); // entry_count size += GetPaddedSize <uint> (size); // duplicate_count size += GetPaddedSize <string> (size); // map (pointer) size += GetPaddedSize <string> (size); // duplicate_map (pointer) size += GetPaddedSize <string> (size); // assembly_name (pointer) size += GetPaddedSize <string> (size); // image (pointer) size += GetPaddedSize <uint> (size); // java_name_width size += GetPaddedSize <string> (size); // java_map (pointer) byte[] moduleData = ELF.GetData(MapModulesSymbolName); if (moduleData.Length == 0) { throw new InvalidOperationException($"{filePath} doesn't have a valid '{MapModulesSymbolName}' symbol"); } ulong calculatedModuleCount = (ulong)moduleData.Length / size; if (calculatedModuleCount != moduleCount) { throw new InvalidOperationException($"{filePath} has invalid '{ModuleCountSymbolName}' symbol value ({moduleCount}), '{MapModulesSymbolName}' size indicates there are {calculatedModuleCount} managedToJava instead"); } var ret = new List <TypeMapModule> (); ulong offset = 0; for (ulong i = 0; i < moduleCount; i++) { Log.Debug($"Module {i + 1}"); var module = new TypeMapModule(); byte[] mvid = new byte[16]; Array.Copy(moduleData, (int)offset, mvid, 0, mvid.Length); module.module_uuid = new Guid(mvid); offset += (ulong)mvid.Length; Log.Debug($" module_uuid == {module.module_uuid}"); module.entry_count = ReadUInt32(moduleData, ref offset); Log.Debug($" entry_count == {module.entry_count}"); module.duplicate_count = ReadUInt32(moduleData, ref offset); Log.Debug($" duplicate_count == {module.duplicate_count}"); // MUST be kept in sync with: src/monodroid/jni/xamarin-app.hh (struct TypeMapModuleEntry) ulong pointer = ReadPointer(moduleData, ref offset); size = 0; size += GetPaddedSize <uint> (size); // type_token_id size += GetPaddedSize <uint> (size); // java_map_index ulong mapSize = size * module.entry_count; byte[] data = ELF.GetData(pointer, mapSize); module.map = new List <TypeMapModuleEntry> (); ReadMapEntries(module.map, data, module.entry_count); // MUST be kept in sync with: src/monodroid/jni/xamarin-app.hh (struct TypeMapModuleEntry) pointer = ReadPointer(moduleData, ref offset); if (pointer != 0) { mapSize = size * module.duplicate_count; data = ELF.GetData(pointer, mapSize); module.duplicate_map = new List <TypeMapModuleEntry> (); ReadMapEntries(module.duplicate_map, data, module.duplicate_count); } pointer = ReadPointer(moduleData, ref offset); module.assembly_name = ELF.GetASCIIZ(pointer); Log.Debug($" assembly_name == {module.assembly_name}"); Log.Debug(""); // Read the values to properly adjust the offset taking padding into account ReadPointer(moduleData, ref offset); ReadUInt32(moduleData, ref offset); ReadPointer(moduleData, ref offset); ret.Add(module); } return(ret); void ReadMapEntries(List <TypeMapModuleEntry> map, byte[] inputData, uint entryCount) { ulong mapOffset = 0; for (uint i = 0; i < entryCount; i++) { var entry = new TypeMapModuleEntry { type_token_id = ReadUInt32(inputData, ref mapOffset), java_map_index = ReadUInt32(inputData, ref mapOffset) }; map.Add(entry); } } }
bool DoConvert() { if (modules == null || javaTypes == null) { Log.Warning($"{Description}: cannot convert maps, no data"); return(false); } string filePath = ELF.FilePath; var managedToJava = new List <MapEntry> (); uint index = 0; bool somethingFailed = false; foreach (TypeMapModule m in modules) { ConvertManagedMap(m, EnsureMap(m), isDuplicate: false); if (m.duplicate_map != null) { if (!ConvertManagedMap(m, m.duplicate_map, isDuplicate: true)) { somethingFailed = true; } } index++; } if (somethingFailed) { return(false); } index = 0; var javaToManaged = new List <MapEntry> (); foreach (TypeMapJava tmj in javaTypes) { (bool success, TypeMapModule? module, bool isGeneric, bool isDuplicate) = FindManagedType(tmj.module_index, tmj.type_token_id); if (!success) { somethingFailed = true; continue; } if (module == null) { throw new InvalidOperationException("module must not be null here"); } javaToManaged.Add( new MapEntry( MakeManagedType(module.module_uuid, tmj.type_token_id, module.assembly_name, filePath, isGeneric, isDuplicate), new MapJavaType(tmj.java_name, filePath) ) ); index++; } map = MakeMap(managedToJava, javaToManaged); return(true); bool ConvertManagedMap(TypeMapModule module, List <TypeMapModuleEntry> map, bool isDuplicate) { foreach (TypeMapModuleEntry entry in map) { TypeMapJava java; if ((uint)javaTypes.Count <= entry.java_map_index) { Log.Error($"Managed type {entry.type_token_id} in module {module.assembly_name} ({module.module_uuid}) has invalid Java map index {entry.java_map_index}"); return(false); } java = javaTypes[(int)entry.java_map_index]; managedToJava.Add( new MapEntry( MakeManagedType(module.module_uuid, entry.type_token_id, module.assembly_name, filePath, isGeneric: false, isDuplicate: isDuplicate), new MapJavaType(java.java_name, filePath) ) ); } return(true); } (bool success, TypeMapModule?module, bool isGeneric, bool isDuplicate) FindManagedType(uint moduleIndex, uint tokenID) { if (moduleIndex >= (uint)modules.Count) { Log.Error($"Invalid module index {moduleIndex} for type token ID {tokenID} at Java map index {index}"); return(false, null, false, false); } TypeMapModule m = modules[(int)moduleIndex]; if (tokenID == 0) { return(true, m, true, false); } foreach (TypeMapModuleEntry entry in EnsureMap(m)) { if (entry.type_token_id == tokenID) { return(true, m, false, false); } } if (m.duplicate_map != null) { foreach (TypeMapModuleEntry entry in m.duplicate_map) { if (entry.type_token_id == tokenID) { return(true, m, false, true); } } } Log.Error($"Module {m.assembly_name} ({m.module_uuid}) at index {moduleIndex} doesn't contain an entry for managed type with token ID {tokenID}"); return(false, null, false, false); } List <TypeMapModuleEntry> EnsureMap(TypeMapModule m) { if (m.map == null) { throw new InvalidOperationException($"Module {m.module_uuid} ({m.assembly_name}) has no map?"); } return(m.map); } }