Skip to content

Commit 96bbe92

Browse files
committed
Native side WIP
1 parent 0ad4118 commit 96bbe92

File tree

5 files changed

+164
-78
lines changed

5 files changed

+164
-78
lines changed

src/native/clr/host/host.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <host/os-bridge.hh>
1818
#include <host/runtime-util.hh>
1919
#include <runtime-base/android-system.hh>
20+
#include <runtime-base/dso-loader.hh>
2021
#include <runtime-base/jni-wrappers.hh>
2122
#include <runtime-base/logger.hh>
2223
#include <runtime-base/search.hh>
@@ -421,6 +422,8 @@ void Host::Java_mono_android_Runtime_initInternal (
421422
}
422423
);
423424

425+
DsoLoader::init (env, RuntimeUtil::get_class_from_runtime_field (env, runtimeClass, "java_lang_System", true));
426+
424427
struct JnienvInitializeArgs init = {};
425428
init.javaVm = jvm;
426429
init.env = env;

src/native/clr/include/runtime-base/android-system.hh

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,16 +104,15 @@ namespace xamarin::android {
104104
static void detect_embedded_dso_mode (jstring_array_wrapper& appDirs) noexcept;
105105
static void setup_environment () noexcept;
106106
static void setup_app_library_directories (jstring_array_wrapper& runtimeApks, jstring_array_wrapper& appDirs, bool have_split_apks) noexcept;
107-
static auto load_dso (std::string_view const& path, unsigned int dl_flags, bool skip_exists_check) noexcept -> void*;
108-
static auto load_dso_from_any_directories (std::string_view const& name, unsigned int dl_flags) noexcept -> void*;
107+
static auto load_dso_from_any_directories (std::string_view const& name, int dl_flags, bool is_jni) noexcept -> void*;
109108

110109
private:
111110
static auto get_full_dso_path (std::string const& base_dir, std::string_view const& dso_path, dynamic_local_string<SENSIBLE_PATH_MAX>& path) noexcept -> bool;
112111

113112
template<class TContainer> // TODO: replace with a concept
114-
static auto load_dso_from_specified_dirs (TContainer directories, std::string_view const& dso_name, unsigned int dl_flags) noexcept -> void*;
115-
static auto load_dso_from_app_lib_dirs (std::string_view const& name, unsigned int dl_flags) noexcept -> void*;
116-
static auto load_dso_from_override_dirs (std::string_view const& name, unsigned int dl_flags) noexcept -> void*;
113+
static auto load_dso_from_specified_dirs (TContainer directories, std::string_view const& dso_name, int dl_flags, bool is_jni) noexcept -> void*;
114+
static auto load_dso_from_app_lib_dirs (std::string_view const& name, int dl_flags, bool is_jni) noexcept -> void*;
115+
static auto load_dso_from_override_dirs (std::string_view const& name, int dl_flags, bool is_jni) noexcept -> void*;
117116
static auto lookup_system_property (std::string_view const &name, size_t &value_len) noexcept -> const char*;
118117
static auto monodroid__system_property_get (std::string_view const&, char *sp_value, size_t sp_value_len) noexcept -> int;
119118
static auto get_max_gref_count_from_system () noexcept -> long;

src/native/clr/include/runtime-base/monodroid-dl.hh

Lines changed: 10 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "../xamarin-app.hh"
1313

1414
#include "android-system.hh"
15+
#include <runtime-base/dso-loader.hh>
1516
#include <runtime-base/search.hh>
1617
#include "startup-aware-lock.hh"
1718

@@ -72,39 +73,6 @@ namespace xamarin::android
7273
return find_dso_cache_entry_common<CacheKind::DSO> (hash);
7374
}
7475

