Skip to content

Commit d583b7c

Browse files
authored
[monodroid] Work around an Android 9 dlopen bug on x86 (#4914)
Fixes: #4893 The setup: 1. App with `AndroidManifest.xml` which sets `/application/@android:extractNativeLibs` to `false`, e.g.: [assembly:Application (ExtractNativeLibs=false)] 2. Build (1) as a *Release* app 3. Run (2) on an Android 9.0 (API-28) *x86* emulator (32-bit!) The result: app crash during process startup! E/mono(14126): Unhandled Exception: E/mono(14126): System.ArgumentNullException: Value cannot be null. E/mono(14126): Parameter name: key E/mono(14126): at System.Collections.Generic.Dictionary`2[TKey,TValue].FindEntry (TKey key) E/mono(14126): at System.Collections.Generic.Dictionary`2[TKey,TValue].TryGetValue (TKey key, TValue& value) E/mono(14126): at Java.Interop.TypeManager.RegisterType (System.String java_class, System.Type t) E/mono(14126): at Android.Runtime.JNIEnv.RegisterJniNatives (System.IntPtr typeName_ptr, System.Int32 typeName_len, System.IntPtr jniClass, System.IntPtr methods_ptr, System.Int32 methods_len) The cause of the crash is an apparent Android bionic bug, a'la: void* h1 = dlopen ("…path/to/libmonodroid.so", RTLD_LAZY); // time passses void* h2 = dlopen ("…path/to/libmonodroid.so", RTLD_LAZY); "Normal"/expected semantics are that `h1` is the same as `h2`, with the second `dlopen()` invocation effectively being a no-op, as the library has already been loaded. On 32-bit Android 9 -- but *only* when `extractNativeLibs`=false! -- what *instead* happens is that that native library is *re-loaded* such that `h1` != `h2` (potentially?!), *and* the global state of `h1` is cleared; the library is reinitialized. Where this comes in is with P/Invokes to `__Internal`, which we "redirect" to `libmonodroid.so`. However, since `dlopen(NULL)` has a history on Android of generating a SIGSEGV, we avoid the crash by explicitly loading `libmonodroid.so` every time we need to satisfy a request for `__Internal` (in `MonodroidRuntime::monodroid_dlopen()` when `name` is `nullptr`). As we re-load `libmonodroid.so` and `dlopen()` potentially *clears* global data, what happens is that certain class members initialized in `Runtime.initInternal()` become `nullptr`. In this particular case, `MonodroidRuntime::Class_getName` became null, which meant that we couldn't lookup Java class names, which caused the subsequent crash. The fix is to introduce a new `libxa-internal-api.so` which contains only three things of consequence: 1. Explicit "init" and "shutdown" exports: MONO_API bool _monodroid_init_internal_api (MonoAndroidInternalCalls *api); MONO_API bool _monodroid_shutdown_internal_api (void); 2. A pointer to a new `MonoAndroidInternalCalls` "interface", implemented in `libmonodroid.so`, and 3. Other "C" exports that `__Internal` needs to invoke, which are implemented by delegating through `MonoAndroidInternalCalls`. `libxa-internal-api.so` thus becomes the *only* library that we ever `dlopen()` more than once, and as we control when it's loaded, we can call `_monodroid_init_internal_api()` and `_monodroid_shutdown_internal_api()` as appropriate, ensuring that when we run on Android 9+x86+`extractNativeLibs`=false, the "damage" from subsequent `dlopen()` invocations is minimized.
1 parent d447aa6 commit d583b7c

20 files changed

+1001
-391
lines changed

build-tools/create-packs/Microsoft.Android.Runtime.proj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ projects that use the Microsoft.Android framework in .NET 5.
3838
<ItemGroup>
3939
<_AndroidRuntimePackAssets Include="$(XamarinAndroidSourcePath)bin\$(Configuration)\lib\xamarin.android\xbuild\Xamarin\Android\lib\$(AndroidABI)\libmono-android.debug.so" />
4040
<_AndroidRuntimePackAssets Include="$(XamarinAndroidSourcePath)bin\$(Configuration)\lib\xamarin.android\xbuild\Xamarin\Android\lib\$(AndroidABI)\libmono-android.release.so" />
41+
<_AndroidRuntimePackAssets Include="$(XamarinAndroidSourcePath)bin\$(Configuration)\lib\xamarin.android\xbuild\Xamarin\Android\lib\$(AndroidABI)\libxa-internal-api.so" />
4142
<_AndroidRuntimePackAssets Include="$(XamarinAndroidSourcePath)bin\$(Configuration)\lib\xamarin.android\xbuild\Xamarin\Android\lib\$(AndroidABI)\libsqlite3_xamarin.so" />
4243
<_AndroidRuntimePackAssets Include="$(XamarinAndroidSourcePath)bin\$(Configuration)\lib\xamarin.android\xbuild\Xamarin\Android\lib\$(AndroidABI)\libxamarin-debug-app-helper.so" />
4344
<FrameworkListFileClass Include="@(_AndroidRuntimePackAssets->'%(Filename)%(Extension)')" Profile="Android" />

build-tools/installers/create-installers.targets

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@
159159
<_MSBuildFiles Include="$(MSBuildSrcDir)\LayoutBinding.cs" />
160160
<_MSBuildFiles Include="@(AndroidSupportedTargetJitAbi->'$(MSBuildSrcDir)\lib\%(Identity)\libmono-android.debug.so')" Condition=" '$(PackageId)' != 'Microsoft.Android.Sdk' " />
161161
<_MSBuildFiles Include="@(AndroidSupportedTargetJitAbi->'$(MSBuildSrcDir)\lib\%(Identity)\libmono-android.release.so')" Condition=" '$(PackageId)' != 'Microsoft.Android.Sdk' " />
162+
<_MSBuildFiles Include="@(AndroidSupportedTargetJitAbi->'$(MSBuildSrcDir)\lib\%(Identity)\libxa-internal-api.so')" Condition=" '$(PackageId)' != 'Microsoft.Android.Sdk' " />
162163
<_MSBuildFiles Include="@(AndroidSupportedTargetJitAbi->'$(MSBuildSrcDir)\lib\%(Identity)\libmono-btls-shared.so')" Condition=" '$(PackageId)' != 'Microsoft.Android.Sdk' " />
163164
<_MSBuildFiles Include="@(AndroidSupportedTargetJitAbi->'$(MSBuildSrcDir)\lib\%(Identity)\libmono-profiler-aot.so')" Condition=" '$(PackageId)' != 'Microsoft.Android.Sdk' " />
164165
<_MSBuildFiles Include="@(AndroidSupportedTargetJitAbi->'$(MSBuildSrcDir)\lib\%(Identity)\libmono-profiler-log.so')" Condition=" '$(PackageId)' != 'Microsoft.Android.Sdk' " />
@@ -294,6 +295,7 @@
294295
<_MSBuildLibHostFilesWin Include="$(MSBuildSrcDir)\lib\host-mxe-Win64\libMonoPosixHelper.dll" />
295296
<_MSBuildLibHostFilesWin Include="$(MSBuildSrcDir)\lib\host-mxe-Win64\libmonosgen-2.0.dll" />
296297
<_MSBuildLibHostFilesWin Include="$(MSBuildSrcDir)\lib\host-mxe-Win64\libxamarin-app.dll" Condition=" '$(HostOS)' != 'Windows' " />
298+
<_MSBuildLibHostFilesWin Include="$(MSBuildSrcDir)\lib\host-mxe-Win64\libxa-internal-api.dll" Condition=" '$(HostOS)' != 'Windows' " />
297299
</ItemGroup>
298300
<ItemDefinitionGroup>
299301
<_MSBuildFilesUnixSign>
@@ -333,6 +335,7 @@
333335
<_MSBuildFilesUnixSign Include="$(MSBuildSrcDir)\lib\host-$(HostOS)\libMonoPosixHelper.$(LibExtension)" Condition=" '$(PackageId)' != 'Microsoft.Android.Sdk' " />
334336
<_MSBuildFilesUnixSign Include="$(MSBuildSrcDir)\lib\host-$(HostOS)\libmonosgen-2.0.$(LibExtension)" Condition=" '$(PackageId)' != 'Microsoft.Android.Sdk' " />
335337
<_MSBuildFilesUnixSign Include="$(MSBuildSrcDir)\lib\host-$(HostOS)\libxamarin-app.$(LibExtension)" Condition=" '$(PackageId)' != 'Microsoft.Android.Sdk' " />
338+
<_MSBuildFilesUnixSign Include="$(MSBuildSrcDir)\lib\host-$(HostOS)\libxa-internal-api.$(LibExtension)" Condition=" '$(PackageId)' != 'Microsoft.Android.Sdk' " />
336339
<_MSBuildFilesUnixSign Include="$(MSBuildSrcDir)\libzip.$(LibExtension)" />
337340
<_MSBuildFilesUnix Include="$(MSBuildSrcDir)\proguard\bin\proguard.sh" />
338341
</ItemGroup>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.xamarin.android.helloworld">
33
<uses-sdk android:minSdkVersion="21" />
4-
<application android:allowBackup="true" android:icon="@mipmap/icon" android:label="@string/app_name">
4+
<application android:allowBackup="true" android:icon="@mipmap/icon" android:label="@string/app_name" android:extractNativeLibs="false">
55
</application>
66
</manifest>

src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,6 +1890,7 @@ because xbuild doesn't support framework reference assemblies.
18901890
<_AndroidNativeLibraryForFastDev Condition=" '$(_InstantRunEnabled)' == 'True' And '$(AndroidUseDebugRuntime)' == 'True' " Include="%(_TargetLibDir.Identity)\libxamarin-debug-app-helper.so" />
18911891
<FrameworkNativeLibrary Include="%(_TargetLibDir.Identity)\libmono-android.debug.so" Condition=" '$(AndroidIncludeDebugSymbols)' == 'True' " ArchiveFileName="libmonodroid.so" />
18921892
<FrameworkNativeLibrary Include="%(_TargetLibDir.Identity)\libmono-android.release.so" Condition=" '$(AndroidIncludeDebugSymbols)' != 'True' " ArchiveFileName="libmonodroid.so" />
1893+
<FrameworkNativeLibrary Include="%(_TargetLibInterpreterDir.Identity)\libxa-internal-api.so" />
18931894
<FrameworkNativeLibrary Include="%(_TargetLibInterpreterDir.Identity)\libmono-btls-shared.so" />
18941895
<FrameworkNativeLibrary Include="%(_TargetLibInterpreterDir.Identity)\libmono-profiler-aot.so" Condition=" @(_EmbedProfilers->Count()) > 0 and (@(_EmbedProfilers->AnyHaveMetadataValue('Identity', 'aot')) or @(_EmbedProfilers->AnyHaveMetadataValue('Identity', 'all'))) " />
18951896
<FrameworkNativeLibrary Include="%(_TargetLibInterpreterDir.Identity)\libmono-profiler-log.so" Condition=" '$(AndroidIncludeDebugSymbols)' == 'True' or (@(_EmbedProfilers->Count()) > 0 and (@(_EmbedProfilers->AnyHaveMetadataValue('Identity', 'log')) or @(_EmbedProfilers->AnyHaveMetadataValue('Identity', 'all')))) " />

src/monodroid/CMakeLists.txt

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,8 @@ else()
228228
endif()
229229
endif()
230230

231+
string(STRIP "${EXTRA_LINKER_FLAGS}" EXTRA_LINKER_FLAGS)
232+
231233
if (ENABLE_TIMING)
232234
add_definitions("-DMONODROID_TIMING=1")
233235
endif()
@@ -285,11 +287,10 @@ set(MONODROID_SOURCES
285287
${SOURCES_DIR}/debug-constants.cc
286288
${SOURCES_DIR}/embedded-assemblies.cc
287289
${SOURCES_DIR}/embedded-assemblies-zip.cc
288-
${SOURCES_DIR}/external-api.cc
289290
${SOURCES_DIR}/globals.cc
290-
${SOURCES_DIR}/jni.c
291291
${SOURCES_DIR}/logger.cc
292292
${SOURCES_DIR}/monodroid-glue.cc
293+
${SOURCES_DIR}/xa-internal-api.cc
293294
${SOURCES_DIR}/osbridge.cc
294295
${SOURCES_DIR}/shared-constants.cc
295296
${SOURCES_DIR}/timezones.cc
@@ -299,6 +300,12 @@ set(MONODROID_SOURCES
299300
${JAVA_INTEROP_SRC_PATH}/java-interop-util.cc
300301
)
301302

303+
set(XA_INTERNAL_API_SOURCES
304+
${SOURCES_DIR}/jni.c
305+
${SOURCES_DIR}/internal-pinvoke-api.cc
306+
${JAVA_INTEROP_SRC_PATH}/java-interop-util.cc
307+
)
308+
302309
if(ENABLE_NDK)
303310
set(MONODROID_SOURCES
304311
${MONODROID_SOURCES}
@@ -314,6 +321,15 @@ if(UNIX)
314321
)
315322
endif()
316323

324+
add_library(xa-internal-api SHARED ${XA_INTERNAL_API_SOURCES})
325+
target_link_libraries(xa-internal-api ${EXTRA_LINKER_FLAGS})
326+
set_target_properties(
327+
xa-internal-api
328+
PROPERTIES
329+
COMPILE_FLAGS -fvisibility=default
330+
LINK_FLAGS -fvisibility=default
331+
)
332+
317333
set(XAMARIN_APP_STUB_SOURCES ${SOURCES_DIR}/application_dso_stub.cc)
318334
add_library(xamarin-app SHARED ${XAMARIN_APP_STUB_SOURCES})
319335
if (ENABLE_NDK)

src/monodroid/jni/cpu-arch-detect.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ get_running_on_cpu (unsigned short *running_on_cpu)
225225
*running_on_cpu = CPU_KIND_UNKNOWN;
226226
}
227227

228-
MONO_API void
228+
void
229229
_monodroid_detect_cpu_and_architecture (unsigned short *built_for_cpu, unsigned short *running_on_cpu, unsigned char *is64bit)
230230
{
231231
assert (built_for_cpu);

src/monodroid/jni/embedded-assemblies.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -597,7 +597,7 @@ EmbeddedAssemblies::md_mmap_apk_file (int fd, uint32_t offset, uint32_t size, co
597597
md_mmap_info file_info;
598598
md_mmap_info mmap_info;
599599

600-
size_t pageSize = static_cast<size_t>(monodroid_getpagesize());
600+
size_t pageSize = static_cast<size_t>(utils.monodroid_getpagesize ());
601601
uint32_t offsetFromPage = static_cast<uint32_t>(offset % pageSize);
602602
uint32_t offsetPage = offset - offsetFromPage;
603603
uint32_t offsetSize = size + offsetFromPage;

0 commit comments

Comments
 (0)