Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e6d636a

Browse files
committed
JNI on-load initialization of libraries
1 parent 434a98d commit e6d636a

File tree

5 files changed

+92
-11
lines changed

5 files changed

+92
-11
lines changed

samples/NativeAOT/MainActivity.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
using Android.Util;
44
using System.Reflection;
55
using System.Runtime.InteropServices;
6+
using System.Security.Cryptography;
7+
using System.Text;
68

79
namespace NativeAOT;
810

@@ -27,5 +29,9 @@ protected override void OnCreate(Bundle? savedInstanceState)
2729
if (t) {
2830
throw new InvalidOperationException ("What happened?");
2931
}
32+
33+
// This tests loading and initializing JNI shared libraries
34+
var hashData = new byte[32];
35+
SHA256.HashData(Encoding.ASCII.GetBytes("Hello World"), hashData.AsSpan());
3036
}
3137
}

src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,16 +134,48 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
134134
</LinkNativeDependsOn>
135135
</PropertyGroup>
136136

137-
<Target
138-
Name="_GenerateNativeAotAndroidAppAssemblerSources">
137+
<Target Name="_PrepareNativeAotAndroidAppInputs">
138+
<ItemGroup>
139+
<_PrivateBuildTargetAbi Condition=" '$(RuntimeIdentifier)' == 'android-arm64' " Include="arm64-v8a" />
140+
<_PrivateBuildTargetAbi Condition=" '$(RuntimeIdentifier)' == 'android-x64' " Include="x86_64" />
141+
</ItemGroup>
142+
143+
<PrepareAbiItems
144+
BuildTargetAbis="@(_PrivateBuildTargetAbi)"
145+
NativeSourcesDir="$(_NativeAssemblySourceDir)"
146+
Debug="$(AndroidIncludeDebugSymbols)"
147+
Mode="jni_init">
148+
<Output TaskParameter="AssemblySources" ItemName="_JniInitFuncsAssemblySource" />
149+
</PrepareAbiItems>
150+
139151
<ItemGroup>
152+
<_PrivateJniInitFuncsNativeObjectFile Include="@(_JniInitFuncsAssemblySource->'$([System.IO.Path]::ChangeExtension('%(Identity)', '.o'))')">
153+
<abi>%(_JniInitFuncsAssemblySource.abi)</abi>
154+
</_PrivateJniInitFuncsNativeObjectFile>
155+
140156
<_PrivateAndroidNaotResolvedAssemblyFiles Include="@(ResolvedFileToPublish->Distinct())" Condition=" '%(ResolvedFileToPublish.Extension)' == '.dll' " />
141157
</ItemGroup>
158+
</Target>
142159

160+
<Target
161+
Name="_GenerateNativeAotAndroidAppAssemblerSources"
162+
DependsOnTargets="_PrepareNativeAotAndroidAppInputs"
163+
Inputs="@(_PrivateAndroidNaotResolvedAssemblyFiles)"
164+
Outputs="@(_JniInitFuncsAssemblySource);@(_PrivateJniInitFuncsNativeObjectFile)">
143165
<GenerateNativeAotLibraryLoadAssemblerSources
144166
ResolvedAssemblies="@(_PrivateAndroidNaotResolvedAssemblyFiles)"
145-
CustomJniInitFunctions="@(AndroidNativeAotJniInitFunction)"
146-
SourcesOutputDirectory="$(IntermediateOutputPath)android" />
167+
CustomJniInitFunctions="@(AndroidStaticJniInitFunction)"
168+
OutputSources="@(_JniInitFuncsAssemblySource)" />
169+
170+
<CompileNativeAssembly
171+
Sources="@(_JniInitFuncsAssemblySource)"
172+
DebugBuild="$(AndroidIncludeDebugSymbols)"
173+
WorkingDirectory="$(_NativeAssemblySourceDir)"
174+
AndroidBinUtilsDirectory="$(AndroidBinUtilsDirectory)" />
175+
176+
<ItemGroup>
177+
<LinkerArg Include="@(_PrivateJniInitFuncsNativeObjectFile)" />
178+
</ItemGroup>
147179

148180
<!-- TODO: we need environment files here too -->
149181
</Target>

src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeAotLibraryLoadAssemblerSources.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public class GenerateNativeAotLibraryLoadAssemblerSources : AndroidTask
3838
public ITaskItem[] ResolvedAssemblies { get; set; } = [];
3939

4040
[Required]
41-
public string SourcesOutputDirectory { get; set; } = "";
41+
public ITaskItem[] OutputSources { get; set; } = [];
4242

4343
// Names of JNI initialization functions in 3rd party libraries. The
4444
// functions are REQUIRED to use the `JNI_OnLoad(JNIEnv*, void* reserved)` signature.
@@ -53,8 +53,24 @@ public override bool RunTask ()
5353

5454
// We run in the inner build, there's going to be just a single RID
5555
string rid = MonoAndroidHelper.GetAssemblyRid (ResolvedAssemblies[0]);
56+
string abi = MonoAndroidHelper.RidToAbi (rid);
5657
AndroidTargetArch targetArch = MonoAndroidHelper.RidToArch (rid);
5758

