Skip to content

Commit 6776200

Browse files
committed
Let's see if this works
1 parent 71749a9 commit 6776200

File tree

9 files changed

+114
-17
lines changed

9 files changed

+114
-17
lines changed

src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Collections;
44
using System.Collections.Generic;
55
using System.Globalization;
6+
using System.Reflection;
67
using System.IO;
78
using System.Text;
89

@@ -248,6 +249,12 @@ void WriteGlobalVariables (GeneratorWriteContext context)
248249
}
249250
}
250251

252+
void WriteVariableReference (GeneratorWriteContext context, LlvmIrVariableReference variable)
253+
{
254+
context.Output.Write (variable.Global ? '@' : '%');
255+
context.Output.Write (variable.Name);
256+
}
257+
251258
void WriteGlobalVariableStart (GeneratorWriteContext context, LlvmIrGlobalVariable variable)
252259
{
253260
if (!String.IsNullOrEmpty (variable.Comment)) {
@@ -355,6 +362,10 @@ ulong GetAggregateValueElementCount (GeneratorWriteContext context, Type type, o
355362
return (uint)((ICollection)value).Count;
356363
}
357364

365+
if (type.ImplementsInterface (typeof(ICollection<>))) {
366+
return (ulong)GetCollectionOfTCount (type, value);
367+
}
368+
358369
throw new InvalidOperationException ($"Internal error: should never get here");
359370
}
360371

@@ -732,6 +743,11 @@ public void WriteValue (GeneratorWriteContext context, Type type, object? value)
732743
throw new NotSupportedException ($"Internal error: array of type {type} is unsupported");
733744
}
734745

746+
if (type == typeof (LlvmIrVariableReference) || type.IsSubclassOf (typeof (LlvmIrVariableReference))) {
747+
WriteVariableReference (context, (LlvmIrVariableReference)value);
748+
return;
749+
}
750+
735751
throw new NotSupportedException ($"Internal error: value type '{type}' is unsupported");
736752
}
737753

@@ -940,8 +956,19 @@ void WriteArrayValue (GeneratorWriteContext context, LlvmIrVariable variable)
940956
list.Add (kvp.Value);
941957
}
942958
entries = list;
943-
} else {
959+
} else if (variable.Type.ImplementsInterface (typeof (ICollection))) {
944960
entries = (ICollection)variable.Value;
961+
} else if (variable.Type.ImplementsInterface (typeof (ICollection<>))) {
962+
// This is slow and messy, but should work for a wide range of types without us having to add
963+
// any explicit support
964+
Type elementType = variable.Type.GetArrayElementType ();
965+
int elementCount = GetCollectionOfTCount (variable.Type, variable.Value);
966+
Array array = Array.CreateInstance (elementType, elementCount);
967+
MethodInfo copyTo = variable.Type.GetMethod ("CopyTo", new Type[] { array.GetType (), typeof (int) });
968+
copyTo.Invoke (variable.Value, new object[] { array, (int)0 });
969+
entries = array;
970+
} else {
971+
throw new NotSupportedException ($"Unsupported array value type '{variable.Type}'");
945972
}
946973

947974
if (entries.Count == 0) {
@@ -1617,5 +1644,21 @@ public static string QuoteString (byte[] bytes, int byteCount, out ulong stringS
16171644

16181645
return QuoteStringNoEscape (sb.ToString ());
16191646
}
1647+
1648+
static int GetCollectionOfTCount (Type type, object instance)
1649+
{
1650+
if (!type.ImplementsInterface (typeof (ICollection<>))) {
1651+
throw new ArgumentException ("Must implement the ICollection<T> interface", nameof (type));
1652+
}
1653+
1654+
PropertyInfo countProperty = type.GetProperty ("Count");
1655+
var ret = (int)countProperty.GetValue (instance);
1656+
1657+
if (ret < 0) {
1658+
throw new InvalidOperationException ($"Collection count is negative: {ret}");
1659+
}
1660+
1661+
return ret;
1662+
}
16201663
}
16211664
}

src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/TypeUtilities.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ public static bool IsArray (this Type t)
178178

