Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9bb026c
NativeAOT runtime skeleton
grendello Sep 1, 2025
28a6a76
Slowly figuring out NativeAOT build complexities
grendello Sep 1, 2025
7ac0af0
Link libc++ statically, include Android naot runtime in linking
grendello Sep 2, 2025
35d1b13
Don't link static libc++, it causes duplicate symbol errors with libs…
grendello Sep 2, 2025
1b262df
Add logging to the naot android runtime
grendello Sep 2, 2025
14d0b50
Typo
grendello Sep 2, 2025
b153cbf
Shared code (gc bridge, os bridge), p/invokes
grendello Sep 3, 2025
b07b6a8
Host code sharing tweaks
grendello Sep 3, 2025
ffd9ce3
Hook up NAOT-specific GC bridge code
grendello Sep 4, 2025
0379c7a
Enable ManagedTypeManager
grendello Sep 4, 2025
700715f
Bring in the rest of relevant changes from #10402
grendello Sep 4, 2025
4f7277a
Temporarily add this, to be removed before merging
grendello Sep 4, 2025
50c97f5
Support logger configuration via the standard XA properties
grendello Sep 5, 2025
290263f
Beginnings of support for calling custom JNI_OnLoad functions
grendello Sep 5, 2025
add9687
Assembly source generation
grendello Sep 8, 2025
aca744f
JNI on-load initialization of libraries
grendello Sep 9, 2025
c779385
Support for environment variables, TBC
grendello Sep 9, 2025
073dd83
[WIP] environment variables + system properties support
grendello Sep 10, 2025
75e3ae2
Environment variables support complete
grendello Sep 10, 2025
8589d38
Address feedback
grendello Sep 11, 2025
41b280e
Remove log spam
grendello Sep 11, 2025
6f36806
Initialize max gref count
grendello Sep 11, 2025
e7a546d
[WIP] System properties
grendello Sep 11, 2025
3944e35
Partial support for system properties
grendello Sep 12, 2025
e0eb174
Fix after rebase
grendello Sep 15, 2025
22128ab
Post-rebase fixes
grendello Sep 15, 2025
a9cd37c
[NativeAOT] Initialize GC threshold much earlier
grendello Sep 15, 2025
1d88a95
System properties work now
grendello Sep 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion samples/NativeAOT/NativeAOT.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@
<!-- Only property required to opt into NativeAOT -->
<PublishAot>true</PublishAot>
</PropertyGroup>
</Project>

<ItemGroup>
<AndroidEnvironment Include="environment.txt" />
</ItemGroup>
</Project>
1 change: 1 addition & 0 deletions samples/NativeAOT/environment.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
naot.system.property=testing 1 2 3
2 changes: 1 addition & 1 deletion samples/NativeAOT/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fi

adb uninstall "${PACKAGE}" || true
adb install -r -d --no-streaming --no-fastdeploy "${APK}"
#adb shell setprop debug.mono.log default,assembly,timing=bare
adb shell setprop debug.mono.log default,assembly,timing=bare
adb logcat -G 128M
adb logcat -c
adb shell am start -S --user "0" -a "android.intent.action.MAIN" -c "android.intent.category.LAUNCHER" -n "${PACKAGE}/${ACTIVITY}" -W
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ static int JNI_OnLoad (IntPtr vm, IntPtr reserved)
try {
AndroidLog.Print (AndroidLogLevel.Info, "JavaInteropRuntime", "JNI_OnLoad()");
XA_Host_NativeAOT_JNI_OnLoad (vm, reserved);
// This must be called before anything else, otherwise we'll see several spurious GC invocations and log messages
// similar to:
//
// 09-15 14:51:01.311 11071 11071 D monodroid-gc: 1 outstanding GREFs. Performing a full GC!
//
JNIEnvInit.NativeAotInitializeMaxGrefGet ();

LogcatTextWriter.Init ();
return (int) JniVersion.v1_6;
}
Expand Down
13 changes: 9 additions & 4 deletions src/Mono.Android/Android.Runtime/JNIEnvInit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,20 @@ static Type TypeGetType (string typeName) =>
androidRuntime!.TypeManager.RegisterNativeMembers (jniType, type, methods);
}

