Skip to content

Commit 9c8ec9c

Browse files
authored
[android] Make Android app on coreclr use host-runtime contract and provide external assembly probe (#112938)
- Update app builder targets / tasks to allow specifying multiple runtime header directories - Make Android app on coreclr use `host_runtime_contract::external_assembly_probe`
1 parent 6c4e699 commit 9c8ec9c

File tree

16 files changed

+123
-103
lines changed

16 files changed

+123
-103
lines changed

src/mono/msbuild/android/build/AndroidBuild.targets

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,17 @@
3737
<AndroidBundleDir Condition="'$(AndroidBundleDir)' == ''">$([MSBuild]::NormalizeDirectory('$(OutDir)', 'Bundle'))</AndroidBundleDir>
3838

3939
<BundleDir>$(AndroidBundleDir)</BundleDir>
40-
<RuntimeHeaders>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include', 'mono-2.0'))</RuntimeHeaders>
41-
<RuntimeHeaders Condition="'$(UseMonoRuntime)' == 'false' and '$(UseNativeAOTRuntime)' != 'true'">$(CoreClrProjectRoot)hosts\inc</RuntimeHeaders>
4240
</PropertyGroup>
4341

42+
<ItemGroup>
43+
<RuntimeHeaders Include="$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include', 'mono-2.0'))"
44+
Condition="'$(UseMonoRuntime)' == 'true' and '$(UseNativeAOTRuntime)' != 'true'"/>
45+
<RuntimeHeaders Include="$(CoreClrProjectRoot)hosts\inc"
46+
Condition="'$(UseMonoRuntime)' == 'false' and '$(UseNativeAOTRuntime)' != 'true'" />
47+
<RuntimeHeaders Include="$(SharedNativeRoot)"
48+
Condition="'$(UseMonoRuntime)' == 'false' and '$(UseNativeAOTRuntime)' != 'true'" />
49+
</ItemGroup>
50+
4451
<ItemGroup Condition="'$(UseMonoRuntime)' == 'true' and '$(UseNativeAOTRuntime)' != 'true'">
4552
<RuntimeComponents Condition="'$(UseAllRuntimeComponents)' == 'true'" Include="@(_MonoRuntimeAvailableComponents)" />
4653
<!-- Make sure marshal-ilgen is included in the components list. -->
@@ -246,7 +253,7 @@
246253
ForceInterpreter="$(MonoForceInterpreter)"
247254
IsLibraryMode="$(_IsLibraryMode)"
248255
MainLibraryFileName="$(MainLibraryFileName)"
249-
MonoRuntimeHeaders="$(RuntimeHeaders)"
256+
RuntimeHeaders="@(RuntimeHeaders)"
250257
NativeDependencies="@(_NativeDependencies)"
251258
OutputDir="$(AndroidBundleDir)"
252259
ProjectName="$(AppName)"

src/mono/msbuild/apple/build/AppleBuild.targets

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,15 @@
5454
<AppleBundleDir Condition="'$(AppleBundleDir)' == ''">$([MSBuild]::NormalizeDirectory('$(OutDir)', 'Bundle'))</AppleBundleDir>
5555

5656
<BundleDir>$(AppleBundleDir)</BundleDir>
57-
<RuntimeHeaders Condition="'$(UseMonoRuntime)' != 'false' and '$(UseNativeAOTRuntime)' != 'true'">$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include', 'mono-2.0'))</RuntimeHeaders>
5857
<_AotModuleTablePath>$(AppleBundleDir)\modules.m</_AotModuleTablePath>
5958
<AppName Condition="'$(AppName)' == ''">$(AssemblyName)</AppName>
6059
</PropertyGroup>
6160

61+
<ItemGroup>
62+
<RuntimeHeaders Include="$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include', 'mono-2.0'))"
63+
Condition="'$(UseMonoRuntime)' != 'false' and '$(UseNativeAOTRuntime)' != 'true'" />
64+
</ItemGroup>
65+
6266
<ItemGroup Condition="'$(AppleAppBuilderRuntime)' == 'MonoVM'">
6367
<RuntimeComponents Condition="'$(UseAllRuntimeComponents)' == 'true'" Include="@(_MonoRuntimeAvailableComponents)"/>
6468
<!-- Make sure marshal-ilgen is included in the components list. -->
@@ -298,7 +302,7 @@
298302
<LinkerArg Remove="@(_LinkerFlagsToDrop)" />
299303
<ExtraAppLinkerArgs Include="@(LinkerArg)" />
300304
</ItemGroup>
301-
305+
302306
<PropertyGroup>
303307
<AppleAppBuilderRuntime Condition="'$(AppleAppBuilderRuntime)' == '' and '$(UseNativeAOTRuntime)' == 'true'">NativeAOT</AppleAppBuilderRuntime>
304308
<AppleAppBuilderRuntime Condition="'$(AppleAppBuilderRuntime)' == '' and '$(UseMonoRuntime)' == 'false'">CoreCLR</AppleAppBuilderRuntime>
@@ -324,7 +328,7 @@
324328
InvariantGlobalization="$(InvariantGlobalization)"
325329
IsLibraryMode="$(_IsLibraryMode)"
326330
MainLibraryFileName="$(MainLibraryFileName)"
327-
MonoRuntimeHeaders="$(RuntimeHeaders)"
331+
MonoRuntimeHeaders="@(RuntimeHeaders)"
328332
NativeMainSource="$(NativeMainSource)"
329333
NativeDependencies="@(NativeDependencies)"
330334
Optimized="$(Optimized)"

src/mono/msbuild/common/LibraryBuilder.targets

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project>
22
<UsingTask TaskName="EmitBundleSourceFiles"
33
AssemblyFile="$(MonoTargetsTasksAssemblyPath)" />
4-
<UsingTask TaskName="LibraryBuilderTask"
4+
<UsingTask TaskName="LibraryBuilderTask"
55
AssemblyFile="$(LibraryBuilderTasksAssemblyPath)" />
66

77
<Target Name="_BuildNativeLibrary"
@@ -33,7 +33,7 @@
3333
ExtraLinkerArguments="@(_ExtraLinkerArgs)"
3434
ExtraSources="@(_ExtraLibrarySources)"
3535
IsSharedLibrary="$(_IsSharedLibrary)"
36-
MonoRuntimeHeaders="$(RuntimeHeaders)"
36+
MonoRuntimeHeaders="@(RuntimeHeaders)"
3737
Name="$(AssemblyName)"
3838
OutputDirectory="$(BundleDir)"
3939
RuntimeIdentifier="$(RuntimeIdentifier)"

src/mono/sample/Android/AndroidSampleApp.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
<ItemGroup>
2121
<RuntimeComponents Condition="'$(RuntimeFlavor)' == 'Mono' and '$(DiagnosticPorts)' != ''" Include="diagnostics_tracing" />
22+
<RuntimeHeaders Include="$(SharedNativeRoot)" Condition="'$(RuntimeFlavor)' != 'Mono'"/>
2223
</ItemGroup>
2324

2425
<Import Project="$(MonoProjectRoot)\msbuild\android\build\AndroidBuild.props" />

src/tasks/AndroidAppBuilder/AndroidAppBuilder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
public class AndroidAppBuilderTask : Task
1111
{
1212
[Required]
13-
public string MonoRuntimeHeaders { get; set; } = ""!;
13+
public string[] RuntimeHeaders { get; set; } = [];
1414

1515
/// <summary>
1616
/// Target directory with *dll and other content to be AOT'd and/or bundled
@@ -141,7 +141,7 @@ public override bool Execute()
141141
apkBuilder.NativeDependencies = NativeDependencies;
142142
apkBuilder.ExtraLinkerArguments = ExtraLinkerArguments;
143143
apkBuilder.RuntimeFlavor = RuntimeFlavor;
144-
(ApkBundlePath, ApkPackageId) = apkBuilder.BuildApk(RuntimeIdentifier, MainLibraryFileName, MonoRuntimeHeaders);
144+
(ApkBundlePath, ApkPackageId) = apkBuilder.BuildApk(RuntimeIdentifier, MainLibraryFileName, RuntimeHeaders);
145145

146146
return true;
147147
}

src/tasks/AndroidAppBuilder/ApkBuilder.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public ApkBuilder(TaskLoggingHelper logger)
6464
public (string apk, string packageId) BuildApk(
6565
string runtimeIdentifier,
6666
string mainLibraryFileName,
67-
string runtimeHeaders)
67+
string[] runtimeHeaders)
6868
{
6969
if (!Enum.TryParse(RuntimeFlavor, true, out parsedRuntimeFlavor))
7070
{
@@ -324,9 +324,10 @@ public ApkBuilder(TaskLoggingHelper logger)
324324
string aotSources = assemblerFiles.ToString();
325325
string monodroidSource = IsCoreCLR ?
326326
"monodroid-coreclr.c" : (IsLibraryMode) ? "monodroid-librarymode.c" : "monodroid.c";
327+
string runtimeInclude = string.Join(" ", runtimeHeaders.Select(h => $"\"{NormalizePathToUnix(h)}\""));
327328

328329
string cmakeLists = Utils.GetEmbeddedResource("CMakeLists-android.txt")
329-
.Replace("%RuntimeInclude%", NormalizePathToUnix(runtimeHeaders))
330+
.Replace("%RuntimeInclude%", runtimeInclude)
330331
.Replace("%NativeLibrariesToLink%", NormalizePathToUnix(nativeLibraries))
331332
.Replace("%MONODROID_SOURCE%", monodroidSource)
332333
.Replace("%AotSources%", NormalizePathToUnix(aotSources))

src/tasks/AndroidAppBuilder/Templates/CMakeLists-android.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ add_library(
2121

2222
%Defines%
2323

24-
include_directories("%RuntimeInclude%")
24+
include_directories(%RuntimeInclude%)
2525

2626
target_link_libraries(
2727
monodroid

src/tasks/AndroidAppBuilder/Templates/monodroid-coreclr.c

Lines changed: 82 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
#include <coreclrhost.h>
1717
#include <dirent.h>
1818

19+
#include <corehost/host_runtime_contract.h>
20+
1921
/********* exported symbols *********/
2022

2123
/* JNI exports */
@@ -34,11 +36,15 @@ Java_net_dot_MonoRunner_freeNativeResources (JNIEnv* env, jobject thiz);
3436

3537
/********* implementation *********/
3638

37-
static const char* g_bundle_path = NULL;
39+
static char* g_bundle_path = NULL;
3840
static const char* g_executable_path = NULL;
3941
static unsigned int g_coreclr_domainId = 0;
4042
static void* g_coreclr_handle = NULL;
4143

44+
#define MAX_MAPPED_COUNT 256 // Arbitrarily 'large enough' number
45+
static void* g_mapped_files[MAX_MAPPED_COUNT];
46+
static size_t g_mapped_file_sizes[MAX_MAPPED_COUNT];
47+
static unsigned int g_mapped_files_count = 0;
4248

4349
#define LOG_INFO(fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, "DOTNET", fmt, ##__VA_ARGS__)
4450
#define LOG_ERROR(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, "DOTNET", fmt, ##__VA_ARGS__)
@@ -68,66 +74,6 @@ strncpy_str (JNIEnv *env, char *buff, jstring str, int nbuff)
6874
(*env)->ReleaseStringUTFChars (env, str, copy_buff);
6975
}
7076

71-
/*
72-
* Get the list of trusted assemblies from a specified @dir_path.
73-
* The path is searched for .dll files which when found are concatenated
74-
* to the output string @tpas separated by ':'.
75-
* The output string should be freed by the caller.
76-
* The return value is the length of the output string.
77-
*/
78-
static size_t
79-
get_tpas_from_path(const char* dir_path, const char** tpas)
80-
{
81-
DIR *dir = opendir(dir_path);
82-
if (dir == NULL)
83-
{
84-
LOG_ERROR("Failed to open directory at: %s", dir_path);
85-
return -1;
86-
}
87-
88-
struct dirent *dir_entry;
89-
size_t dir_path_len = strlen(dir_path);
90-
char *concat_dll_paths = NULL;
91-
size_t concat_dll_paths_len = 0;
92-
93-
while ((dir_entry = readdir(dir)))
94-
{
95-
if (dir_entry->d_type == DT_REG)
96-
{
97-
size_t file_name_len = strlen(dir_entry->d_name);
98-
// filter out .dll files
99-
if (file_name_len > 4 && strcmp(dir_entry->d_name + file_name_len - 4, ".dll") == 0)
100-
{
101-
size_t curr_dll_path = dir_path_len + file_name_len + 2; // +2 for '/' and ':'
102-
concat_dll_paths_len += curr_dll_path;
103-
concat_dll_paths = realloc(concat_dll_paths, concat_dll_paths_len);
104-
if (concat_dll_paths == NULL)
105-
{
106-
LOG_ERROR("realloc failed while resolving: %s", dir_entry->d_name);
107-
closedir(dir);
108-
return -1;
109-
}
110-
concat_dll_paths[concat_dll_paths_len-curr_dll_path] = '\0'; // adjust previous string end
111-
size_t ret = sprintf(concat_dll_paths, "%s%s/%s:", concat_dll_paths, dir_path, dir_entry->d_name); // concat the current dll path
112-
if (ret != concat_dll_paths_len)
113-
{
114-
LOG_ERROR("sprintf failed while resolving: %s", dir_entry->d_name);
115-
closedir(dir);
116-
return -1;
117-
}
118-
}
119-
}
120-
}
121-
closedir(dir);
122-
123-
if (concat_dll_paths != NULL && concat_dll_paths_len > 0) {
124-
concat_dll_paths[concat_dll_paths_len - 1] = '\0'; // remove the trailing ':'
125-
}
126-
127-
*tpas = concat_dll_paths;
128-
return concat_dll_paths_len;
129-
}
130-
13177
static int
13278
bundle_executable_path (const char* executable, const char* bundle_path, const char** executable_path)
13379
{
@@ -147,6 +93,56 @@ bundle_executable_path (const char* executable, const char* bundle_path, const c
14793
return executable_path_len;
14894
}
14995

96+
static bool
97+
external_assembly_probe(const char* name, void** data, int64_t* size)
98+
{
99+
if (g_mapped_files_count >= MAX_MAPPED_COUNT)
100+
{
101+
LOG_ERROR("Too many mapped files, cannot map %s", name);
102+
return false;
103+
}
104+
105+
// Get just the file name
106+
const char* pos = strrchr(name, '/');
107+
if (pos != NULL)
108+
name = pos + 1;
109+
110+
// Look in the bundle path where the files were extracted
111+
char full_path[1024];
112+
size_t path_len = strlen(g_bundle_path) + strlen(name) + 1; // +1 for '/'
113+
size_t res = snprintf(full_path, path_len + 1, "%s/%s", g_bundle_path, name);
114+
if (res < 0 || res != path_len)
115+
return false;
116+
117+
int fd = open(full_path, O_RDONLY);
118+
if (fd == -1)
119+
return false;
120+
121+
struct stat buf;
122+
if (fstat(fd, &buf) == -1)
123+
{
124+
close(fd);
125+
return false;
126+
}
127+
128+
int64_t size_local = buf.st_size;
129+
void* mapped = mmap(NULL, size_local, PROT_READ, MAP_PRIVATE, fd, 0);
130+
if (mapped == MAP_FAILED)
131+
{
132+
close(fd);
133+
return false;
134+
}
135+
136+
LOG_INFO("Mapped %s -> %s", name, full_path);
137+
g_mapped_files[g_mapped_files_count] = mapped;
138+
g_mapped_file_sizes[g_mapped_files_count] = size_local;
139+
g_mapped_files_count++;
140+
close(fd);
141+
*data = mapped;
142+
*size = size_local;
143+
return true;
144+
}
145+
150146
static void
151147
free_resources ()
152148
{
@@ -166,9 +162,13 @@ free_resources ()
166162
coreclr_shutdown (g_coreclr_handle, g_coreclr_domainId);
167163
g_coreclr_handle = NULL;
168164
}
165+
for (int i = 0; i < g_mapped_files_count; ++i)
166+
{
167+
munmap (g_mapped_files[i], g_mapped_file_sizes[i]);
168+
}
169169
}
170170

171-
static int
171+
static int
172172
mono_droid_execute_assembly (const char* executable_path, void* coreclr_handle, unsigned int coreclr_domainId, int managed_argc, const char** managed_argv)
173173
{
174174
unsigned int rv;
@@ -178,6 +178,8 @@ mono_droid_execute_assembly (const char* executable_path, void* coreclr_handle,
178178
return rv;
179179
}
180180

181+
#define PROPERTY_COUNT 3
182+
181183
static int
182184
mono_droid_runtime_init (const char* executable)
183185
{
@@ -198,34 +200,38 @@ mono_droid_runtime_init (const char* executable)
198200

199201
chdir (g_bundle_path);
200202

201-
// TODO: set TRUSTED_PLATFORM_ASSEMBLIES, APP_PATHS and NATIVE_DLL_SEARCH_DIRECTORIES
203+
struct host_runtime_contract host_contract = {
204+
sizeof(struct host_runtime_contract),
205+
NULL, // context
206+
NULL, // get_runtime_property
207+
NULL, // bundle_proble
208+
NULL, // pinvoke_override
209+
&external_assembly_probe };
202210

203-
const char* appctx_keys[3];
211+
const char* appctx_keys[PROPERTY_COUNT];
204212
appctx_keys[0] = "RUNTIME_IDENTIFIER";
205213
appctx_keys[1] = "APP_CONTEXT_BASE_DIRECTORY";
206-
appctx_keys[2] = "TRUSTED_PLATFORM_ASSEMBLIES";
214+
appctx_keys[2] = "HOST_RUNTIME_CONTRACT";
207215

208-
const char* appctx_values[3];
216+
const char* appctx_values[PROPERTY_COUNT];
209217
appctx_values[0] = ANDROID_RUNTIME_IDENTIFIER;
210218
appctx_values[1] = g_bundle_path;
211-
size_t tpas_len = get_tpas_from_path(g_bundle_path, &appctx_values[2]);
212-
if (tpas_len < 1)
213-
{
214-
LOG_ERROR("Failed to get trusted assemblies from path: %s", g_bundle_path);
215-
return -1;
216-
}
219+
220+
char contract_str[19]; // 0x + 16 hex digits + '\0'
221+
snprintf(contract_str, 19, "0x%zx", (size_t)(&host_contract));
222+
appctx_values[2] = contract_str;
217223

218224
LOG_INFO ("Calling coreclr_initialize");
219225
int rv = coreclr_initialize (
220226
g_executable_path,
221227
executable,
222-
3,
228+
PROPERTY_COUNT,
223229
appctx_keys,
224230
appctx_values,
225231
&g_coreclr_handle,
226232
&g_coreclr_domainId
227233
);
228-
LOG_INFO ("coreclr_initialize returned %d", rv);
234+
LOG_INFO ("coreclr_initialize returned 0x%x", rv);
229235
return rv;
230236
}
231237

@@ -237,7 +243,7 @@ Java_net_dot_MonoRunner_setEnv (JNIEnv* env, jobject thiz, jstring j_key, jstrin
237243

238244
const char *key = (*env)->GetStringUTFChars(env, j_key, 0);
239245
const char *val = (*env)->GetStringUTFChars(env, j_value, 0);
240-
246+
241247
LOG_INFO ("Setting env: %s=%s", key, val);
242248
setenv (key, val, true);
243249
(*env)->ReleaseStringUTFChars(env, j_key, key);
@@ -270,7 +276,7 @@ int
270276
Java_net_dot_MonoRunner_execEntryPoint (JNIEnv* env, jobject thiz, jstring j_entryPointLibName, jobjectArray j_args)
271277
{
272278
LOG_INFO("Java_net_dot_MonoRunner_execEntryPoint (CoreCLR):");
273-
279+
274280
if ((g_bundle_path == NULL) || (g_executable_path == NULL))
275281
{
276282
LOG_ERROR("Bundle path or executable path not set");

0 commit comments

Comments
 (0)