59+
// There can be only one, since we run in the inner build
60+
ITaskItem? outputFile = null;
61+
foreach (ITaskItem item in OutputSources) {
62+
string itemAbi = MonoAndroidHelper.GetAssemblyAbi (item);
63+
if (MonoAndroidHelper.StringEquals (abi, itemAbi, StringComparison.OrdinalIgnoreCase)) {
64+
outputFile = item;
65+
break;
66+
}
67+
}
68+
69+
if (outputFile == null) {
70+
throw new InvalidOperationException ($"Internal error: no output file found for ABI '{abi}' (RID '{rid}')");
71+
}
72+
Log.LogDebugMessage ($"JNI init funcs file to generate: {outputFile.ItemSpec}");
73+
5874
var assemblies = new Dictionary<string, ITaskItem> (StringComparer.OrdinalIgnoreCase);
5975
foreach (ITaskItem item in ResolvedAssemblies) {
6076
string name = MonoAndroidHelper.GetAssemblyNameWithCulture (item);
@@ -130,7 +146,7 @@ public override bool RunTask ()
130146
}
131147
}
132148

133-
var jniInitFuncsLlFilePath = Path.Combine (SourcesOutputDirectory, $"jni_init_funcs.{MonoAndroidHelper.RidToAbi (rid)}.ll");
149+
string jniInitFuncsLlFilePath = outputFile.ItemSpec;
134150
var generator = new NativeAotDsoLoadNativeAssemblyGenerator (Log, bclInitFunctions, customInitFunctions);
135151
LLVMIR.LlvmIrModule jniInitFuncsModule = generator.Construct ();
136152
using var jniInitFuncsWriter = MemoryStreamPool.Shared.CreateStreamWriter ();

src/Xamarin.Android.Build.Tasks/Tasks/PrepareAbiItems.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public class PrepareAbiItems : AndroidTask
1818
const string JniRemappingBase = "jni_remap";
1919
const string MarshalMethodsBase = "marshal_methods";
2020
const string PinvokePreserveBase = "pinvoke_preserve";
21+
const string JniInitFuncsBase = "jni_init_funcs";
2122

2223
public override string TaskPrefix => "PAI";
2324

@@ -57,6 +58,8 @@ public override bool RunTask ()
5758
baseName = MarshalMethodsBase;
5859
} else if (MonoAndroidHelper.StringEquals ("runtime_linking", Mode, StringComparison.OrdinalIgnoreCase)) {
5960
baseName = PinvokePreserveBase;
61+
} else if (MonoAndroidHelper.StringEquals ("jni_init", Mode, StringComparison.OrdinalIgnoreCase)) {
62+
baseName = JniInitFuncsBase;
6063
} else {
6164
Log.LogError ($"Unknown mode: {Mode}");
6265
return false;

src/native/nativeaot/host/host.cc

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,48 @@
66

77
using namespace xamarin::android;
88

9-
auto HostCommon::Java_JNI_OnLoad (JavaVM *vm, [[maybe_unused]] void *reserved) noexcept -> jint
9+
using JniOnLoadHandler = jint (*) (JavaVM *vm, void *reserved);
10+
11+
//
12+
// These external functions are generated during application build (see obj/${CONFIG}/${FRAMEWORK}-android/${RID}/android/jni_init_funcs*.ll)
13+
//
14+
extern "C" {
15+
extern const uint32_t __jni_on_load_handler_count;
16+
extern const JniOnLoadHandler __jni_on_load_handlers[];
17+
extern const char* __jni_on_load_handler_names[];
18+
}
19+
20+
auto HostCommon::Java_JNI_OnLoad (JavaVM *vm, void *reserved) noexcept -> jint
1021
{
22+
Logger::init_logging_categories ();
23+
1124
log_warn (LOG_ASSEMBLY, "{}", __PRETTY_FUNCTION__);
1225

1326
jvm = vm;
1427

1528
JNIEnv *env = nullptr;
16-
vm->GetEnv ((void**)&env, JNI_VERSION_1_6);
17-
OSBridge::initialize_on_onload (vm, env);
18-
GCBridge::initialize_on_onload (env);
29+
vm->GetEnv ((void**)&env, JNI_VERSION_1_6);
30+
OSBridge::initialize_on_onload (vm, env);
31+
GCBridge::initialize_on_onload (env);
32+
33+
if (__jni_on_load_handler_count > 0) {
34+
for (uint32_t i = 0; i < __jni_on_load_handler_count; i++) {
35+
log_debug (
36+
LOG_ASSEMBLY,
37+
"Calling JNI on-load init func '{}' ({:p})",
38+
optional_string (__jni_on_load_handler_names[i]),
39+
reinterpret_cast<void*>(__jni_on_load_handlers[i])
40+
);
41+
__jni_on_load_handlers[i] (vm, reserved);
42+
}
43+
}
1944

2045
return JNI_VERSION_1_6;
2146
}
2247

2348
void Host::OnInit () noexcept
2449
{
2550
log_warn (LOG_ASSEMBLY, "{}", __PRETTY_FUNCTION__);
26-
Logger::init_logging_categories ();
2751

2852
JNIEnv *env = OSBridge::ensure_jnienv ();
2953
jclass runtimeClass = env->FindClass ("mono/android/Runtime");

0 commit comments

Comments
 (0)