Skip to content

Commit 525ff1f

Browse files
committed
Implement support for CoreCLR host to load assemblies from storage
This adds the missing bits to load assembly store from device's filesystem instead of from the APK when running application which has the `android:extractNativeLibs` attribute set to `true` in its manifest.
1 parent d31a6bf commit 525ff1f

File tree

7 files changed

+127
-16
lines changed

7 files changed

+127
-16
lines changed

src/native/clr/host/host.cc

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1+
#include <sys/types.h>
2+
#include <dirent.h>
3+
4+
#include <cerrno>
15
#include <cstdio>
6+
#include <cstring>
27

38
#include <coreclrhost.h>
49

@@ -62,10 +67,98 @@ auto Host::zip_scan_callback (std::string_view const& apk_path, int apk_fd, dyna
6267
return false;
6368
}
6469

70+
[[gnu::always_inline]]
71+
void Host::scan_filesystem_for_assemblies_and_libraries () noexcept
72+
{
73+
std::string const& native_lib_dir = AndroidSystem::get_native_libraries_dir ();
74+
log_debug (LOG_ASSEMBLY, "Looking for assemblies in '{}'", native_lib_dir);
75+
76+
DIR *lib_dir = opendir (native_lib_dir.c_str ());
77+
if (lib_dir == nullptr) [[unlikely]] {
78+
Helpers::abort_application (
79+
LOG_ASSEMBLY,
80+
std::format (
81+
"Unable to open native library directory '{}'. {}",
82+
native_lib_dir,
83+
std::strerror (errno)
84+
)
85+
);
86+
}
87+
88+
int dir_fd = dirfd (lib_dir);
89+
if (dir_fd < 0) [[unlikely]] {
90+
Helpers::abort_application (
91+
LOG_ASSEMBLY,
92+
std::format (
93+
"Unable to obtain file descriptor for opened directory '{}'. {}",
94+
native_lib_dir,
95+
std::strerror (errno)
96+
)
97+
);
98+
}
99+
100+
do {
101+
errno = 0;
102+
dirent *cur = readdir (lib_dir);
103+
if (cur == nullptr) {
104+
if (errno != 0) {
105+
log_warn (LOG_ASSEMBLY, "Failed to open a directory entry from '{}': {}", native_lib_dir, std::strerror (errno));
106+
continue; // No harm, keep going
107+
}
108+
break; // we're done
109+
}
110+
111+
// We can ignore the obvious entries
112+
if (cur->d_name[0] == '.') {
113+
continue;
114+
}
115+
116+
if (!found_assembly_store) {
117+
found_assembly_store = Constants::assembly_store_file_name.compare (cur->d_name) == 0;
118+
if (!found_assembly_store) {
119+
continue;
120+
}
121+
122+
log_debug (LOG_ASSEMBLY, "Found assembly store in '{}/{}'", native_lib_dir, Constants::assembly_store_file_name);
123+
int store_fd = openat (dir_fd, cur->d_name, O_RDONLY);
124+
if (store_fd < 0) {
125+
Helpers::abort_application (
126+
LOG_ASSEMBLY,
127+
std::format (
128+
"Unable to open assembly store '{}/{}' for reading. {}",
129+
native_lib_dir,
130+
Constants::assembly_store_file_name,
131+
std::strerror (errno)
132+
)
133+
);
134+
}
135+
136+
auto file_size = Util::get_file_size_at (dir_fd, cur->d_name);
137+
if (!file_size) {
138+
// get_file_size_at logged errno for us
139+
Helpers::abort_application (
140+
LOG_ASSEMBLY,
141+
std::format (
142+
"Unable to map assembly store '{}/{}'",
143+
native_lib_dir,
144+
Constants::assembly_store_file_name
145+
)
146+
);
147+
}
148+
149+
AssemblyStore::map (store_fd, cur->d_name, 0, static_cast<uint32_t>(file_size.value ()));
150+
close (store_fd);
151+
break; // we've found all we need
152+
}
153+
} while (true);
154+
closedir (lib_dir);
155+
}
156+
65157
void Host::gather_assemblies_and_libraries (jstring_array_wrapper& runtimeApks, bool have_split_apks)
66158
{
67159
if (!AndroidSystem::is_embedded_dso_mode_enabled ()) {
68-
Helpers::abort_application ("Filesystem mode not supported yet.");
160+
scan_filesystem_for_assemblies_and_libraries ();
161+
return;
69162
}
70163

71164
int64_t apk_count = static_cast<int64_t>(runtimeApks.get_length ());

src/native/clr/include/constants.hh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ namespace xamarin::android {
3131
static constexpr bool is_debug_build = true;
3232
#endif
3333
static constexpr std::string_view MANGLED_ASSEMBLY_NAME_EXT { ".so" };
34+
static constexpr std::string_view dso_suffix { ".so" };
3435

3536
private:
3637
static constexpr std::string_view RUNTIME_CONFIG_BLOB_BASE_NAME { "libarc.bin" };
@@ -94,7 +95,15 @@ namespace xamarin::android {
9495
private:
9596
static constexpr size_t split_config_abi_apk_name_size = calc_size (split_config_prefix, android_abi, split_config_extension);
9697

98+
static constexpr std::string_view assembly_store_prefix { "libassemblies." };
99+
static constexpr std::string_view assembly_store_extension { ".blob" };
100+
static constexpr size_t assembly_store_file_name_size = calc_size (assembly_store_prefix, android_lib_abi, assembly_store_extension, dso_suffix);
101+
static constexpr auto assembly_store_file_name_array = concat_string_views<assembly_store_file_name_size> (assembly_store_prefix, android_lib_abi, assembly_store_extension, dso_suffix);
102+
97103
public:
104+
// .data() must be used otherwise string_view length will include the trailing \0 in the array
105+
static constexpr std::string_view assembly_store_file_name { assembly_store_file_name_array.data () };
106+
98107
static constexpr auto split_config_abi_apk_name = concat_string_views<split_config_abi_apk_name_size> (split_config_prefix, android_abi, split_config_extension);
99108

100109
//

src/native/clr/include/host/host.hh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ namespace xamarin::android {
3333
static void create_xdg_directories_and_environment (jstring_wrapper &homeDir) noexcept;
3434
static auto zip_scan_callback (std::string_view const& apk_path, int apk_fd, dynamic_local_string<SENSIBLE_PATH_MAX> const& entry_name, uint32_t offset, uint32_t size) -> bool;
3535
static void gather_assemblies_and_libraries (jstring_array_wrapper& runtimeApks, bool have_split_apks);
36+
static void scan_filesystem_for_assemblies_and_libraries () noexcept;
3637

3738
static size_t clr_get_runtime_property (const char *key, char *value_buffer, size_t value_buffer_size, void *contract_context) noexcept;
3839
static bool clr_external_assembly_probe (const char *path, void **data_start, int64_t *size) noexcept;

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ namespace xamarin::android {
7070
primary_override_dir = determine_primary_override_dir (home);
7171
}
7272

73+
static auto get_native_libraries_dir () noexcept -> std::string const&
74+
{
75+
return native_libraries_dir;
76+
}
77+
7378
static void create_update_dir (std::string const& override_dir) noexcept
7479
{
7580
if constexpr (Constants::is_release_build) {
@@ -141,6 +146,7 @@ namespace xamarin::android {
141146
static inline bool running_in_emulator = false;
142147
static inline bool embedded_dso_mode_enabled = false;
143148
static inline std::string primary_override_dir;
149+
static inline std::string native_libraries_dir;
144150

145151
#if defined (DEBUG)
146152
static inline std::unordered_map<std::string, std::string> bundled_properties;

src/native/clr/include/runtime-base/util.hh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <cerrno>
99
#include <concepts>
1010
#include <cstdio>
11+
#include <optional>
1112
#include <string_view>
1213

1314
#include "../constants.hh"
@@ -88,6 +89,17 @@ namespace xamarin::android {
8889
return file_exists (file.get ());
8990
}
9091

92+
static auto get_file_size_at (int dirfd, const char *file_name) noexcept -> std::optional<size_t>
93+
{
94+
struct stat sbuf;
95+
if (fstatat (dirfd, file_name, &sbuf, 0) == -1) {
96+
log_warn (LOG_ASSEMBLY, "Failed to stat file '{}': {}", file_name, std::strerror (errno));
97+
return std::nullopt;
98+
}
99+
100+
return static_cast<size_t>(sbuf.st_size);
101+
}
102+
91103
static void set_environment_variable (std::string_view const& name, jstring_wrapper& value) noexcept
92104
{
93105
::setenv (name.data (), value.get_cstr (), 1);

src/native/clr/include/startup/zip.hh

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -59,23 +59,12 @@ namespace xamarin::android {
5959
// .data() must be used otherwise string_view length will include the trailing \0 in the array
6060
static constexpr std::string_view lib_prefix { lib_prefix_array.data () };
6161

62-
static constexpr std::string_view dso_suffix { ".so" };
63-
64-
static constexpr std::string_view assembly_store_prefix { "libassemblies." };
65-
static constexpr std::string_view assembly_store_extension { ".blob" };
66-
67-
static constexpr size_t assembly_store_file_name_size = calc_size (assembly_store_prefix, Constants::android_lib_abi, assembly_store_extension, dso_suffix);
68-
static constexpr auto assembly_store_file_name_array = concat_string_views<assembly_store_file_name_size> (assembly_store_prefix, Constants::android_lib_abi, assembly_store_extension, dso_suffix);
69-
70-
// .data() must be used otherwise string_view length will include the trailing \0 in the array
71-
static constexpr std::string_view assembly_store_file_name { assembly_store_file_name_array.data () };
72-
73-
static constexpr size_t assembly_store_file_path_size = calc_size(lib_prefix, assembly_store_file_name);
74-
static constexpr auto assembly_store_file_path_array = concat_string_views<assembly_store_file_path_size> (lib_prefix, assembly_store_file_name);
62+
static constexpr size_t assembly_store_file_path_size = calc_size(lib_prefix, Constants::assembly_store_file_name);
63+
static constexpr auto assembly_store_file_path_array = concat_string_views<assembly_store_file_path_size> (lib_prefix, Constants::assembly_store_file_name);
7564

7665
public:
77-
// .data() must be used otherwise string_view length will include the trailing \0 in the array
78-
static constexpr std::string_view assembly_store_file_path { assembly_store_file_path_array.data () };
66+
// .data() must be used otherwise string_view length will include the trailing \0 in the array
67+
static constexpr std::string_view assembly_store_file_path { assembly_store_file_path_array.data () };
7968

8069
public:
8170
// Scans the ZIP archive for any entries matching the `lib/{ARCH}/` prefix and calls `entry_cb`

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ AndroidSystem::detect_embedded_dso_mode (jstring_array_wrapper& appDirs) noexcep
277277
} else {
278278
log_debug (LOG_ASSEMBLY, "Native libs extracted to {}, assuming application/android:extractNativeLibs == true", appDirs[Constants::APP_DIRS_DATA_DIR_INDEX].get_cstr ());
279279
set_embedded_dso_mode_enabled (false);
280+
native_libraries_dir.assign (appDirs[Constants::APP_DIRS_DATA_DIR_INDEX].get_cstr ());
280281
}
281282
}
282283

0 commit comments

Comments
 (0)