diff --git a/.gitignore b/.gitignore index b423d76104c..d213090b878 100644 --- a/.gitignore +++ b/.gitignore @@ -30,8 +30,6 @@ Novell *.keystore /omnisharp.json src/native/CMakePresets.json -src/native/clr/pinvoke-override/generate-pinvoke-tables -src/native/clr/pinvoke-override/pinvoke-tables.include.generated src/native/mono/pinvoke-override/generate-pinvoke-tables src/native/mono/pinvoke-override/pinvoke-tables.include.generated dotnet-install.ps1 diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.apkdesc index 065272635fa..5a303c0e468 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.apkdesc @@ -5,34 +5,34 @@ "Size": 3036 }, "classes.dex": { - "Size": 402352 + "Size": 402852 }, "lib/arm64-v8a/libassembly-store.so": { - "Size": 2717688 + "Size": 2724872 }, "lib/arm64-v8a/libclrjit.so": { - "Size": 2835128 + "Size": 2757816 }, "lib/arm64-v8a/libcoreclr.so": { - "Size": 4891056 + "Size": 4837240 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 1324320 + "Size": 1246136 }, "lib/arm64-v8a/libSystem.Globalization.Native.so": { "Size": 72112 }, "lib/arm64-v8a/libSystem.IO.Compression.Native.so": { - "Size": 1262888 + "Size": 1258776 }, "lib/arm64-v8a/libSystem.Native.so": { - "Size": 101888 + "Size": 99664 }, "lib/arm64-v8a/libSystem.Security.Cryptography.Native.Android.so": { - "Size": 168080 + "Size": 163936 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 20968 + "Size": 21448 }, "res/drawable-hdpi-v4/icon.png": { "Size": 2178 @@ -59,5 +59,5 @@ "Size": 1904 } }, - "PackageSize": 7271867 + "PackageSize": 7239099 } \ No newline at end of file diff --git a/src/native/CMakeLists.txt b/src/native/CMakeLists.txt index 26139682952..a8b57a80e8b 100644 --- a/src/native/CMakeLists.txt +++ b/src/native/CMakeLists.txt @@ -225,7 +225,10 @@ file(REAL_PATH "../../" REPO_ROOT_DIR) set(EXTERNAL_DIR "${REPO_ROOT_DIR}/external") set(JAVA_INTEROP_SRC_PATH "${EXTERNAL_DIR}/Java.Interop/src/java-interop") set(LIBUNWIND_SOURCE_DIR "${EXTERNAL_DIR}/libunwind") -set(ROBIN_MAP_DIR "${EXTERNAL_DIR}/robin-map") + +if(IS_MONO_RUNTIME) + set(ROBIN_MAP_DIR "${EXTERNAL_DIR}/robin-map") +endif() # # Include directories diff --git a/src/native/clr/host/CMakeLists.txt b/src/native/clr/host/CMakeLists.txt index e18d1a2531d..a5bf1c4b05d 100644 --- a/src/native/clr/host/CMakeLists.txt +++ b/src/native/clr/host/CMakeLists.txt @@ -119,7 +119,6 @@ macro(lib_target_options TARGET_NAME) PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include ${EXTERNAL_DIR} - ${ROBIN_MAP_DIR}/include ) target_include_directories( diff --git a/src/native/clr/include/host/pinvoke-override-impl.hh b/src/native/clr/include/host/pinvoke-override-impl.hh index 2f2b9498394..b340e297ba3 100644 --- a/src/native/clr/include/host/pinvoke-override-impl.hh +++ b/src/native/clr/include/host/pinvoke-override-impl.hh @@ -4,154 +4,50 @@ #error The PINVOKE_OVERRIDE_INLINE macro must be defined before including this header file #endif -#include -#include - #include "pinvoke-override.hh" #include "../runtime-base/logger.hh" #include "../runtime-base/monodroid-dl.hh" -#include "../runtime-base/startup-aware-lock.hh" namespace xamarin::android { PINVOKE_OVERRIDE_INLINE - auto PinvokeOverride::load_library_symbol (std::string_view const& library_name, std::string_view const& symbol_name, void **dso_handle) noexcept -> void* + auto PinvokeOverride::load_library_symbol (std::string_view const& library_name, std::string_view const& symbol_name) noexcept -> void* { - void *lib_handle = dso_handle == nullptr ? nullptr : *dso_handle; - - if (lib_handle == nullptr) { - // We're being called as part of the p/invoke mechanism, we don't need to look in the AOT cache - constexpr bool PREFER_AOT_CACHE = false; + void *lib_handle = nullptr; - // Handle p/invokes of the form [DllImport ("liblog")] or [DllImport ("log")] - // TODO: try modifying the name to contain both the `log` prefix and the `.so` suffix - dynamic_local_path_string short_library_name; - if (!Util::path_has_directory_components (library_name)) { - if (!library_name.starts_with (Constants::DSO_PREFIX)) { - short_library_name.append (Constants::DSO_PREFIX); - } - short_library_name.append (library_name); - if (!short_library_name.ends_with (Constants::dso_suffix)) { - short_library_name.append (Constants::dso_suffix); - } + // We're being called as part of the p/invoke mechanism, so skip AOT DSO lookup. + constexpr bool PREFER_AOT_CACHE = false; - log_debug (LOG_ASSEMBLY, "Modified p/invoke library name to '{}'", short_library_name.get ()); - lib_handle = MonodroidDl::monodroid_dlopen (short_library_name.get (), microsoft::java_interop::JAVA_INTEROP_LIB_LOAD_LOCALLY); + // Handle p/invokes of the form [DllImport ("liblog")] or [DllImport ("log")] + // TODO: try modifying the name to contain both the `log` prefix and the `.so` suffix + dynamic_local_path_string short_library_name; + if (!Util::path_has_directory_components (library_name)) { + if (!library_name.starts_with (Constants::DSO_PREFIX)) { + short_library_name.append (Constants::DSO_PREFIX); } - - if (lib_handle == nullptr) { - lib_handle = MonodroidDl::monodroid_dlopen (library_name, microsoft::java_interop::JAVA_INTEROP_LIB_LOAD_LOCALLY); + short_library_name.append (library_name); + if (!short_library_name.ends_with (Constants::dso_suffix)) { + short_library_name.append (Constants::dso_suffix); } - if (lib_handle == nullptr) { - log_warn (LOG_ASSEMBLY, "Shared library '{}' not loaded, p/invoke '{}' may fail", library_name, symbol_name); - return nullptr; - } - - if (dso_handle != nullptr) { - void *expected_null = nullptr; - if (!__atomic_compare_exchange (dso_handle, &expected_null, &lib_handle, false /* weak */, __ATOMIC_ACQUIRE /* success_memorder */, __ATOMIC_RELAXED /* xxxfailure_memorder */)) { - log_debug (LOG_ASSEMBLY, "Library '{}' handle already cached by another thread", library_name); - } - } + log_debug (LOG_ASSEMBLY, "Modified p/invoke library name to '{}'", short_library_name.get ()); + lib_handle = MonodroidDl::monodroid_dlopen (short_library_name.get (), microsoft::java_interop::JAVA_INTEROP_LIB_LOAD_LOCALLY); } - void *entry_handle = MonodroidDl::monodroid_dlsym (lib_handle, symbol_name); - if (entry_handle == nullptr) { - log_warn (LOG_ASSEMBLY, "Symbol '{}' not found in shared library '{}', p/invoke may fail", symbol_name, library_name); - return nullptr; + if (lib_handle == nullptr) { + lib_handle = MonodroidDl::monodroid_dlopen (library_name, microsoft::java_interop::JAVA_INTEROP_LIB_LOAD_LOCALLY); } - return entry_handle; - } - - PINVOKE_OVERRIDE_INLINE - auto PinvokeOverride::load_library_entry (std::string const& library_name, std::string const& entrypoint_name, pinvoke_api_map_ptr api_map) noexcept -> void* - { - // Make sure some other thread hasn't just added the entry - auto iter = api_map->find (entrypoint_name); - if (iter != api_map->end () && iter->second != nullptr) { - return iter->second; + if (lib_handle == nullptr) { + log_warn (LOG_ASSEMBLY, "Shared library '{}' not loaded, p/invoke '{}' may fail", library_name, symbol_name); + return nullptr; } - void *entry_handle = load_library_symbol (library_name.c_str (), entrypoint_name.c_str ()); + void *entry_handle = MonodroidDl::monodroid_dlsym (lib_handle, symbol_name); if (entry_handle == nullptr) { - // error already logged + log_warn (LOG_ASSEMBLY, "Symbol '{}' not found in shared library '{}', p/invoke may fail", symbol_name, library_name); return nullptr; } - log_debug (LOG_ASSEMBLY, "Caching p/invoke entry {} @ {}", library_name, entrypoint_name); - (*api_map)[entrypoint_name] = entry_handle; return entry_handle; } - - PINVOKE_OVERRIDE_INLINE - auto PinvokeOverride::fetch_or_create_pinvoke_map_entry (std::string const& library_name, std::string const& entrypoint_name, hash_t entrypoint_name_hash, pinvoke_api_map_ptr api_map, bool need_lock) noexcept -> void* - { - auto iter = api_map->find (entrypoint_name, entrypoint_name_hash); - if (iter != api_map->end () && iter->second != nullptr) { - return iter->second; - } - - if (!need_lock) { - return load_library_entry (library_name, entrypoint_name, api_map); - } - - StartupAwareLock lock (pinvoke_map_write_lock); - return load_library_entry (library_name, entrypoint_name, api_map); - } - - PINVOKE_OVERRIDE_INLINE - auto PinvokeOverride::find_pinvoke_address (hash_t hash, const PinvokeEntry *entries, size_t entry_count) noexcept -> PinvokeEntry* - { - while (entry_count > 0uz) { - const size_t mid = entry_count / 2uz; - const PinvokeEntry *const ret = entries + mid; - const std::strong_ordering result = hash <=> ret->hash; - - if (result < 0) { - entry_count = mid; - } else if (result > 0) { - entries = ret + 1; - entry_count -= mid + 1uz; - } else { - return const_cast(ret); - } - } - - return nullptr; - } - - PINVOKE_OVERRIDE_INLINE - auto PinvokeOverride::handle_other_pinvoke_request (std::string_view const& library_name, hash_t library_name_hash, std::string_view const& entrypoint_name, hash_t entrypoint_name_hash) noexcept -> void* - { - std::string lib_name {library_name}; - std::string entry_name {entrypoint_name}; - - auto iter = other_pinvoke_map.find (lib_name, library_name_hash); - void *handle = nullptr; - if (iter == other_pinvoke_map.end ()) { - StartupAwareLock lock (pinvoke_map_write_lock); - - pinvoke_api_map_ptr lib_map; - // Make sure some other thread hasn't just added the map - iter = other_pinvoke_map.find (lib_name, library_name_hash); - if (iter == other_pinvoke_map.end () || iter->second == nullptr) { - lib_map = new pinvoke_api_map (1); - other_pinvoke_map[lib_name] = lib_map; - } else { - lib_map = iter->second; - } - - handle = fetch_or_create_pinvoke_map_entry (lib_name, entry_name, entrypoint_name_hash, lib_map, /* need_lock */ false); - } else { - if (iter->second == nullptr) [[unlikely]] { - log_warn (LOG_ASSEMBLY, "Internal error: null entry in p/invoke map for key '{}'", library_name); - return nullptr; // fall back to `monodroid_dlopen` - } - - handle = fetch_or_create_pinvoke_map_entry (lib_name, entry_name, entrypoint_name_hash, iter->second, /* need_lock */ true); - } - - return handle; - } } diff --git a/src/native/clr/include/host/pinvoke-override.hh b/src/native/clr/include/host/pinvoke-override.hh index 099c5264669..ab797183086 100644 --- a/src/native/clr/include/host/pinvoke-override.hh +++ b/src/native/clr/include/host/pinvoke-override.hh @@ -1,89 +1,16 @@ #pragma once -#include -#include #include #include -// NDEBUG causes robin_map.h not to include which, in turn, prevents indirect inclusion of . -// conflicts with our std::mutex definition in cppcompat.hh -#if !defined (NDEBUG) -#define NDEBUG -#define NDEBUG_UNDEFINE -#endif - -// hush some compiler warnings -#if defined (__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-parameter" -#endif // __clang__ - -#include - -#if defined (__clang__) -#pragma clang diagnostic pop -#endif // __clang__ - -#if defined (NDEBUG_UNDEFINE) -#undef NDEBUG -#undef NDEBUG_UNDEFINE -#endif - #include "../runtime-base/monodroid-dl.hh" -#include namespace xamarin::android { - struct PinvokeEntry - { - hash_t hash; - const char *name; - void *func; - }; - - struct string_hash - { - [[gnu::always_inline]] - xamarin::android::hash_t operator() (std::string const& s) const noexcept - { - return xamarin::android::xxhash::hash (s.c_str (), s.length ()); - } - }; - class PinvokeOverride { - using pinvoke_api_map = tsl::robin_map< - std::string, - void*, - string_hash, - std::equal_to, - std::allocator>, - true - >; - - using pinvoke_api_map_ptr = pinvoke_api_map*; - using pinvoke_library_map = tsl::robin_map< - std::string, - pinvoke_api_map_ptr, - string_hash, - std::equal_to, - std::allocator>, - true - >; - - static inline constexpr pinvoke_library_map::size_type LIBRARY_MAP_INITIAL_BUCKET_COUNT = 1uz; - public: - static auto load_library_symbol (std::string_view const& library_name, std::string_view const& symbol_name, void **dso_handle = nullptr) noexcept -> void*; - static auto load_library_entry (std::string const& library_name, std::string const& entrypoint_name, pinvoke_api_map_ptr api_map) noexcept -> void*; - static auto fetch_or_create_pinvoke_map_entry (std::string const& library_name, std::string const& entrypoint_name, hash_t entrypoint_name_hash, pinvoke_api_map_ptr api_map, bool need_lock) noexcept -> void*; - static auto find_pinvoke_address (hash_t hash, const PinvokeEntry *entries, size_t entry_count) noexcept -> PinvokeEntry*; - static auto handle_other_pinvoke_request (std::string_view const& library_name, hash_t library_name_hash, std::string_view const& entrypoint_name, hash_t entrypoint_name_hash) noexcept -> void*; - static void handle_jni_on_load (JavaVM *vm, void *reserved) noexcept; + static auto load_library_symbol (std::string_view const& library_name, std::string_view const& symbol_name) noexcept -> void*; static auto monodroid_pinvoke_override (const char *library_name, const char *entrypoint_name) noexcept -> void*; - - private: - static inline std::mutex pinvoke_map_write_lock{}; - static inline pinvoke_library_map other_pinvoke_map { PinvokeOverride::LIBRARY_MAP_INITIAL_BUCKET_COUNT }; }; } diff --git a/src/native/clr/pinvoke-override/CMakeLists.txt b/src/native/clr/pinvoke-override/CMakeLists.txt index b1f35a16e2f..9fc1b960a30 100644 --- a/src/native/clr/pinvoke-override/CMakeLists.txt +++ b/src/native/clr/pinvoke-override/CMakeLists.txt @@ -32,12 +32,6 @@ macro(create_library _libname _alias _sources) set_static_library_suffix(${_libname}) - target_compile_definitions( - ${_libname} - PRIVATE - TSL_NO_EXCEPTIONS - ) - target_compile_options( ${_libname} PRIVATE @@ -45,12 +39,6 @@ macro(create_library _libname _alias _sources) ${PINVOKE_OVERRIDE_CXX_ARGS} ) - target_include_directories( - ${_libname} - PRIVATE - ${ROBIN_MAP_DIR}/include - ) - target_include_directories( ${_libname} PUBLIC diff --git a/src/native/clr/pinvoke-override/dynamic.cc b/src/native/clr/pinvoke-override/dynamic.cc index 5352aacab68..27cf31720c9 100644 --- a/src/native/clr/pinvoke-override/dynamic.cc +++ b/src/native/clr/pinvoke-override/dynamic.cc @@ -1,24 +1,18 @@ #include #include +#include #define PINVOKE_OVERRIDE_INLINE [[gnu::noinline]] #include using namespace xamarin::android; -using JniOnLoadHandler = jint (*) (JavaVM *vm, void *reserved); - // // These external functions are generated during application build (see obj/${CONFIGURATION}/${RID}/android/pinvoke_preserve.*.ll) // extern "C" { void* find_pinvoke (hash_t library_name_hash, hash_t entrypoint_hash, bool &known_library); - - extern const uint32_t __jni_on_load_handler_count; - extern const JniOnLoadHandler __jni_on_load_handlers[]; - extern const char* __jni_on_load_handler_names[]; - extern const void* __explicitly_preserved_symbols[]; } [[gnu::flatten]] @@ -69,31 +63,11 @@ auto PinvokeOverride::monodroid_pinvoke_override (const char *library_name, cons } log_debug (LOG_ASSEMBLY, "p/invoke not from a known library, slow path taken."sv); - pinvoke_ptr = handle_other_pinvoke_request (library_name, library_name_hash, entrypoint_name, entrypoint_hash); + pinvoke_ptr = load_library_symbol (library_name, entrypoint_name); log_debug (LOG_ASSEMBLY, "foreign library pinvoke_ptr == {:p}"sv, pinvoke_ptr); return pinvoke_ptr; } -void PinvokeOverride::handle_jni_on_load (JavaVM *vm, void *reserved) noexcept -{ - if (__jni_on_load_handler_count == 0) { - return; - } - - for (uint32_t i = 0; i < __jni_on_load_handler_count; i++) { - __jni_on_load_handlers[i] (vm, reserved); - } - - // This is just to reference the generated array, all we need from it is to be there - // TODO: see if there's an attribute we can use to make the linker keep the symbol instead. - // void *first_ptr = __explicitly_preserved_symbols; - // if (first_ptr == nullptr) { - // // This will never actually be logged, since by the time this function is called we haven't initialized - // // logging categories yet. It's here just to have some code in the if statement body. - // log_debug (LOG_ASSEMBLY, "No explicitly preserved symbols"); - // } -} - const void* Host::clr_pinvoke_override (const char *library_name, const char *entry_point_name) noexcept { log_debug (LOG_ASSEMBLY, "[dynamic] clr_pinvoke_override (\"{}\", \"{}\")"sv, optional_string (library_name), optional_string (entry_point_name)); diff --git a/src/native/clr/pinvoke-override/generate-pinvoke-tables.cc b/src/native/clr/pinvoke-override/generate-pinvoke-tables.cc deleted file mode 100644 index 4264ab3e8c4..00000000000 --- a/src/native/clr/pinvoke-override/generate-pinvoke-tables.cc +++ /dev/null @@ -1,264 +0,0 @@ -// -// This generator emits the CoreCLR `internal_pinvokes` table (symbols linked into the host itself: -// `xa-internal-api`, `java-interop`, `liblog`) and the library-name hashes. It is NOT wired into -// `build-tools/scripts/generate-pinvoke-tables.sh` (that script only regenerates the MonoVM table), -// because the CoreCLR override no longer serves the .NET BCL p/invokes from a static table. Run it by -// hand only when the internal p/invoke list changes, by compiling this file with a C++20 compiler -// (g++ 10+, clang 11+, on mac it may require XCode 12.5 or newer) against the CoreCLR host include -// dirs and running the resulting binary with the output `pinvoke-tables.include` path. -// -// Whenever a new internal p/invoke is added, try to keep the entries sorted alphabetically. This is -// not required by the generator but easier to examine by humans. -// -// If a new library is added, please remember to generate a hash of its name and update pinvoke-override-api.cc -// -// To get the list of exported native symbols for a library, you can run the following command on Unix: -// -// for s in $(llvm-nm -DUj [LIBRARY] | sort); do echo "\"$s\","; done -// -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace fs = std::filesystem; -using namespace xamarin::android; - -const std::vector internal_pinvoke_names = { -// "create_public_directory", -// "java_interop_free", -// "monodroid_clear_gdb_wait", -// "_monodroid_counters_dump", - "_monodroid_detect_cpu_and_architecture", -// "monodroid_dylib_mono_free", -// "monodroid_dylib_mono_init", -// "monodroid_dylib_mono_new", -// "monodroid_embedded_assemblies_set_assemblies_prefix", -// "monodroid_fopen", - "monodroid_free", - "_monodroid_gc_wait_for_bridge_processing", -// "_monodroid_get_dns_servers", -// "monodroid_get_dylib", -// "monodroid_get_namespaced_system_property", -// "_monodroid_get_network_interface_supports_multicast", -// "_monodroid_get_network_interface_up_state", -// "monodroid_get_system_property", - "_monodroid_gref_dec", - "_monodroid_gref_get", - "_monodroid_gref_inc", - "_monodroid_gref_log", - "_monodroid_gref_log_delete", - "_monodroid_gref_log_new", - "monodroid_log", -// "monodroid_log_traces", - "_monodroid_lookup_replacement_type", - "_monodroid_lookup_replacement_method_info", - "_monodroid_lref_log_delete", - "_monodroid_lref_log_new", - "_monodroid_max_gref_get", -// "monodroid_strdup_printf", -// "monodroid_strfreev", -// "monodroid_strsplit", - "monodroid_timing_start", - "monodroid_timing_stop", - "monodroid_TypeManager_get_java_class_name", - "clr_typemap_managed_to_java", - "clr_typemap_java_to_managed", - "clr_initialize_gc_bridge", - "_monodroid_weak_gref_dec", - "_monodroid_weak_gref_delete", - "_monodroid_weak_gref_get", - "_monodroid_weak_gref_inc", - "_monodroid_weak_gref_new", -// "path_combine", -// "recv_uninterrupted", -// "send_uninterrupted", -// "set_world_accessable", - "xamarin_app_init", - -// We can treat liblog as "internal", since we link against it - "__android_log_print", -}; - -template -struct PinvokeEntry -{ - std::string name; - Hash hash; - bool write_func_pointer; - - template friend - Os& operator<< (Os& os, PinvokeEntry const& p) - { - os << std::showbase << std::hex << p.hash << ", \"" << p.name << "\", "; - - if (p.write_func_pointer) { - return os << "reinterpret_cast(&" << p.name << ")"; - } - - return os << "nullptr"; - } -}; - -void print (std::ostream& os, std::string comment, std::string variable_name, auto const& seq) -{ - os << "\t//" << comment << '\n'; - os << "\tstd::array " << variable_name << " {{" << std::endl; - - for (auto const& elem : seq) { - os << "\t\t{" << elem << "}," << std::endl; - } - - os << "\t}};" << std::endl << std::endl; -} - -template -bool add_hash (std::string const& pinvoke, Hash hash, std::vector>& vec, std::unordered_set& used_cache, bool write_func_pointer) -{ - vec.emplace_back (pinvoke, hash, write_func_pointer); - if (used_cache.contains (hash)) { - std::cerr << (sizeof(Hash) == 4 ? "32" : "64") << "-bit hash collision for key '" << pinvoke << "': " << std::hex << std::showbase << hash << std::endl; - return true; - } - - used_cache.insert (hash); - return false; -} - -bool generate_hashes (std::string table_name, std::vector const& names, std::vector>& pinvokes32, std::vector>& pinvokes64, bool write_func_pointer) -{ - std::unordered_set used_pinvokes32{}; - std::unordered_set used_pinvokes64{}; - uint32_t hash32; - uint64_t hash64; - bool have_collisions = false; - - std::cout << "There are " << names.size () << " " << table_name << " p/invoke functions" << std::endl; - for (std::string const& pinvoke : names) { - have_collisions |= add_hash (pinvoke, xxhash32::hash (pinvoke.c_str (), pinvoke.length ()), pinvokes32, used_pinvokes32, write_func_pointer); - have_collisions |= add_hash (pinvoke, xxhash64::hash (pinvoke.c_str (), pinvoke.length ()), pinvokes64, used_pinvokes64, write_func_pointer); - } - - std::cout << "p/invoke hash collisions for '" << table_name << "' were " << (have_collisions ? "" : "not ") << "found" << std::endl; - - std::ranges::sort (pinvokes32, {}, &PinvokeEntry::hash); - std::ranges::sort (pinvokes64, {}, &PinvokeEntry::hash); - - return have_collisions; -} - -template -void write_library_name_hash (Hash (*hasher)(const char*, size_t), std::ostream& os, std::string library_name, std::string variable_prefix) -{ - Hash hash = hasher (library_name.c_str (), library_name.length ()); - os << "constexpr hash_t " << variable_prefix << "_library_hash = " << std::hex << hash << ";" << std::endl; -} - -template -void write_library_name_hashes (Hash (*hasher)(const char*, size_t), std::ostream& output) -{ - write_library_name_hash (hasher, output, "java-interop", "java_interop"); - write_library_name_hash (hasher, output, "xa-internal-api", "xa_internal_api"); - write_library_name_hash (hasher, output, "liblog", "android_liblog"); - write_library_name_hash (hasher, output, "libSystem.Native", "system_native"); - write_library_name_hash (hasher, output, "libSystem.IO.Compression.Native", "system_io_compression_native"); - write_library_name_hash (hasher, output, "libSystem.Security.Cryptography.Native.Android", "system_security_cryptography_native_android"); - write_library_name_hash (hasher, output, "libSystem.Globalization.Native", "system_globalization_native"); -} - -int main (int argc, char **argv) -{ - if (argc < 2) { - std::cerr << "Usage: generate-pinvoke-tables OUTPUT_FILE_PATH" << std::endl << std::endl; - return 1; - } - - fs::path output_file_path {argv[1]}; - - if (fs::exists (output_file_path)) { - if (fs::is_directory (output_file_path)) { - std::cerr << "Output destination '" << output_file_path << "' is a directory" << std::endl; - return 1; - } - - fs::remove (output_file_path); - } else { - fs::path file_dir = output_file_path.parent_path (); - if (fs::exists (file_dir)) { - if (!fs::is_directory (file_dir)) { - std::cerr << "Output destination parent path points to a file ('" << file_dir << "'" << std::endl; - return 1; - } - } else if (!file_dir.empty ()) { - if (!fs::create_directories (file_dir)) { - std::cerr << "Failed to create output directory '" << file_dir << "'" << std::endl; - std::cerr << strerror (errno) << std::endl; - return 1; - } - } - } - - bool have_collisions = false; - std::vector> internal_pinvokes32{}; - std::vector> internal_pinvokes64{}; - have_collisions |= generate_hashes ("internal", internal_pinvoke_names, internal_pinvokes32, internal_pinvokes64, true); - - std::cout << "Generating tables in file: " << output_file_path << std::endl; - - std::ofstream output {output_file_path, std::ios::binary}; - - output << "//" << std::endl; - output << "// Autogenarated file. DO NOT EDIT." << std::endl; - output << "//" << std::endl; - output << "// To regenerate run ../../../../build-tools/scripts/generate-pinvoke-tables.sh on Linux or macOS" << std::endl; - output << "// A compiler with support for C++20 ranges is required" << std::endl; - output << "//" << std::endl << std::endl; - - output << "#include " << std::endl; - output << "#include " << std::endl << std::endl; - - output << "namespace {" << std::endl; - output << "#if INTPTR_MAX == INT64_MAX" << std::endl; - print (output, "64-bit internal p/invoke table", "internal_pinvokes", internal_pinvokes64); - output << std::endl; - write_library_name_hashes (xxhash64::hash, output); - - output << "#else" << std::endl; - - print (output, "32-bit internal p/invoke table", "internal_pinvokes", internal_pinvokes32); - output << std::endl; - write_library_name_hashes (xxhash32::hash, output); - - output << "#endif" << std::endl << std::endl; - - output << "constexpr size_t internal_pinvokes_count = " << std::dec << std::noshowbase << internal_pinvoke_names.size () << ";" << std::endl; - output << "} // end of anonymous namespace" << std::endl; - - return have_collisions ? 1 : 0; -} - -// This serves as a quick compile-time test of the algorithm's correctness. -// The tests are copied from https://github.com/ekpyron/xxhashct/test.cpp - -template -struct constexpr_test { - static_assert (value == expected, "Compile-time hash mismatch."); -}; - -constexpr_test ("", 0), 0x2CC5D05U> constexprTest_1; -constexpr_test ("", 0), 0x36B78AE7U> constexprTest_2; -//constexpr_test ("", 0), 0xEF46DB3751D8E999ULL> constexprTest_3; -//constexpr_test ("", 0), 0xAC75FDA2929B17EFULL> constexprTest_4; -constexpr_test ("test", 4), 0x3E2023CFU> constexprTest32_5; -constexpr_test ("test", 4), 0xA9C14438U> constexprTest32_6; -//constexpr_test ("test", 4), 0x4fdcca5ddb678139ULL> constexprTest64_7; -//constexpr_test ("test", 4), 0x5A183B8150E2F651ULL> constexprTest64_8; diff --git a/src/native/clr/pinvoke-override/pinvoke-tables.include b/src/native/clr/pinvoke-override/pinvoke-tables.include deleted file mode 100644 index 716ef4ced44..00000000000 --- a/src/native/clr/pinvoke-override/pinvoke-tables.include +++ /dev/null @@ -1,97 +0,0 @@ -// -// Autogenarated file. DO NOT EDIT. -// -// To regenerate run ../../../../build-tools/scripts/generate-pinvoke-tables.sh on Linux or macOS -// A compiler with support for C++20 ranges is required -// - -#include -#include - -namespace { -#if INTPTR_MAX == INT64_MAX - //64-bit internal p/invoke table - std::array internal_pinvokes {{ - {0x37307e5fddf709dc, "_monodroid_weak_gref_dec", reinterpret_cast(&_monodroid_weak_gref_dec)}, - {0x3b2467e7eadd4a6a, "_monodroid_lref_log_new", reinterpret_cast(&_monodroid_lref_log_new)}, - {0x423c8f539a2c56d2, "_monodroid_lookup_replacement_type", reinterpret_cast(&_monodroid_lookup_replacement_type)}, - {0x4310c1531ddddc14, "__android_log_print", reinterpret_cast(&__android_log_print)}, - {0x4b1956138764939a, "_monodroid_gref_log_new", reinterpret_cast(&_monodroid_gref_log_new)}, - {0x5f0b4e426eff086b, "_monodroid_detect_cpu_and_architecture", reinterpret_cast(&_monodroid_detect_cpu_and_architecture)}, - {0x9099a4b95e3c3a89, "_monodroid_lref_log_delete", reinterpret_cast(&_monodroid_lref_log_delete)}, - {0x9187e6bc6294cacf, "clr_typemap_managed_to_java", reinterpret_cast(&clr_typemap_managed_to_java)}, - {0x920bf58357fb56f3, "clr_initialize_gc_bridge", reinterpret_cast(&clr_initialize_gc_bridge)}, - {0x9a946dfe9916a942, "clr_typemap_java_to_managed", reinterpret_cast(&clr_typemap_java_to_managed)}, - {0x9d2b3233c41789df, "_monodroid_weak_gref_inc", reinterpret_cast(&_monodroid_weak_gref_inc)}, - {0xa6ec846592d99536, "_monodroid_weak_gref_delete", reinterpret_cast(&_monodroid_weak_gref_delete)}, - {0xa7f58f3ee428cc6b, "_monodroid_gref_log_delete", reinterpret_cast(&_monodroid_gref_log_delete)}, - {0xab094d8d2fc086a1, "_monodroid_gref_dec", reinterpret_cast(&_monodroid_gref_dec)}, - {0xae3df96dda0143bd, "_monodroid_gref_log", reinterpret_cast(&_monodroid_gref_log)}, - {0xb6222d90af401865, "_monodroid_weak_gref_get", reinterpret_cast(&_monodroid_weak_gref_get)}, - {0xb8306f71b963cd3d, "monodroid_log", reinterpret_cast(&monodroid_log)}, - {0xb9bae9c43fb05089, "xamarin_app_init", reinterpret_cast(&xamarin_app_init)}, - {0xc2a21d3f6c8ccc24, "_monodroid_lookup_replacement_method_info", reinterpret_cast(&_monodroid_lookup_replacement_method_info)}, - {0xc5b4690e13898fa3, "monodroid_timing_start", reinterpret_cast(&monodroid_timing_start)}, - {0xcaab0a3ab6057bbd, "_monodroid_gref_inc", reinterpret_cast(&_monodroid_gref_inc)}, - {0xd1e121b94ea63f2e, "_monodroid_gref_get", reinterpret_cast(&_monodroid_gref_get)}, - {0xd5151b00eb33d85e, "monodroid_TypeManager_get_java_class_name", reinterpret_cast(&monodroid_TypeManager_get_java_class_name)}, - {0xe27b9849b7e982cb, "_monodroid_max_gref_get", reinterpret_cast(&_monodroid_max_gref_get)}, - {0xe86307aac9a2631a, "_monodroid_weak_gref_new", reinterpret_cast(&_monodroid_weak_gref_new)}, - {0xf3048baf83034541, "_monodroid_gc_wait_for_bridge_processing", reinterpret_cast(&_monodroid_gc_wait_for_bridge_processing)}, - {0xf41c48df6f9be476, "monodroid_free", reinterpret_cast(&monodroid_free)}, - {0xf5a918ef520db207, "monodroid_timing_stop", reinterpret_cast(&monodroid_timing_stop)}, - }}; - - -constexpr hash_t java_interop_library_hash = 0x54568ec36068e6b6; -constexpr hash_t xa_internal_api_library_hash = 0x43fd1b21148361b2; -constexpr hash_t android_liblog_library_hash = 0x1f2e4bce0544fb0a; -constexpr hash_t system_native_library_hash = 0x4cd7bd0032e920e1; -constexpr hash_t system_io_compression_native_library_hash = 0x9190f4cb761b1d3c; -constexpr hash_t system_security_cryptography_native_android_library_hash = 0x1848c0093f0afd8; -constexpr hash_t system_globalization_native_library_hash = 0x28b5c8fca080abd5; -#else - //32-bit internal p/invoke table - std::array internal_pinvokes {{ - {0xb7a486a, "monodroid_TypeManager_get_java_class_name", reinterpret_cast(&monodroid_TypeManager_get_java_class_name)}, - {0x1bef8dce, "_monodroid_gref_inc", reinterpret_cast(&_monodroid_gref_inc)}, - {0x1f1e0ee9, "_monodroid_gref_dec", reinterpret_cast(&_monodroid_gref_dec)}, - {0x2aea7c33, "_monodroid_max_gref_get", reinterpret_cast(&_monodroid_max_gref_get)}, - {0x3227d81a, "monodroid_timing_start", reinterpret_cast(&monodroid_timing_start)}, - {0x333d4835, "_monodroid_lookup_replacement_method_info", reinterpret_cast(&_monodroid_lookup_replacement_method_info)}, - {0x39e5b5d4, "__android_log_print", reinterpret_cast(&__android_log_print)}, - {0x656e00bd, "clr_typemap_managed_to_java", reinterpret_cast(&clr_typemap_managed_to_java)}, - {0x9070e02c, "_monodroid_lref_log_delete", reinterpret_cast(&_monodroid_lref_log_delete)}, - {0x910452d0, "monodroid_timing_stop", reinterpret_cast(&monodroid_timing_stop)}, - {0x9a734f16, "_monodroid_weak_gref_get", reinterpret_cast(&_monodroid_weak_gref_get)}, - {0x9c5b24a8, "_monodroid_weak_gref_new", reinterpret_cast(&_monodroid_weak_gref_new)}, - {0xa04e5d1c, "monodroid_free", reinterpret_cast(&monodroid_free)}, - {0xa3c1e548, "clr_initialize_gc_bridge", reinterpret_cast(&clr_initialize_gc_bridge)}, - {0xb02468aa, "_monodroid_gref_get", reinterpret_cast(&_monodroid_gref_get)}, - {0xb6431f9a, "clr_typemap_java_to_managed", reinterpret_cast(&clr_typemap_java_to_managed)}, - {0xbe8d7701, "_monodroid_gref_log_new", reinterpret_cast(&_monodroid_gref_log_new)}, - {0xc0d097a7, "_monodroid_lref_log_new", reinterpret_cast(&_monodroid_lref_log_new)}, - {0xc439b5d7, "_monodroid_lookup_replacement_type", reinterpret_cast(&_monodroid_lookup_replacement_type)}, - {0xc5146c54, "_monodroid_gref_log_delete", reinterpret_cast(&_monodroid_gref_log_delete)}, - {0xe215a17c, "_monodroid_weak_gref_delete", reinterpret_cast(&_monodroid_weak_gref_delete)}, - {0xe7e77ca5, "_monodroid_gref_log", reinterpret_cast(&_monodroid_gref_log)}, - {0xe8d7ba9b, "_monodroid_weak_gref_inc", reinterpret_cast(&_monodroid_weak_gref_inc)}, - {0xea2184e3, "_monodroid_gc_wait_for_bridge_processing", reinterpret_cast(&_monodroid_gc_wait_for_bridge_processing)}, - {0xeac7f6e3, "xamarin_app_init", reinterpret_cast(&xamarin_app_init)}, - {0xeea5931c, "_monodroid_weak_gref_dec", reinterpret_cast(&_monodroid_weak_gref_dec)}, - {0xfa1832a6, "_monodroid_detect_cpu_and_architecture", reinterpret_cast(&_monodroid_detect_cpu_and_architecture)}, - {0xfa4e32ca, "monodroid_log", reinterpret_cast(&monodroid_log)}, - }}; - - -constexpr hash_t java_interop_library_hash = 0x6e36e350; -constexpr hash_t xa_internal_api_library_hash = 0x13c9bd62; -constexpr hash_t android_liblog_library_hash = 0xa70c9969; -constexpr hash_t system_native_library_hash = 0x5b9ade60; -constexpr hash_t system_io_compression_native_library_hash = 0xafe3142c; -constexpr hash_t system_security_cryptography_native_android_library_hash = 0x93625cd; -constexpr hash_t system_globalization_native_library_hash = 0xa66f1e5a; -#endif - -constexpr size_t internal_pinvokes_count = 28; -} // end of anonymous namespace diff --git a/src/native/clr/pinvoke-override/precompiled.cc b/src/native/clr/pinvoke-override/precompiled.cc index a57748bdb4e..0e1a1bf3c87 100644 --- a/src/native/clr/pinvoke-override/precompiled.cc +++ b/src/native/clr/pinvoke-override/precompiled.cc @@ -1,12 +1,126 @@ #define PINVOKE_OVERRIDE_INLINE [[gnu::always_inline]] +#include +#include + +#include + #include #include #include using namespace xamarin::android; -#include "pinvoke-tables.include" +namespace { + [[noreturn]] + void abort_missing_internal_symbol (std::string_view const& library_name, std::string_view const& entrypoint_name) + { + Helpers::abort_application ( + LOG_ASSEMBLY, + std::format ( + "Internal p/invoke symbol '{}'@'{}' not found"sv, + entrypoint_name, + library_name + ) + ); + } + + auto load_xa_internal_api_symbol (std::string_view const& entrypoint_name) -> void* + { + if (entrypoint_name == "_monodroid_detect_cpu_and_architecture"sv) { + return reinterpret_cast (&_monodroid_detect_cpu_and_architecture); + } + if (entrypoint_name == "monodroid_free"sv) { + return reinterpret_cast (&monodroid_free); + } + if (entrypoint_name == "_monodroid_gc_wait_for_bridge_processing"sv) { + return reinterpret_cast (&_monodroid_gc_wait_for_bridge_processing); + } + if (entrypoint_name == "_monodroid_gref_dec"sv) { + return reinterpret_cast (&_monodroid_gref_dec); + } + if (entrypoint_name == "_monodroid_gref_get"sv) { + return reinterpret_cast (&_monodroid_gref_get); + } + if (entrypoint_name == "_monodroid_gref_inc"sv) { + return reinterpret_cast (&_monodroid_gref_inc); + } + if (entrypoint_name == "_monodroid_gref_log"sv) { + return reinterpret_cast (&_monodroid_gref_log); + } + if (entrypoint_name == "_monodroid_gref_log_delete"sv) { + return reinterpret_cast (&_monodroid_gref_log_delete); + } + if (entrypoint_name == "_monodroid_gref_log_new"sv) { + return reinterpret_cast (&_monodroid_gref_log_new); + } + if (entrypoint_name == "monodroid_log"sv) { + return reinterpret_cast (&monodroid_log); + } + if (entrypoint_name == "_monodroid_lookup_replacement_type"sv) { + return reinterpret_cast (&_monodroid_lookup_replacement_type); + } + if (entrypoint_name == "_monodroid_lookup_replacement_method_info"sv) { + return reinterpret_cast (&_monodroid_lookup_replacement_method_info); + } + if (entrypoint_name == "_monodroid_lref_log_delete"sv) { + return reinterpret_cast (&_monodroid_lref_log_delete); + } + if (entrypoint_name == "_monodroid_lref_log_new"sv) { + return reinterpret_cast (&_monodroid_lref_log_new); + } + if (entrypoint_name == "_monodroid_max_gref_get"sv) { + return reinterpret_cast (&_monodroid_max_gref_get); + } + if (entrypoint_name == "monodroid_timing_start"sv) { + return reinterpret_cast (&monodroid_timing_start); + } + if (entrypoint_name == "monodroid_timing_stop"sv) { + return reinterpret_cast (&monodroid_timing_stop); + } + if (entrypoint_name == "monodroid_TypeManager_get_java_class_name"sv) { + return reinterpret_cast (&monodroid_TypeManager_get_java_class_name); + } + if (entrypoint_name == "clr_typemap_managed_to_java"sv) { + return reinterpret_cast (&clr_typemap_managed_to_java); + } + if (entrypoint_name == "clr_typemap_java_to_managed"sv) { + return reinterpret_cast (&clr_typemap_java_to_managed); + } + if (entrypoint_name == "clr_initialize_gc_bridge"sv) { + return reinterpret_cast (&clr_initialize_gc_bridge); + } + if (entrypoint_name == "_monodroid_weak_gref_dec"sv) { + return reinterpret_cast (&_monodroid_weak_gref_dec); + } + if (entrypoint_name == "_monodroid_weak_gref_delete"sv) { + return reinterpret_cast (&_monodroid_weak_gref_delete); + } + if (entrypoint_name == "_monodroid_weak_gref_get"sv) { + return reinterpret_cast (&_monodroid_weak_gref_get); + } + if (entrypoint_name == "_monodroid_weak_gref_inc"sv) { + return reinterpret_cast (&_monodroid_weak_gref_inc); + } + if (entrypoint_name == "_monodroid_weak_gref_new"sv) { + return reinterpret_cast (&_monodroid_weak_gref_new); + } + if (entrypoint_name == "xamarin_app_init"sv) { + return reinterpret_cast (&xamarin_app_init); + } + + abort_missing_internal_symbol ("xa-internal-api"sv, entrypoint_name); + } + + auto load_liblog_symbol (std::string_view const& entrypoint_name) -> void* + { + if (entrypoint_name == "__android_log_print"sv) { + return reinterpret_cast (&__android_log_print); + } + + abort_missing_internal_symbol ("liblog"sv, entrypoint_name); + } +} [[gnu::flatten]] auto PinvokeOverride::monodroid_pinvoke_override (const char *library_name, const char *entrypoint_name) noexcept -> void* @@ -15,31 +129,13 @@ auto PinvokeOverride::monodroid_pinvoke_override (const char *library_name, cons return nullptr; } - hash_t library_name_hash = xxhash::hash (library_name, strlen (library_name)); - hash_t entrypoint_hash = xxhash::hash (entrypoint_name, strlen (entrypoint_name)); - - if (library_name_hash == java_interop_library_hash || library_name_hash == xa_internal_api_library_hash || library_name_hash == android_liblog_library_hash) { - PinvokeEntry *entry = find_pinvoke_address (entrypoint_hash, internal_pinvokes.data (), internal_pinvokes_count); - - if (entry == nullptr) [[unlikely]] { - log_fatal (LOG_ASSEMBLY, "Internal p/invoke symbol '{} @ {}' (hash: {:x}) not found in compile-time map."sv, - optional_string (library_name), optional_string (entrypoint_name), entrypoint_hash); - log_fatal (LOG_ASSEMBLY, "compile-time map contents:"sv); - for (size_t i = 0uz; i < internal_pinvokes_count; i++) { - PinvokeEntry const& e = internal_pinvokes[i]; - log_fatal (LOG_ASSEMBLY, "\t'{}'={:p} (hash: {:x})"sv, optional_string (e.name), e.func, e.hash); - } - Helpers::abort_application ( - LOG_ASSEMBLY, - std::format ( - "Failure handling a p/invoke request for '{}'@'{}'"sv, - optional_string (entrypoint_name), - optional_string (library_name) - ) - ); - } + std::string_view library_name_view {library_name}; + std::string_view entrypoint_name_view {entrypoint_name}; - return entry->func; + if (library_name_view == "xa-internal-api"sv) { + return load_xa_internal_api_symbol (entrypoint_name_view); + } else if (library_name_view == "liblog"sv) { + return load_liblog_symbol (entrypoint_name_view); } // The .NET BCL native libraries (`libSystem.Native`, `libSystem.Globalization.Native`, @@ -54,25 +150,23 @@ auto PinvokeOverride::monodroid_pinvoke_override (const char *library_name, cons // we return `nullptr` for these libraries and let CoreCLR's own resolver handle them, removing // the whole class of drift bugs. // - // NOTE: this only affects the precompiled override used by the default separate-`.so` layout. - // The unified-DSO layout uses the generated `find_pinvoke` table in `dynamic.cc`, where these + // NOTE: this only affects the precompiled override used by the default separate-`.so` layout. The + // unified-DSO layout uses the generated `find_pinvoke` function in `dynamic.cc`, where these // symbols are hidden inside a single DSO and therefore must still be resolved by the override. - if (library_name_hash == system_native_library_hash || - library_name_hash == system_security_cryptography_native_android_library_hash || - library_name_hash == system_io_compression_native_library_hash || - library_name_hash == system_globalization_native_library_hash) { + if (library_name_view == "libSystem.Native"sv || + library_name_view == "libSystem.Security.Cryptography.Native.Android"sv || + library_name_view == "libSystem.IO.Compression.Native"sv || + library_name_view == "libSystem.Globalization.Native"sv) { return nullptr; } // Any other library (e.g. `e_sqlite3`, app-specific or third-party native libraries) is resolved // through dotnet/android's own loader. Unlike the BCL libraries above, this is NOT equivalent to - // returning `nullptr`: `handle_other_pinvoke_request` goes through `MonodroidDl::monodroid_dlopen`, - // which knows how to load DSOs embedded in the APK (`extractNativeLibs=false`), normalizes + // returning `nullptr`: `load_library_symbol` goes through `MonodroidDl::monodroid_dlopen`, which + // knows how to load DSOs embedded in the APK (`extractNativeLibs=false`), normalizes // `[DllImport ("log")]`/`[DllImport ("liblog")]`-style names, and consults the runtime's lib // directories. CoreCLR's default resolver does not replicate that behaviour, so this path is kept. - // It also carries no static table, so it is not subject to the drift problem that motivated the - // BCL change above. - return handle_other_pinvoke_request (library_name, library_name_hash, entrypoint_name, entrypoint_hash); + return load_library_symbol (library_name_view, entrypoint_name_view); } const void* Host::clr_pinvoke_override (const char *library_name, const char *entry_point_name) noexcept diff --git a/tests/Mono.Android-Tests/Mono.Android-Tests/Android.Runtime/PInvokeOverrideTests.cs b/tests/Mono.Android-Tests/Mono.Android-Tests/Android.Runtime/PInvokeOverrideTests.cs index 0d67252bfae..4e7231bfc9c 100644 --- a/tests/Mono.Android-Tests/Mono.Android-Tests/Android.Runtime/PInvokeOverrideTests.cs +++ b/tests/Mono.Android-Tests/Mono.Android-Tests/Android.Runtime/PInvokeOverrideTests.cs @@ -14,9 +14,9 @@ public class PInvokeOverrideTests [Test] public void CheckThirdPartySharedLibrariesWork () { - // We need to test if a library that is neither a .NET for Android internal one nor a standard DotNet one is properly cached in the robin_map p/invoke - // override cache. The only such library that we can count to always be present in the apk is `libmonosgen-2.0.so`. It doesn't matter which API we call, - // what it does and what returns as long as we hit the .NET for Android code path which uses the robin_map cache. If something doesn't work, the runtime will + // We need to test if a library that is neither a .NET for Android internal one nor a standard DotNet one is properly resolved by the p/invoke + // override. The only such library that we can count to always be present in the apk is `libmonosgen-2.0.so`. It doesn't matter which API we call, + // what it does and what returns as long as we hit the .NET for Android fallback path. If something doesn't work, the runtime will // crash, thus we don't need any asserts here. The API is called twice, because the first call hits a slightly different code path than the subsequent ones. mono_config_is_server_mode (); mono_config_is_server_mode ();