// NOTE: should have different name than `Initialize` to avoid:
// * Assertion at /__w/1/s/src/mono/mono/metadata/icall.c:6258, condition `!only_unmanaged_callers_only' not met
internal static void InitializeJniRuntime (JniRuntime runtime)
// This must be called by NativeAOT before InitializeJniRuntime, as early as possible
internal static void NativeAotInitializeMaxGrefGet ()
{
androidRuntime = runtime;
gref_gc_threshold = RuntimeNativeMethods._monodroid_max_gref_get ();
if (gref_gc_threshold != int.MaxValue) {
gref_gc_threshold = checked((gref_gc_threshold * 9) / 10);
}
}

// NOTE: should have different name than `Initialize` to avoid:
// * Assertion at /__w/1/s/src/mono/mono/metadata/icall.c:6258, condition `!only_unmanaged_callers_only' not met
internal static void InitializeJniRuntime (JniRuntime runtime)
{
androidRuntime = runtime;
SetSynchronizationContext ();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ protected override void Construct (LlvmIrModule module)
SortedDictionary<string, string>? systemProperties = null;
if (envBuilder.SystemProperties.Count > 0) {
systemProperties = new (envBuilder.SystemProperties, StringComparer.Ordinal);
} else {
systemProperties = new (StringComparer.Ordinal);
}

var envVarsBlob = new LlvmIrStringBlob ();
Expand All @@ -47,6 +49,25 @@ protected override void Construct (LlvmIrModule module)
};
module.Add (envVars);
module.AddGlobalVariable ("__naot_android_app_environment_variable_contents", envVarsBlob, LlvmIrVariableOptions.GlobalConstant);

// We reuse the same structure as for environment variables, there's no point in adding a new, identical, one
var sysPropsBlob = new LlvmIrStringBlob ();
List<StructureInstance<LlvmIrHelpers.AppEnvironmentVariable>> appSysProps = LlvmIrHelpers.MakeEnvironmentVariableList (
Log,
systemProperties,
sysPropsBlob,
appEnvironmentVariableStructureInfo
);

var sysPropsCount = new LlvmIrGlobalVariable ((uint)appSysProps.Count, "__naot_android_app_system_property_count");
module.Add (sysPropsCount);

var sysProps = new LlvmIrGlobalVariable (appSysProps, "__naot_android_app_system_properties") {
Comment = " System properties defined by the application",
Options = LlvmIrVariableOptions.GlobalConstant,
};
module.Add (sysProps);
module.AddGlobalVariable ("__naot_android_app_system_property_contents", sysPropsBlob, LlvmIrVariableOptions.GlobalConstant);
}

void MapStructures (LlvmIrModule module)
Expand Down
30 changes: 16 additions & 14 deletions src/native/nativeaot/host/host-environment.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@

using namespace xamarin::android;

struct AppEnvironmentVariable {
uint32_t name_index;
uint32_t value_index;
};