75-
static auto monodroid_dlopen_log_and_return (void *handle, std::string_view const& full_name) -> void*
76-
{
77-
if (handle == nullptr) {
78-
const char *load_error = dlerror ();
79-
if (load_error == nullptr) {
80-
load_error = "Unknown error";
81-
}
82-
log_error (
83-
LOG_ASSEMBLY,
84-
"Could not load library '{}'. {}",
85-
full_name,
86-
load_error
87-
);
88-
}
89-
90-
return handle;
91-
}
92-
93-
static auto monodroid_dlopen_ignore_component_or_load (std::string_view const& name, int flags) noexcept -> void*
94-
{
95-
// We first try to load the DSO using the passed name, it will cause `dlopen` to search our APK (or
96-
// on-filesystem location), if necessary, so it's more efficient than trying to load from any specific
97-
// directories first.
98-
unsigned int dl_flags = static_cast<unsigned int>(flags);
99-
void *handle = AndroidSystem::load_dso (name, dl_flags, false /* skip_existing_check */);
100-
if (handle != nullptr) {
101-
return monodroid_dlopen_log_and_return (handle, name);
102-
}
103-
104-
handle = AndroidSystem::load_dso_from_any_directories (name, dl_flags);
105-
return monodroid_dlopen_log_and_return (handle, name);
106-
}
107-
10876
private:
10977
[[gnu::always_inline]]
11078
static auto get_dso_name (const DSOCacheEntry *const dso) -> std::string_view
@@ -149,10 +117,11 @@ namespace xamarin::android
149117
log_debug (LOG_ASSEMBLY, "monodroid_dlopen: hash match {}found, DSO name is '{}'", dso == nullptr ? "not "sv : ""sv, get_dso_name (dso));
150118

151119
if (dso == nullptr) {
152-
// DSO not known at build time, try to load it
153-
return monodroid_dlopen_ignore_component_or_load (name, flags);
120+
// DSO not known at build time, try to load it. Since we don't know whether or not the library extends
121+
// JNI, we're going to assume it is and thus use System.loadLibrary eventually.
122+
return DsoLoader::load (name, flags, true /* is_jni */);
154123
} else if (dso->handle != nullptr) {
155-
return monodroid_dlopen_log_and_return (dso->handle, get_dso_name (dso));
124+
return dso->handle;
156125
}
157126

158127
if (dso->ignore) {
@@ -179,21 +148,20 @@ namespace xamarin::android
179148
dso->handle = android_dlopen_ext (dso_name.data (), flags, &dli);
180149

181150
if (dso->handle != nullptr) {
182-
return monodroid_dlopen_log_and_return (dso->handle, dso_name);
151+
return dso->handle;
183152
}
184153
break;
185154
}
186155
}
187156
#endif
188-
unsigned int dl_flags = static_cast<unsigned int>(flags);
189-
dso->handle = AndroidSystem::load_dso_from_any_directories (dso_name, dl_flags);
157+
dso->handle = AndroidSystem::load_dso_from_any_directories (dso_name, flags, dso->is_jni_library);
190158

191159
if (dso->handle != nullptr) {
192-
return monodroid_dlopen_log_and_return (dso->handle, dso_name);
160+
return dso->handle;
193161
}
194162

195-
dso->handle = AndroidSystem::load_dso_from_any_directories (name, dl_flags);
196-
return monodroid_dlopen_log_and_return (dso->handle, name);
163+
dso->handle = AndroidSystem::load_dso_from_any_directories (name, flags, dso->is_jni_library);
164+
return dso->handle;
197165
}
198166

199167
[[gnu::flatten]]

src/native/clr/runtime-base/android-system.cc

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <xamarin-app.hh>
99
#include <runtime-base/android-system.hh>
1010
#include <runtime-base/cpu-arch.hh>
11+
#include <runtime-base/dso-loader.hh>
1112
#include <runtime-base/strings.hh>
1213
#include <runtime-base/util.hh>
1314

@@ -435,30 +436,8 @@ auto AndroidSystem::get_full_dso_path (std::string const& base_dir, std::string_
435436
return true;
436437
}
437438

