From 6f93cf47f6f17fbd050c73d1e85fa03715855575 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Wed, 8 Jan 2020 10:34:38 +0100 Subject: [PATCH] Update for changes introduced in 130905e1612f1c3f41b6b233056cae2753270630 --- .../Android.Runtime/AndroidRuntime.cs | 2 +- .../Utilities/TypeMapGenerator.cs | 16 +- src/monodroid/jni/embedded-assemblies.cc | 186 ++++++++++++++---- src/monodroid/jni/embedded-assemblies.hh | 6 +- src/monodroid/jni/xamarin-app.hh | 9 +- 5 files changed, 178 insertions(+), 41 deletions(-) diff --git a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs index 93a5fc0e9de..da6af9f62a8 100644 --- a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs +++ b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs @@ -234,7 +234,7 @@ protected override IEnumerable GetSimpleReferences (Type type) foreach (var simpleRef in base.GetSimpleReferences (type)) { yield return simpleRef; } - var j = JNIEnv.monodroid_typemap_managed_to_java (type.FullName + ", " + type.Assembly.GetName ().Name); + var j = JNIEnv.monodroid_typemap_managed_to_java (type.Module.ModuleVersionId.ToByteArray (), type.MetadataToken); if (j != IntPtr.Zero) { yield return Marshal.PtrToStringAnsi (j); } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs index 57066408d8e..208a9c43b11 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs @@ -304,13 +304,18 @@ void OutputModule (BinaryWriter bw, byte[] moduleUUID, ModuleData moduleData) bw.Write (moduleUUID); var javaNames = new SortedDictionary (StringComparer.Ordinal); + var duplicateJavaNames = new Dictionary (StringComparer.Ordinal); var managedTypes = new SortedDictionary (); - var managedTypeNames = new Dictionary (); + // TODO: put duplicate types in a separate list (typeid:index) at the end of the typemap file/assembly code + // and mark such entries somehow in the typeid->java table (e.g. set the high bit of the index, it's unlikely + // we'd have 4 billion entries) int maxJavaNameLength = 0; foreach (TypeMapEntry entry in moduleData.Types) { if (javaNames.ContainsKey (entry.JavaName)) { logger ($"Warning: duplicate Java type name '{entry.JavaName}' (old token: {javaNames [entry.JavaName]}; new token: {entry.Token}). Ignoring the new type."); + if (!duplicateJavaNames.ContainsKey (entry.JavaName)) + duplicateJavaNames.Add (entry.JavaName, false); continue; } @@ -319,16 +324,21 @@ void OutputModule (BinaryWriter bw, byte[] moduleUUID, ModuleData moduleData) maxJavaNameLength = entry.JavaName.Length; managedTypes.Add (entry.Token, 0); - managedTypeNames.Add (entry.Token, entry.ManagedTypeName); } var javaNameList = javaNames.Keys.ToList (); foreach (TypeMapEntry entry in moduleData.Types) { + if (duplicateJavaNames.TryGetValue (entry.JavaName, out bool skip)) { + if (skip) + continue; + duplicateJavaNames [entry.JavaName] = true; + } + managedTypes [entry.Token] = (uint)javaNameList.IndexOf (entry.JavaName); } bw.Write (javaNames.Count); - bw.Write (maxJavaNameLength); + bw.Write (maxJavaNameLength + 1); string assemblyName = moduleData.Assembly.Name.Name; bw.Write (assemblyName.Length); diff --git a/src/monodroid/jni/embedded-assemblies.cc b/src/monodroid/jni/embedded-assemblies.cc index c00b5622a04..47ee25324e6 100644 --- a/src/monodroid/jni/embedded-assemblies.cc +++ b/src/monodroid/jni/embedded-assemblies.cc @@ -162,15 +162,6 @@ EmbeddedAssemblies::binary_search (const Key *key, const Entry *base, size_t nme MonoReflectionType* EmbeddedAssemblies::typemap_java_to_managed (MonoString *java_type) { -// #if defined (DEBUG) || !defined (ANDROID) -// for (TypeMappingInfo *info = java_to_managed_maps; info != nullptr; info = info->next) { -// /* log_warn (LOG_DEFAULT, "# jonp: checking file: %s!%s for type '%s'", info->source_apk, info->source_entry, java); */ -// const char *e = reinterpret_cast (bsearch (java, info->mapping, static_cast(info->num_entries), static_cast(info->entry_length), TypeMappingInfo_compare_key)); -// if (e == nullptr) -// continue; -// return e + info->value_offset; -// } -// #endif timing_period total_time; if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { timing = new Timing (); @@ -178,10 +169,28 @@ EmbeddedAssemblies::typemap_java_to_managed (MonoString *java_type) } simple_pointer_guard java_type_name (mono_string_to_utf8 (java_type)); - if (!java_type_name || *java_type_name == '\0') { + if (XA_UNLIKELY (!java_type_name || *java_type_name == '\0')) { + return nullptr; + } + + int32_t type_token_id = -1; +#if defined (DEBUG) || !defined (ANDROID) + size_t idx = 0; + for (; idx < module_count; idx++) { + const uint8_t *java_entry = binary_search (java_type_name.get (), modules[idx].java_map, modules[idx].entry_count, modules[idx].java_name_width + 3); + if (java_entry == nullptr) + continue; + type_token_id = *reinterpret_cast(java_entry + modules[idx].java_name_width); + break; + } + + if (idx >= module_count) { + log_error (LOG_ASSEMBLY, "Unable to find module with Java type '%s' mapping", java_type_name.get ()); return nullptr; } + TypeMapModule &module = modules[idx]; +#else const TypeMapJava *java_entry = binary_search (java_type_name.get (), map_java, java_type_count, java_name_width); if (java_entry == nullptr) { log_warn (LOG_ASSEMBLY, "Unable to find mapping to a managed type from Java type '%s'", java_type_name.get ()); @@ -199,6 +208,8 @@ EmbeddedAssemblies::typemap_java_to_managed (MonoString *java_type) log_warn (LOG_ASSEMBLY, "Unable to find mapping from Java type '%s' to managed type with token ID %u in module [%s]", java_type_name.get (), java_entry->type_token_id, mono_guid_to_string (module.module_uuid)); return nullptr; } + type_token_id = java_entry->type_token_id; +#endif if (module.image == nullptr) { module.image = mono_image_loaded (module.assembly_name); @@ -213,10 +224,10 @@ EmbeddedAssemblies::typemap_java_to_managed (MonoString *java_type) } } - log_warn (LOG_ASSEMBLY, "Java type '%s' corresponds to managed token id %u (0x%x)", java_type_name.get (), java_entry->type_token_id, java_entry->type_token_id); - MonoClass *klass = mono_class_get (module.image, static_cast(java_entry->type_token_id)); + log_warn (LOG_ASSEMBLY, "Java type '%s' corresponds to managed token id %u (0x%x)", java_type_name.get (), type_token_id, type_token_id); + MonoClass *klass = mono_class_get (module.image, static_cast(type_token_id)); if (klass == nullptr) { - log_error (LOG_ASSEMBLY, "Unable to find managed type with token ID %u in assembly '%s', corresponding to Java type '%s'", java_entry->type_token_id, module.assembly_name, java_type_name.get ()); + log_error (LOG_ASSEMBLY, "Unable to find managed type with token ID %u in assembly '%s', corresponding to Java type '%s'", type_token_id, module.assembly_name, java_type_name.get ()); return nullptr; } @@ -236,6 +247,14 @@ EmbeddedAssemblies::compare_java_name (const char *java_name, const TypeMapJava return strcmp (java_name, reinterpret_cast(entry->java_name)); } +#if defined (DEBUG) || !defined (ANDROID) +int +EmbeddedAssemblies::compare_java_name (const char *java_name, const uint8_t *entry) +{ + return strcmp (java_name, reinterpret_cast(entry)); +} +#endif // DEBUG || !ANDROID + const char* EmbeddedAssemblies::typemap_managed_to_java (const uint8_t *mvid, const int32_t token) { @@ -258,7 +277,16 @@ EmbeddedAssemblies::typemap_managed_to_java (const uint8_t *mvid, const int32_t return nullptr; } - const TypeMapModule *match = binary_search (mvid, map_modules, map_module_count); + TypeMapModule *map; + uint32_t map_entry_count; +#if defined (DEBUG) || !defined (ANDROID) + map = modules; + map_entry_count = module_count; +#else + map = map_modules; + map_entry_count = map_module_count; +#endif + const TypeMapModule *match = binary_search (mvid, map, map_entry_count); if (match == nullptr) { log_warn (LOG_ASSEMBLY, "Module matching MVID [%s] not found.", mono_guid_to_string (mvid)); return nullptr; @@ -272,17 +300,28 @@ EmbeddedAssemblies::typemap_managed_to_java (const uint8_t *mvid, const int32_t // Each map entry is a pair of 32-bit integers: [TypeTokenID][JavaMapArrayIndex] const TypeMapModuleEntry *entry = binary_search (&token, match->map, match->entry_count); if (entry == nullptr) { - log_warn (LOG_ASSEMBLY, "Type with token %d in module [%s] not found.", token, mono_guid_to_string (mvid)); + log_warn (LOG_ASSEMBLY, "Type with token %d (0x%x) in module {%s} (%s) not found.", token, token, mono_guid_to_string (mvid), match->assembly_name); return nullptr; } - if (entry->java_map_index >= java_type_count) { - log_warn (LOG_ASSEMBLY, "Type with token %d in module [%s] has invalid Java type index %u", token, mono_guid_to_string (mvid), entry->java_map_index); + uint32_t java_entry_count; +#if defined (DEBUG) || !defined (ANDROID) + java_entry_count = match->entry_count; +#else + java_entry_count = java_type_count; +#endif + if (entry->java_map_index >= java_entry_count) { + log_warn (LOG_ASSEMBLY, "Type with token %d (0x%x) in module {%s} (%s) has invalid Java type index %u", token, token, mono_guid_to_string (mvid), match->assembly_name, entry->java_map_index); return nullptr; } + char *ret; +#if defined (DEBUG) || !defined (ANDROID) + ret = reinterpret_cast(match->java_map + ((match->java_name_width + 4) * entry->java_map_index)); +#else const TypeMapJava *java_entry = reinterpret_cast (reinterpret_cast(map_java) + ((sizeof(TypeMapJava) + java_name_width) * entry->java_map_index)); - const char *ret = reinterpret_cast(java_entry->java_name); + ret = reinterpret_cast(java_entry->java_name); +#endif if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { total_time.mark_end (); @@ -292,9 +331,11 @@ EmbeddedAssemblies::typemap_managed_to_java (const uint8_t *mvid, const int32_t log_debug ( LOG_ASSEMBLY, - "Type with token %d in module [%s] corresponds to Java type '%s'", + "Type with token %d (0x%x) in module {%s} (%s) corresponds to Java type '%s'", + token, token, mono_guid_to_string (mvid), + match->assembly_name, ret ); @@ -482,11 +523,10 @@ EmbeddedAssemblies::typemap_read_header ([[maybe_unused]] int dir_fd, const char return false; } - size_t data_size = 0; ssize_t nread = read (fd, &header, sizeof (header)); if (nread <= 0) { if (nread < 0) { - log_error (LOG_ASSEMBLY, "Failed to read %s file header from '%s/%s': %s", file_type, dir_path, file_path, strerror(errno)); + log_error (LOG_ASSEMBLY, "Failed to read %s file header from '%s/%s': %s", file_type, dir_path, file_path, strerror (errno)); } else { log_error (LOG_ASSEMBLY, "End of file while reading %s file header from '%s/%s'", file_type, dir_path, file_path); } @@ -507,31 +547,58 @@ EmbeddedAssemblies::typemap_read_header ([[maybe_unused]] int dir_fd, const char return true; } -bool +uint8_t* +EmbeddedAssemblies::typemap_load_index (TypeMapIndexHeader &header, size_t file_size, int index_fd) +{ + constexpr size_t UUID_SIZE = 16; + + size_t entry_size = header.module_file_name_width + UUID_SIZE; + size_t data_size = entry_size * module_count; + if (sizeof(header) + data_size > file_size) { + log_error (LOG_ASSEMBLY, "Index file is too small, expected %u, found %u bytes", data_size + sizeof(header), file_size); + return nullptr; + } + + auto data = new uint8_t [data_size]; + ssize_t nread = read (index_fd, data, data_size); + if (nread != static_cast(data_size)) { + log_error (LOG_ASSEMBLY, "Failed to read %u bytes from index file. %s", data_size, strerror (errno)); + return nullptr; + } + + uint8_t *p = data; + for (size_t i = 0; i < module_count; i++) { + memcpy (modules[i].module_uuid, p, UUID_SIZE); + modules[i].assembly_name = reinterpret_cast(p + UUID_SIZE); + p += entry_size; + } + + return data; +} + +uint8_t* EmbeddedAssemblies::typemap_load_index (int dir_fd, const char *dir_path, const char *index_path) { log_debug (LOG_ASSEMBLY, "Loading TypeMap index file '%s/%s'", dir_path, index_path); - bool ret = true; TypeMapIndexHeader header; size_t file_size; int fd = -1; + uint8_t *data = nullptr; if (!typemap_read_header (dir_fd, "TypeMap index", dir_path, index_path, MODULE_INDEX_MAGIC, header, file_size, fd)) { - ret = false; goto cleanup; } module_count = header.entry_count; - modules = new TypeMapModule[module_count]; - // TODO: read actual data - // TODO: pre-fill UUID + file name (store in `assembly_name`) from the previous step + modules = new TypeMapModule[module_count](); + data = typemap_load_index (header, file_size, fd); cleanup: if (fd >= 0) close (fd); - return ret; + return data; } bool @@ -543,17 +610,66 @@ EmbeddedAssemblies::typemap_load_file (int dir_fd, const char *dir_path, const c BinaryTypeMapHeader header; size_t file_size; int fd = -1; + size_t alloc_size; + ssize_t nread;; + char *chars; + uint8_t *p; if (!typemap_read_header (dir_fd, "TypeMap", dir_path, file_path, MODULE_MAGIC, header, file_size, fd)) { ret = false; goto cleanup; } - log_debug (LOG_ASSEMBLY, "TypeMap '%s/%s': entry count == %u; Java type name field width == %u; MVID == %s", dir_path, file_path, header.entry_count, header.java_name_width, mono_guid_to_string (header.module_uuid)); - + alloc_size = ADD_WITH_OVERFLOW_CHECK (size_t, header.assembly_name_length, 1); + module.assembly_name = new char[alloc_size]; + nread = read (fd, module.assembly_name, header.assembly_name_length); + module.assembly_name [header.assembly_name_length] = 0; module.entry_count = header.entry_count; - // TODO: set module.assembly_name - // TODO: allocate memory for module.map and read data into it + + log_debug (LOG_ASSEMBLY, "TypeMap '%s/%s': entry count == %u; Java type name field width == %u; MVID == %s; assembly name length == %u; assembly name == %s", + dir_path, file_path, header.entry_count, header.java_name_width, mono_guid_to_string (header.module_uuid), header.assembly_name_length, module.assembly_name); + + alloc_size = MULTIPLY_WITH_OVERFLOW_CHECK (size_t, header.java_name_width + 4, header.entry_count); + module.java_name_width = header.java_name_width; + module.java_map = new uint8_t[alloc_size]; + nread = read (fd, module.java_map, alloc_size); + if (nread != static_cast(alloc_size)) { + log_error (LOG_ASSEMBLY, "Failed to read %u bytes (java-to-managed) from module file %s/%s. %s", alloc_size, dir_path, file_path, strerror (errno)); + ret = false; + delete[] module.java_map; + module.java_map = nullptr; + goto cleanup; + } + + module.map = new TypeMapModuleEntry[header.entry_count]; + alloc_size = MULTIPLY_WITH_OVERFLOW_CHECK (size_t, sizeof(TypeMapModuleEntry), header.entry_count); + nread = read (fd, module.map, alloc_size); + if (nread != static_cast(alloc_size)) { + log_error (LOG_ASSEMBLY, "Failed to read %u bytes (managed-to-java) from module file %s/%s. %s", alloc_size, dir_path, file_path, strerror (errno)); + ret = false; + delete[] module.java_map; + module.java_map = nullptr; + delete[] module.map; + module.map = nullptr; + goto cleanup; + } + + alloc_size = module.java_name_width + 1; + chars = new char[alloc_size](); + p = module.java_map; + log_debug (LOG_ASSEMBLY, "Java entries in %s/%s", dir_path, file_path); + for (size_t i = 0; i < module.entry_count; i++) { + memcpy (chars, p, module.java_name_width); + uint32_t token = *reinterpret_cast(p + module.java_name_width); + log_debug (LOG_ASSEMBLY, " %04u: %s; %u (0x%x)", i, chars, token, token); + p += module.java_name_width + 4; + } + delete[] chars; + + log_debug (LOG_ASSEMBLY, "Managed entries in %s/%s", dir_path, file_path); + for (size_t i = 0; i < module.entry_count; i++) { + log_debug (LOG_ASSEMBLY, " %04u: token %u (0x%x); index %u", i, module.map[i].type_token_id, module.map[i].type_token_id, module.map[i].java_map_index); + } cleanup: if (fd >= 0) @@ -580,7 +696,11 @@ EmbeddedAssemblies::try_load_typemaps_from_directory (const char *path) #endif constexpr char index_name[] = "typemap.index"; - if (!typemap_load_index (dir_fd, dir_path, index_name)) { + + // The pointer must be stored here because, after index is loaded, module.assembly_name points into the index data + // and must be valid until after the actual module file is loaded. + simple_pointer_guard index_data = typemap_load_index (dir_fd, dir_path, index_name); + if (!index_data) { log_fatal (LOG_ASSEMBLY, "Unable to load TypeMap data index from '%s/%s'", dir_path.get (), index_name); exit (FATAL_EXIT_NO_ASSEMBLIES); // TODO: use a new error code here } diff --git a/src/monodroid/jni/embedded-assemblies.hh b/src/monodroid/jni/embedded-assemblies.hh index 9a01ba072cc..f2f87ef32b2 100644 --- a/src/monodroid/jni/embedded-assemblies.hh +++ b/src/monodroid/jni/embedded-assemblies.hh @@ -75,7 +75,8 @@ namespace xamarin::android::internal { template bool typemap_read_header (int dir_fd, const char *file_type, const char *dir_path, const char *file_path, uint32_t expected_magic, H &header, size_t &file_size, int &fd); - bool typemap_load_index (int dir_fd, const char *dir_path, const char *index_path); + uint8_t* typemap_load_index (int dir_fd, const char *dir_path, const char *index_path); + uint8_t* typemap_load_index (TypeMapIndexHeader &header, size_t file_size, int index_fd); bool typemap_load_file (int dir_fd, const char *dir_path, const char *file_path, TypeMapModule &module); #endif // DEBUG || !ANDROID bool register_debug_symbols_for_assembly (const char *entry_name, MonoBundledAssembly *assembly, const mono_byte *debug_contents, int debug_size); @@ -108,6 +109,9 @@ namespace xamarin::android::internal { static int compare_mvid (const uint8_t *mvid, const TypeMapModule *module); static int compare_type_token (const int32_t *token, const TypeMapModuleEntry *entry); static int compare_java_name (const char *java_name, const TypeMapJava *entry); +#if defined (DEBUG) || !defined (ANDROID) + static int compare_java_name (const char *java_name, const uint8_t *java_map); +#endif private: bool register_debug_symbols; diff --git a/src/monodroid/jni/xamarin-app.hh b/src/monodroid/jni/xamarin-app.hh index a1cde045669..74ed59ffe70 100644 --- a/src/monodroid/jni/xamarin-app.hh +++ b/src/monodroid/jni/xamarin-app.hh @@ -20,7 +20,6 @@ struct BinaryTypeMapHeader uint32_t entry_count; uint32_t java_name_width; uint32_t assembly_name_length; - uint8_t assembly_name[]; }; struct TypeMapIndexHeader @@ -41,9 +40,13 @@ struct TypeMapModule { uint8_t module_uuid[16]; uint32_t entry_count; - const TypeMapModuleEntry *map; - const char *assembly_name; + TypeMapModuleEntry *map; + char *assembly_name; MonoImage *image; +#ifdef DEBUG + uint32_t java_name_width; + uint8_t *java_map; +#endif }; struct TypeMapJava