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);
        }
        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);
                }
            }
        }