438-
auto AndroidSystem::load_dso (std::string_view const& path, unsigned int dl_flags, bool skip_exists_check) noexcept -> void*
439-
{
440-
if (path.empty ()) [[unlikely]] {
441-
return nullptr;
442-
}
443-
444-
log_info (LOG_ASSEMBLY, "Trying to load shared library '{}'", path);
445-
if (!skip_exists_check && !is_embedded_dso_mode_enabled () && !Util::file_exists (path)) {
446-
log_info (LOG_ASSEMBLY, "Shared library '{}' not found", path);
447-
return nullptr;
448-
}
449-
450-
char *error = nullptr;
451-
void *handle = java_interop_lib_load (path.data (), dl_flags, &error);
452-
if (handle == nullptr && Util::should_log (LOG_ASSEMBLY)) {
453-
log_info_nocheck_fmt (LOG_ASSEMBLY, "Failed to load shared library '{}'. {}", path, error);
454-
}
455-
java_interop_free (error);
456-
457-
return handle;
458-
}
459-
460439
template<class TContainer> [[gnu::always_inline]]
461-
auto AndroidSystem::load_dso_from_specified_dirs (TContainer directories, std::string_view const& dso_name, unsigned int dl_flags) noexcept -> void*
440+
auto AndroidSystem::load_dso_from_specified_dirs (TContainer directories, std::string_view const& dso_name, int dl_flags, bool is_jni) noexcept -> void*
462441
{
463442
if (dso_name.empty ()) {
464443
return nullptr;
@@ -470,7 +449,7 @@ auto AndroidSystem::load_dso_from_specified_dirs (TContainer directories, std::s
470449
continue;
471450
}
472451

473-
void *handle = load_dso (full_path.get (), dl_flags, false);
452+
void *handle = DsoLoader::load (full_path.get (), dl_flags, is_jni);
474453
if (handle != nullptr) {
475454
return handle;
476455
}
@@ -479,26 +458,26 @@ auto AndroidSystem::load_dso_from_specified_dirs (TContainer directories, std::s
479458
return nullptr;
480459
}
481460

482-
auto AndroidSystem::load_dso_from_app_lib_dirs (std::string_view const& name, unsigned int dl_flags) noexcept -> void*
461+
auto AndroidSystem::load_dso_from_app_lib_dirs (std::string_view const& name, int dl_flags, bool is_jni) noexcept -> void*
483462
{
484-
return load_dso_from_specified_dirs (app_lib_directories, name, dl_flags);
463+
return load_dso_from_specified_dirs (app_lib_directories, name, dl_flags, is_jni);
485464
}
486465

487-
auto AndroidSystem::load_dso_from_override_dirs (std::string_view const& name, unsigned int dl_flags) noexcept -> void*
466+
auto AndroidSystem::load_dso_from_override_dirs (std::string_view const& name, int dl_flags, bool is_jni) noexcept -> void*
488467
{
489468
if constexpr (Constants::is_release_build) {
490469
return nullptr;
491470
} else {
492-
return load_dso_from_specified_dirs (AndroidSystem::override_dirs, name, dl_flags);
471+
return load_dso_from_specified_dirs (AndroidSystem::override_dirs, name, dl_flags, is_jni);
493472
}
494473
}
495474

496475
[[gnu::flatten]]
497-
auto AndroidSystem::load_dso_from_any_directories (std::string_view const& name, unsigned int dl_flags) noexcept -> void*
476+
auto AndroidSystem::load_dso_from_any_directories (std::string_view const& name, int dl_flags, bool is_jni) noexcept -> void*
498477
{
499-
void *handle = load_dso_from_override_dirs (name, dl_flags);
478+
void *handle = load_dso_from_override_dirs (name, dl_flags, is_jni);
500479
if (handle == nullptr) {
501-
handle = load_dso_from_app_lib_dirs (name, dl_flags);
480+
handle = load_dso_from_app_lib_dirs (name, dl_flags, is_jni);
502481
}
503482
return handle;
504483
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
#pragma once
2+
3+
#include <jni.h>
4+
#include <dlfcn.h>
5+
#include <android/dlext.h>
6+
7+
#include <string_view>
8+
9+
#include <runtime-base/android-system.hh>
10+
#include <runtime-base/util.hh>
11+
#include <shared/helpers.hh>
12+
13+
namespace xamarin::android {
14+
class DsoLoader
15+
{
16+
public:
17+
[[gnu::flatten]]
18+
static void init (JNIEnv *env, jclass systemClass)
19+
{
20+
jni_env = env;
21+
systemKlass = systemClass;
22+
System_loadLibrary = env->GetMethodID (systemClass, "loadLibrary", "(Ljava/lang/String;)V");
23+
if (System_loadLibrary == nullptr) [[unlikely]] {
24+
Helpers::abort_application ("Failed to look up the Java System.loadLibrary method.");
25+
}
26+
}
27+
28+
// Overload used to load libraries from the file system.
29+
template<bool SkipExistsCheck = false>
30+
[[gnu::always_inline, gnu::flatten]]
31+
static auto load (std::string_view const& path, int flags, bool is_jni) -> void*
32+
{
33+
if (is_jni) {
34+
return load_jni (path, true /* name_is_path */);
35+
}
36+
37+
log_info (LOG_ASSEMBLY, "Trying to load shared library '{}'", path);
38+
if constexpr (!SkipExistsCheck) {
39+
if (!AndroidSystem::is_embedded_dso_mode_enabled () && !Util::file_exists (path)) {
40+
log_info (LOG_ASSEMBLY, "Shared library '{}' not found", path);
41+
return nullptr;
42+
}
43+
}
44+
45+
return log_and_return (dlopen (path.data (), flags), path);
46+
}
47+
48+
// Overload used to load libraries from the APK.
49+
[[gnu::always_inline, gnu::flatten]]
50+
static auto load (int fd, off64_t offset, std::string_view const& name, int flags, bool is_jni) -> void*
51+
{
52+
if (is_jni) {
53+
return load_jni (name, true /* name_is_path */);
54+
}
55+
56+
android_dlextinfo dli;
57+
dli.flags = ANDROID_DLEXT_USE_LIBRARY_FD | ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET;
58+
dli.library_fd = fd;
59+
dli.library_fd_offset = offset;
60+
61+
return log_and_return (android_dlopen_ext (name.data (), flags, &dli), name);
62+
}
63+
64+
private:
65+
[[gnu::always_inline]]
66+
static auto log_and_return (void *handle, std::string_view const& full_name) -> void*
67+
{
68+
if (handle != nullptr) [[likely]] {
69+
return handle;
70+
}
71+
72+
const char *load_error = dlerror ();
73+
if (load_error == nullptr) {
74+
load_error = "Unknown error";
75+
}
76+
log_error (
77+
LOG_ASSEMBLY,
78+
"Could not load library '{}'. {}"sv,
79+
full_name,
80+
load_error
81+
);
82+
83+
return nullptr;
84+
}
85+
86+
static auto load_jni (std::string_view const& name, bool name_is_path) -> void*
87+
{
88+
if (jni_env == nullptr || systemKlass == nullptr) [[unlikely]] {
89+
Helpers::abort_application ("DSO loader class not initialized properly."sv);
90+
}
91+
92+
// System.loadLibrary call is going to be slow anyway, so we can spend some more time generating an
93+
// undecorated library name here instead of at build time. This saves us a little bit of space in
94+
// `libxamarin-app.so` and makes the build code less complicated.
95+
auto get_undecorated_name = [](std::string_view const& full_name, bool is_path) -> std::string_view {
96+
std::string_view name;
97+
98+
if (!is_path) {
99+
name = full_name;
100+
} else {
101+
name = full_name; // TODO: cut the path
102+
}
103+
104+
size_t name_start = 0;
105+
size_t name_end = full_name.length ();
106+
107+
if (name.starts_with ("lib"sv) && name.length () > 3) {
108+
name_start = 3;
109+
}
110+
111+
if (name.ends_with (".so"sv) && name.length () > 3) {
112+
name_end -= 3;
113+
}
114+
115+
if (name_start >= name_end) [[unlikely]] {
116+
return name;
117+
}
118+
119+
return std::move (name.substr (name_start, name.length () - name_end));
120+
};
121+
122+
const std::string_view undecorated_lib_name = get_undecorated_name (name, name_is_path);
123+
124+
jstring lib_name = jni_env->NewStringUTF (undecorated_lib_name.data ());
125+
jni_env->CallStaticVoidMethod (systemKlass, System_loadLibrary, lib_name);
126+
127+
// This is unfortunate, but since `System.loadLibrary` doesn't return the class handle, we must get it this
128+
// way :(
129+
return log_and_return (dlopen (name.data (), RTLD_NOLOAD), name);
130+
}
131+
132+
private:
133+
static inline jmethodID System_loadLibrary = nullptr;
134+
static inline jclass systemKlass = nullptr;
135+
static inline JNIEnv *jni_env = nullptr;
136+
};
137+
}

0 commit comments

Comments
 (0)