[CoreCLR] A library in `AndroidNativeLibraryNoJniPreload` is loaded with `System.loadLibrary` from the main thread on its first p/invoke, which fails 4 times in the system namespace before the call binds
维护者通常 1 天内回复
还没有人认领这个 Issue。
评估
- 难度
- 5/5
- 预计耗时
- 一周以上
- 新手友好度
- 38/100
- Issue 类型
- 缺陷
- 描述清晰度
- 基本清楚
- 活跃度
- 活跃
- 技术栈
- android, csharp
调研方向
Start by tracing CoreCLR’s Android P/Invoke library-loading path, especially the MainThreadDsoLoader and SystemLoadLibraryWrapper::load entry points identified in the report. Reproduce with the supplied AndroidNativeLibraryNoJniPreload project on Android arm64 and inspect the first repro_add call and its log output. Done means the already loaded app library binds without failed system-namespace loads or repeated delays.
由索引模型根据 Issue 内容生成。
描述
Android framework version
net11.0-android (Preview)
Affected platform version
.NET SDK 11.0.100-rc.2.26504.105, Android workload 37.2.0-rc.2.84, CoreCLR (UseMonoRuntime=false), Release, android-arm64; Android 17 (API 37) emulator
Description
On CoreCLR, an app's own native library that is listed in AndroidNativeLibraryNoJniPreload is not found by the first [DllImport] into it. Before the call binds, the runtime posts a load of the library to the main thread, which calls java.lang.System.loadLibrary. There the caller is the framework (caller=/system/framework/framework.jar), so the library is looked for in the system linker namespace, not the app's class-loader namespace, and it fails 4 times. Each failure is logged with its stack. Then monodroid-assembly warns "Shared library '...' not loaded, p/invoke '...' may fail", and the call is bound some other way and works.
This happens whether or not the app has already loaded the library itself with System.loadLibrary from its activity, which is what such an app does.
Why we list a library there: Firebase's C++ library (libFirebaseCppApp) reads the activity in its JNI_OnLoad, so it must not be preloaded as the process starts. The app loads it from its activity instead. With its ~37 p/invokes, an app gets ~150 UnsatisfiedLinkError stacks (half its logcat) and the main thread is held at every first call.
Cost in the repro below: the first call takes 528-896 ms, against 0.9 ms when the library is preloaded.
Expected
The first repro_add call binds to the app's lib/arm64/libreprolib.so (already loaded in the java_first true case) with no failed loads, as when the library is preloaded (-p:ReproPreload=true: 0 failures, first call 0.9 ms).
Notes
From the runtime's own strings (libnet-android.release.so: MainThreadDsoLoader, "Looper CB called on thread %d. Will attempt to load DSO", SystemLoadLibraryWrapper::load), the lookup goes through a load posted to the main thread's looper. Called from a looper callback, System.loadLibrary has no app frame on the stack, so the library is resolved in the system namespace, where an app's library is never found. Calling Runtime.loadLibrary0 with the app's class loader (or dlopen of the library's path in the app's native library directory) would find it. The lookup also doesn't notice that the app has already loaded the library. Because the warning path returns no handle, every first call of each import pays again.
Steps to Reproduce
#include <jni.h>
#include <android/log.h>
int repro_add(int a, int b) { return a + b; }
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
__android_log_print(ANDROID_LOG_INFO, "Repro", "libreprolib JNI_OnLoad");
return JNI_VERSION_1_6;
}
dotnet new android, with this project file:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net11.0-android</TargetFramework>
<SupportedOSPlatformVersion>24</SupportedOSPlatformVersion>
<OutputType>Exe</OutputType>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<ApplicationId>com.example.nojnipreloadrepro</ApplicationId>
<ApplicationVersion>1</ApplicationVersion>
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<UseMonoRuntime>false</UseMonoRuntime>
<RuntimeIdentifiers>android-arm64</RuntimeIdentifiers>
</PropertyGroup>
<ItemGroup>
<AndroidNativeLibrary Include="../native/arm64-v8a/libreprolib.so" Abi="arm64-v8a" />
<AndroidNativeLibraryNoJniPreload Include="libreprolib.so" Condition="'$(ReproPreload)' != 'true'" />
</ItemGroup>
</Project>
and this activity:
using System.Runtime.InteropServices;
using Android.Util;
namespace NoJniPreloadRepro;
[Activity(Label = "@string/app_name", MainLauncher = true)]
public class MainActivity : Activity
{
[DllImport("reprolib")]
private static extern int repro_add(int a, int b);
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.activity_main);
// --ez java_first true: the app loads the library itself first, as one whose JNI_OnLoad needs the activity does.
if (Intent?.GetBooleanExtra("java_first", false) == true)
{
Java.Lang.JavaSystem.LoadLibrary("reprolib");
Log.Info("Repro", "loaded libreprolib through Java (System.loadLibrary)");
}
// The first p/invoke, from a thread of the app's own.
new Thread(() =>
{
var watch = System.Diagnostics.Stopwatch.StartNew();
var sum = repro_add(2, 3);
Log.Info("Repro", $"repro_add(2, 3) = {sum}, first call took {watch.Elapsed.TotalMilliseconds:0.0} ms");
}).Start();
}
}
Did you find any workaround?
No response
Relevant log output
D nativeloader: Load libreprolib.so using system ns (caller=/system/framework/framework.jar!classes3.dex): dlopen failed: library "libreprolib.so" not found
W System.err: java.lang.UnsatisfiedLinkError: dlopen failed: library "libreprolib.so" not found
W System.err: at java.lang.Runtime.loadLibrary0(Runtime.java:1122)
W System.err: at java.lang.Runtime.loadLibrary0(Runtime.java:1033)
W System.err: at java.lang.System.loadLibrary(System.java:1765)
W System.err: at android.os.MessageQueue.nativePollOnce(Native Method)
W System.err: at android.os.MessageQueue.next(MessageQueue.java:773)
W System.err: at android.os.Looper.loop(Looper.java:397)
W System.err: at android.app.ActivityThread.main(ActivityThread.java:9523)
... (4 times, on the main thread)
W monodroid-assembly: Shared library 'reprolib' not loaded, p/invoke 'repro_add' may fail (on the calling thread)
I Repro : repro_add(2, 3) = 5, first call took 895.6 ms
- 主要语言
- C#
- 星标
- 2.1k
- 派生
- 579
- 平均合并
- 1 天 9 小时
- 30 天内合并 PR
- 220
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 没有贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
dotnet/android 的其他 Issue
-
Area: App+Library Build
难度 2/5 1-3 小时 新手友好度 72/100
维护者通常 1 天内回复
-
Area: Mono.Android
难度 2/5 1-3 小时 新手友好度 65/100
dotnet/android#9192 · 4 条评论 · 1 个 reaction ·
维护者通常 1 天内回复
-
automated needs-triage skill-runner
难度 1/5 1 小时以内 新手友好度 15/100
维护者通常 1 天内回复
-
Area: App Runtime bug
难度 2/5 1-3 小时 新手友好度 58/100
dotnet/android#13253 · 4 条评论 ·
维护者通常 1 天内回复
-
Area: App+Library Build enhancement
难度 5/5 一周以上 新手友好度 35/100
维护者通常 1 天内回复
相似的 Issue
-
bug
难度 2/5 1-3 小时 新手友好度 67/100
Tyrrrz/YoutubeDownloader#912 ·
-
难度 2/5 1-3 小时 新手友好度 66/100
-
难度 2/5 1-3 小时 新手友好度 74/100
-
README says main uses .NET 9 while the current branch targets .NET 10可能已有人在做 @dajiaohuang 今天认领。 未关闭
难度 1/5 1 小时以内 新手友好度 94/100
ardalis/CleanArchitecture#1098 ·
-
good first issue
难度 1/5 1 小时以内 新手友好度 88/100
madeyoga/AuthEndpoints#365 ·
维护者通常 1 天内回复