void LoadMap(string name, ulong pointer, uint entry_count, Action <string, string> addToMap)
        {
            string entries = entry_count == 1 ? "entry" : "entries";

            Log.Info($"  Loading {name} map: {entry_count} {entries}, please wait...");

            ulong size = 0;

            size += GetPaddedSize <string> (size);            // from
            size += GetPaddedSize <string> (size);            // to

            ulong mapSize = entry_count * size;

            byte[] data = ELF.GetData(pointer, mapSize);

            ulong  offset = 0;
            string mapFrom;
            string mapTo;

            for (uint i = 0; i < entry_count; i++)
            {
                pointer = ReadPointer(data, ref offset);
                if (pointer != 0)
                {
                    mapFrom = ELF.GetASCIIZ(pointer);
                }
                else
                {
                    mapFrom = $"#{i}";
                }

                pointer = ReadPointer(data, ref offset);
                if (pointer != 0)
                {
                    mapTo = ELF.GetASCIIZ(pointer);
                }
                else
                {
                    mapTo = $"#{i}";
                }

                addToMap(mapFrom, mapTo);
            }
        }
        List <TypeMapJava> LoadJavaTypes(string filePath)
        {
            ulong javaTypeCount = (ulong)ELF.GetUInt32(JavaTypeCountSymbolName);
            ulong javaNameWidth = (ulong)ELF.GetUInt32(JavaNameWidthSymbolName);

            // MUST be kept in sync with: src/monodroid/jni/xamarin-app.hh (struct TypeMapModule)
            ulong size = 0;

            size += GetPaddedSize <uint> (size);            // module_index
            size += GetPaddedSize <uint> (size);            // type_token_id
            size += javaNameWidth;

            byte[] data = ELF.GetData(MapJavaSymbolName);
            if (data.Length == 0)
            {
                throw new InvalidOperationException($"{filePath} doesn't have a valid '{MapJavaSymbolName}' symbol");
            }

            ulong calculatedJavaTypeCount = (ulong)data.LongLength / size;

            if (calculatedJavaTypeCount != javaTypeCount)
            {
                throw new InvalidOperationException($"{filePath} has invalid '{JavaTypeCountSymbolName}' symbol value ({javaTypeCount}), '{JavaTypeCountSymbolName}' size indicates there are {calculatedJavaTypeCount} managedToJava instead");
            }

            var   ret    = new List <TypeMapJava> ();
            ulong offset = 0;

            for (ulong i = 0; i < javaTypeCount; i++)
            {
                var javaEntry = new TypeMapJava {
                    module_index  = ReadUInt32(data, ref offset, packed: true),
                    type_token_id = ReadUInt32(data, ref offset, packed: true),
                };

                javaEntry.java_name = ELF.GetASCIIZ(data, offset);
                offset += javaNameWidth;
                ret.Add(javaEntry);
            }

            return(ret);
        }
        bool DoLoadMaps()
        {
            // MUST be kept in sync with: src/monodroid/jni/xamarin-app.hh (struct TypeMap)
            ulong size = 0;

            size += GetPaddedSize <uint> (size);              // entry_count
            size += GetPaddedSize <string> (size);            // assembly_name (pointer)
            size += GetPaddedSize <string> (size);            // data (pointer)
            size += GetPaddedSize <string> (size);            // java_to_managed (pointer)
            size += GetPaddedSize <string> (size);            // managed_to_java (pointer)

            string filePath = ELF.FilePath;

            byte[] mapData = ELF.GetData(TypeMapSymbolName);
            if (mapData.Length == 0)
            {
                Log.Error($"{filePath} doesn't have a valid '{TypeMapSymbolName}' symbol");
                return(false);
            }

            if ((ulong)mapData.Length != size)
            {
                Log.Error($"Symbol '{TypeMapSymbolName}' in {filePath} has invalid size. Expected {size}, got {mapData.Length}");
                return(false);
            }

            ulong offset      = 0;
            uint  entry_count = ReadUInt32(mapData, ref offset);

            ReadPointer(mapData, ref offset);                 // assembly_name, unused in Debug mode
            ReadPointer(mapData, ref offset);                 // data, unused in Debug mode

            ulong pointer = ReadPointer(mapData, ref offset); // java_to_managed

            LoadMap("Java to Managed", pointer, entry_count, AddJavaToManaged);

            pointer = ReadPointer(mapData, ref offset);              // managed_to_java
            LoadMap("Managed to Java", pointer, entry_count, AddManagedToJava);

            return(true);
        }
        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);
                }
            }
        }