179179
// TODO: cache results here
180180
// IDictionary<string, string> is a special case for name:value string arrays which we use for some constructs.
181-
return (t.ImplementsInterface (typeof(ICollection<>)) && t.ImplementsInterface (typeof(ICollection))) ||
181+
return (t.ImplementsInterface (typeof(ICollection<>)) || t.ImplementsInterface (typeof(ICollection))) ||
182182
t.ImplementsInterface (typeof(IDictionary<string, string>));
183183
}
184184
}

src/Xamarin.Android.Build.Tasks/Utilities/NativeRuntimeComponents.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,19 @@ class NativeRuntimeComponents
1010
internal class Archive
1111
{
1212
public readonly string Name;
13+
public readonly string JniOnLoadName;
1314
public bool Include => shouldInclude (this);
1415
public readonly bool WholeArchive;
1516
public bool DontExportSymbols { get; set; }
1617

1718
Func<Archive, bool> shouldInclude;
1819

19-
public Archive (string name, Func<Archive, bool>? include = null, bool wholeArchive = false)
20+
public Archive (string name, Func<Archive, bool>? include = null, bool wholeArchive = false, string? jniOnLoadName = null)
2021
{
2122
Name = name;
2223
shouldInclude = include == null ? ((Archive arch) => true) : include;
2324
WholeArchive = wholeArchive;
25+
JniOnLoadName = jniOnLoadName;
2426
}
2527
}
2628

@@ -52,8 +54,8 @@ public AndroidArchive (string name)
5254

5355
sealed class BclArchive : Archive
5456
{
55-
public BclArchive (string name, bool wholeArchive = false)
56-
: base (name, wholeArchive: wholeArchive)
57+
public BclArchive (string name, bool wholeArchive = false, string? jniOnLoadName = null)
58+
: base (name, wholeArchive: wholeArchive, jniOnLoadName: jniOnLoadName)
5759
{
5860
DontExportSymbols = true;
5961
}
@@ -87,7 +89,7 @@ public NativeRuntimeComponents (ITaskItem[] monoComponents)
8789
new BclArchive ("libSystem.Globalization.Native.a"),
8890
new BclArchive ("libSystem.IO.Compression.Native.a"),
8991
new BclArchive ("libSystem.Native.a"),
90-
new BclArchive ("libSystem.Security.Cryptography.Native.Android.a"),
92+
new BclArchive ("libSystem.Security.Cryptography.Native.Android.a", jniOnLoadName: "AndroidCryptoNative_InitLibraryOnLoad"),
9193

9294
// .NET for Android
9395
new AndroidArchive ("libpinvoke-override-dynamic-release.a"),

src/Xamarin.Android.Build.Tasks/Utilities/PreservePinvokesNativeAssemblyGenerator.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ protected override void Construct (LlvmIrModule module)
100100
}
101101