extern "C" {
extern const uint32_t __naot_android_app_environment_variable_count;
extern const AppEnvironmentVariable __naot_android_app_environment_variables[];
Expand All @@ -18,16 +13,23 @@ extern "C" {

void HostEnvironment::init () noexcept
{
if (__naot_android_app_environment_variable_count == 0) {
return;
if (__naot_android_app_environment_variable_count > 0) {
log_debug (LOG_DEFAULT, "Setting environment variables ({})", __naot_android_app_environment_variable_count);
set_values<set_variable> (
__naot_android_app_environment_variable_count,
__naot_android_app_environment_variables,
__naot_android_app_environment_variable_contents
);
}

log_debug (LOG_DEFAULT, "Setting {} environment variables", __naot_android_app_environment_variable_count);
for (size_t i = 0; i < __naot_android_app_environment_variable_count; i++) {
AppEnvironmentVariable const& env_var = __naot_android_app_environment_variables[i];
const char *var_name = &__naot_android_app_environment_variable_contents[env_var.name_index];
const char *var_value = &__naot_android_app_environment_variable_contents[env_var.value_index];

set_variable (var_name, var_value);
if (__naot_android_app_system_property_count == 0) {
return;
}

log_debug (LOG_DEFAULT, "Setting system properties ({})", __naot_android_app_system_property_count);
set_values<set_system_property> (
__naot_android_app_system_property_count,
__naot_android_app_system_properties,
__naot_android_app_system_property_contents
);
}
35 changes: 34 additions & 1 deletion src/native/nativeaot/include/host/host-environment.hh
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,55 @@
#include <runtime-base/logger.hh>

namespace xamarin::android {
struct AppEnvironmentVariable {
uint32_t name_index;
uint32_t value_index;
};

extern "C" {
extern const uint32_t __naot_android_app_system_property_count;
extern const AppEnvironmentVariable __naot_android_app_system_properties[];
extern const char __naot_android_app_system_property_contents[];
}

class HostEnvironment
{
public:
static void init () noexcept;

[[gnu::flatten, gnu::always_inline]]
static void set_variable (const char *name, const char *value) noexcept
{
log_debug (LOG_DEFAULT, " Variable {} = '{}'", name, value);
log_debug (LOG_DEFAULT, " Variable {} = '{}'", optional_string (name), optional_string (value));
if (::setenv (name, value, 1) < 0) {
log_warn (LOG_DEFAULT, "Failed to set environment variable '{}': {}", name, ::strerror (errno));
}
}

[[gnu::flatten, gnu::always_inline]]
static void set_variable (std::string_view const& name, std::string_view const& value) noexcept
{
set_variable (name.data (), value.data ());
}

[[gnu::flatten, gnu::always_inline]]
static void set_system_property (const char *name, const char *value) noexcept
{
// TODO: should we **actually** try to set the system property here? Would that even work? Needs testing
log_debug (LOG_DEFAULT, " System property {} = '{}'", optional_string (name), optional_string (value));
}

private:
template<void (*setter)(const char *name, const char *value) noexcept> [[gnu::flatten, gnu::always_inline]]
static void set_values (uint32_t const& count, AppEnvironmentVariable const (&entries)[], const char (&contents)[]) noexcept
{
for (size_t i = 0; i < count; i++) {
AppEnvironmentVariable const& env_var = entries[i];
const char *var_name = &contents[env_var.name_index];
const char *var_value = &contents[env_var.value_index];

setter (var_name, var_value);
}
}
};
}
22 changes: 20 additions & 2 deletions src/native/nativeaot/runtime-base/android-system.cc
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
#include <cstring>
#include <string_view>

#include <host/host-environment.hh>
#include <runtime-base/android-system.hh>

using namespace xamarin::android;

auto
AndroidSystem::lookup_system_property ([[maybe_unused]] std::string_view const& name, [[maybe_unused]] size_t &value_len) noexcept -> const char*
AndroidSystem::lookup_system_property (std::string_view const& name, [[maybe_unused]] size_t &value_len) noexcept -> const char*
{
return nullptr; // No-op in NativeAOT
if (__naot_android_app_system_property_count == 0) {
return nullptr;
}

for (size_t i = 0; i < __naot_android_app_system_property_count; i++) {
AppEnvironmentVariable const& sys_prop = __naot_android_app_system_properties[i];
const char *prop_name = &__naot_android_app_system_property_contents[sys_prop.name_index];
if (name.compare (prop_name) != 0) {
continue;
}

const char *prop_value = &__naot_android_app_system_property_contents[sys_prop.value_index];
value_len = strlen (prop_value);
return prop_value;
}

return nullptr;
}
Loading