102102
Log.LogDebugMessage (" Looking for enabled native components");
103-
var componentNames = new List<string> ();
103+
var componentNames = new HashSet<string> (StringComparer.Ordinal);
104+
var jniOnLoadNames = new HashSet<string> (StringComparer.Ordinal);
104105
var nativeComponents = new NativeRuntimeComponents (monoComponents);
105106
foreach (NativeRuntimeComponents.Archive archiveItem in nativeComponents.KnownArchives) {
106107
if (!archiveItem.Include) {
@@ -109,13 +110,28 @@ protected override void Construct (LlvmIrModule module)
109110

110111
Log.LogDebugMessage ($" {archiveItem.Name}");
111112
componentNames.Add (archiveItem.Name);
113+
if (!String.IsNullOrEmpty (archiveItem.JniOnLoadName)) {
114+
jniOnLoadNames.Add (archiveItem.JniOnLoadName);
115+
}
112116
}
113117

114118
if (componentNames.Count == 0) {
115119
Log.LogDebugMessage ("No native framework components are included in the build, not scanning for p/invoke usage");
116120
return;
117121
}
118122

123+
module.AddGlobalVariable ("__jni_on_load_handler_count", (uint)jniOnLoadNames.Count, LlvmIrVariableOptions.GlobalConstant);
124+
var jniOnLoadPointers = new List<LlvmIrVariableReference> ();
125+
foreach (string name in jniOnLoadNames) {
126+
jniOnLoadPointers.Add (new LlvmIrGlobalVariableReference (name));
127+
128+
// Just a dummy declaration, we don't care about the arguments
129+
var funcSig = new LlvmIrFunctionSignature (name, returnType: typeof(void));
130+
var _ = module.DeclareExternalFunction (funcSig);
131+
}
132+
module.AddGlobalVariable ("__jni_on_load_handlers", jniOnLoadPointers, LlvmIrVariableOptions.GlobalConstant);
133+
module.AddGlobalVariable ("__jni_on_load_handler_names", jniOnLoadNames, LlvmIrVariableOptions.GlobalConstant);
134+
119135
bool is64Bit = state.TargetArch switch {
120136
AndroidTargetArch.Arm64 => true,
121137
AndroidTargetArch.X86_64 => true,
@@ -304,7 +320,7 @@ LlvmIrFunctionAttributeSet MakeFindPinvokeAttributeSet (LlvmIrModule module)
304320
// Returns `true` for all p/invokes that we know are part of our set of components, otherwise returns `false`.
305321
// Returning `false` merely means that the p/invoke isn't in any of BCL or our code and therefore we shouldn't
306322
// care. It doesn't mean the p/invoke will be removed in any way.
307-
bool MustPreserve (PinvokeScanner.PinvokeEntryInfo pinfo, List<string> components)
323+
bool MustPreserve (PinvokeScanner.PinvokeEntryInfo pinfo, ICollection<string> components)
308324
{
309325
if (String.Compare ("xa-internal-api", pinfo.LibraryName, StringComparison.Ordinal) == 0) {
310326
return true;

src/native/monodroid/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,11 @@ if(BUILD_STATIC_LIBRARY)
149149
${XAMARIN_MONODROID_SOURCES}
150150
)
151151
set_static_library_suffix(${XAMARIN_MONO_ANDROID_STATIC_LIB})
152+
target_compile_definitions(
153+
${XAMARIN_MONO_ANDROID_STATIC_LIB}
154+
PRIVATE
155+
DYNAMIC_RUNTIME_LINKING
156+
)
152157
endif()
153158

154159
macro(lib_target_options TARGET_NAME)

src/native/monodroid/monodroid-glue.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,9 @@ MonodroidRuntime::Java_JNI_OnLoad (JavaVM *vm, [[maybe_unused]] void *reserved)
351351
vm->GetEnv ((void**)&env, JNI_VERSION_1_6);
352352
osBridge.initialize_on_onload (vm, env);
353353

354+
#if defined(DYNAMIC_RUNTIME_LINKING)
355+
PinvokeOverride::handle_jni_on_load (vm, reserved);
356+
#endif
354357
return JNI_VERSION_1_6;
355358
}
356359

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include <dlfcn.h>
2+
#include <jni.h>
23

34
#define PINVOKE_OVERRIDE_INLINE [[gnu::noinline]]
45
#include "pinvoke-override-api-impl.hh"
@@ -9,17 +10,26 @@ using namespace xamarin::android::internal;
910
//
1011
// This is generated during application build (see obj/${CONFIGURATION}/${RID}/android/pinvoke_preserve.*.ll)
1112
//
12-
extern "C" void* find_pinvoke (hash_t library_name_hash, hash_t entrypoint_hash, bool &known_library);
13+
using JniOnLoadHandler = jint (*) (JavaVM *vm, void *reserved);
14+
15+
extern "C" {
16+
void* find_pinvoke (hash_t library_name_hash, hash_t entrypoint_hash, bool &known_library);
17+
18+
extern const uint32_t __jni_on_load_handler_count;
19+
extern const JniOnLoadHandler __jni_on_load_handlers[];
20+
extern const char* __jni_on_load_handler_names[];
21+
}
1322

1423
[[gnu::flatten]]
1524
void*
16-
PinvokeOverride::monodroid_pinvoke_override (const char *library_name, const char *entrypoint_name)
25+
PinvokeOverride::monodroid_pinvoke_override (const char *library_name, const char *entrypoint_name) noexcept
1726
{
1827
log_debug (LOG_ASSEMBLY, __PRETTY_FUNCTION__);
1928
log_debug (LOG_ASSEMBLY, "library_name == '%s'; entrypoint_name == '%s'", library_name, entrypoint_name);
2029

21-
if (library_name == nullptr || entrypoint_name == nullptr) {
22-
return nullptr;
30+
if (library_name == nullptr || entrypoint_name == nullptr) [[unlikely]] {
31+
log_fatal (LOG_ASSEMBLY, "Both library name ('%s') and entry point name ('%s') must be specified", library_name, entrypoint_name);
32+
Helpers::abort_application ();
2333
}
2434

2535
hash_t library_name_hash = xxhash::hash (library_name, strlen (library_name));
@@ -28,7 +38,8 @@ PinvokeOverride::monodroid_pinvoke_override (const char *library_name, const cha
2838

2939
bool known_library = true;
3040
void *pinvoke_ptr = find_pinvoke (library_name_hash, entrypoint_hash, known_library);
31-
if (pinvoke_ptr != nullptr) {
41+
if (pinvoke_ptr != nullptr) [[likely]] {
42+
log_debug (LOG_ASSEMBLY, "pinvoke_ptr == %p", pinvoke_ptr);
3243
return pinvoke_ptr;
3344
}
3445

@@ -39,12 +50,26 @@ PinvokeOverride::monodroid_pinvoke_override (const char *library_name, const cha
3950
// the find* functions didn't know its hash), but we cannot be sure of that so we'll try to load it.
4051
pinvoke_ptr = dlsym (RTLD_DEFAULT, entrypoint_name);
4152
if (pinvoke_ptr == nullptr) {
42-
log_warn (LOG_ASSEMBLY, "Unable to load p/invoke entry '%s/%s' from the unified runtime DSO", library_name, entrypoint_name);
53+
log_fatal (LOG_ASSEMBLY, "Unable to load p/invoke entry '%s/%s' from the unified runtime DSO", library_name, entrypoint_name);
54+
Helpers::abort_application ();
4355
}
4456

4557
return pinvoke_ptr;
4658
}
4759

4860
log_debug (LOG_ASSEMBLY, "p/invoke not from a known library, slow path taken.");
49-
return handle_other_pinvoke_request (library_name, library_name_hash, entrypoint_name, entrypoint_hash);;
61+
pinvoke_ptr = handle_other_pinvoke_request (library_name, library_name_hash, entrypoint_name, entrypoint_hash);;
62+
log_debug (LOG_ASSEMBLY, "foreign library pinvoke_ptr == %p", pinvoke_ptr);
63+
return pinvoke_ptr;
64+
}
65+
66+
void PinvokeOverride::handle_jni_on_load (JavaVM *vm, void *reserved) noexcept
67+
{
68+
if (__jni_on_load_handler_count == 0) {
69+
return;
70+
}
71+
72+
for (uint32_t i = 0; i < __jni_on_load_handler_count; i++) {
73+
__jni_on_load_handlers[i] (vm, reserved);
74+
}
5075
}

src/native/pinvoke-override/pinvoke-override-api.hh

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
#include <string>
44

5+
#include <jni.h>
6+
57
#include "cppcompat.hh"
68
#include "xxhash.hh"
79

@@ -75,7 +77,8 @@ namespace xamarin::android {
7577
static void* 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;
7678
static PinvokeEntry* find_pinvoke_address (hash_t hash, const PinvokeEntry *entries, size_t entry_count) noexcept;
7779
static void* handle_other_pinvoke_request (const char *library_name, hash_t library_name_hash, const char *entrypoint_name, hash_t entrypoint_name_hash) noexcept;
78-
static void* monodroid_pinvoke_override (const char *library_name, const char *entrypoint_name);
80+
static void* monodroid_pinvoke_override (const char *library_name, const char *entrypoint_name) noexcept;
81+
static void handle_jni_on_load (JavaVM *vm, void *reserved) noexcept;
7982

8083
private:
8184
static xamarin::android::mutex pinvoke_map_write_lock;

src/native/pinvoke-override/precompiled.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ using namespace xamarin::android;
99

1010
[[gnu::flatten]]
1111
void*
12-
PinvokeOverride::monodroid_pinvoke_override (const char *library_name, const char *entrypoint_name)
12+
PinvokeOverride::monodroid_pinvoke_override (const char *library_name, const char *entrypoint_name) noexcept
1313
{
1414
if (library_name == nullptr || entrypoint_name == nullptr) {
1515
return nullptr;

0 commit comments

Comments
 (0)