From c4cba4f5fe2a93fb28157db01c13595245c714b7 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Mon, 17 Mar 2025 12:52:25 +0100 Subject: [PATCH 01/39] Bump to NDK r29-beta1 Changes: https://github.com/android/ndk/wiki/Changelog-r29 Most important changes: * Bump LLVM version to 20.0 --- .../xaprepare/ConfigAndData/BuildAndroidPlatforms.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs index 268266ccd0f..c65c7d66186 100644 --- a/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs @@ -5,12 +5,10 @@ namespace Xamarin.Android.Prepare { class BuildAndroidPlatforms { - public const string AndroidNdkVersion = "28c"; - public const string AndroidNdkPkgRevision = "28.2.13676358"; - - public static string NdkMinimumAPI => Context.Instance.Properties.GetRequiredValue (KnownProperties.AndroidMinimumDotNetApiLevel); - public static string NdkMinimumAPILegacy32 => NdkMinimumAPI; - public static string NdkMinimumNonMonoAPI => Context.Instance.Properties.GetRequiredValue (KnownProperties.AndroidMinimumNonMonoApiLevel); + public const string AndroidNdkVersion = "29-beta1"; + public const string AndroidNdkPkgRevision = "29.0.13113456"; + public const int NdkMinimumAPI = 21; + public const int NdkMinimumAPILegacy32 = 21; public static readonly List AllPlatforms = new List { new AndroidPlatform (apiName: "", apiLevel: 1, platformID: "1"), From 335eea3c64d04faecb8713ef7a1f97b5a79d00d3 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Wed, 2 Jul 2025 09:42:40 +0200 Subject: [PATCH 02/39] Bump to r29-beta2 --- .../xaprepare/ConfigAndData/BuildAndroidPlatforms.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs index c65c7d66186..954e0a279b8 100644 --- a/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs @@ -5,8 +5,8 @@ namespace Xamarin.Android.Prepare { class BuildAndroidPlatforms { - public const string AndroidNdkVersion = "29-beta1"; - public const string AndroidNdkPkgRevision = "29.0.13113456"; + public const string AndroidNdkVersion = "29-beta2"; + public const string AndroidNdkPkgRevision = "29.0.13599879"; public const int NdkMinimumAPI = 21; public const int NdkMinimumAPILegacy32 = 21; From 71aada2c4fdf931646df2f22c16e6c7c42c07479 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 7 Aug 2025 16:31:47 +0200 Subject: [PATCH 03/39] Bump to beta3 --- .../xaprepare/ConfigAndData/BuildAndroidPlatforms.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs index 954e0a279b8..c417dcac34d 100644 --- a/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs @@ -5,8 +5,8 @@ namespace Xamarin.Android.Prepare { class BuildAndroidPlatforms { - public const string AndroidNdkVersion = "29-beta2"; - public const string AndroidNdkPkgRevision = "29.0.13599879"; + public const string AndroidNdkVersion = "29-beta3"; + public const string AndroidNdkPkgRevision = "29.0.13846066"; public const int NdkMinimumAPI = 21; public const int NdkMinimumAPILegacy32 = 21; From 5250a855cb602911a86aef995bd4b5af121a3a38 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 16 Oct 2025 09:15:13 +0200 Subject: [PATCH 04/39] Fix after rebase From 8f69a923526dac0637ae0e35eb427c162c022124 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 16 Oct 2025 09:16:11 +0200 Subject: [PATCH 05/39] Bump to the release version --- .../xaprepare/ConfigAndData/BuildAndroidPlatforms.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs index c417dcac34d..3543e0308e0 100644 --- a/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs @@ -5,8 +5,8 @@ namespace Xamarin.Android.Prepare { class BuildAndroidPlatforms { - public const string AndroidNdkVersion = "29-beta3"; - public const string AndroidNdkPkgRevision = "29.0.13846066"; + public const string AndroidNdkVersion = "29"; + public const string AndroidNdkPkgRevision = "29.0.14206865"; public const int NdkMinimumAPI = 21; public const int NdkMinimumAPILegacy32 = 21; From 68cc0730984879e3e3019e4d8028c96e605ab12d Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Fri, 9 Jan 2026 14:30:09 +0100 Subject: [PATCH 06/39] Disable linking against libc++ Step 1. Cause breakage --- .../targets/Microsoft.Android.Sdk.NativeAOT.targets | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets index b4ecd0f5fe6..5d8f0711b11 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets @@ -164,9 +164,9 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. <_NdkLibs Include="@(RuntimePackAsset->WithMetadataValue('Filename', 'libnaot-android.$(Configuration.ToLower())-static-$(Configuration.ToLower())'))" /> - <_NdkLibs Include="$(_NdkSysrootDir)libc++_static.a" /> - <_NdkLibs Include="$(_NdkSysrootDir)libc++abi.a" /> - + + + From 9056a4d92a64f8d7026df822fcdfa3149a55759a Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Fri, 9 Jan 2026 20:46:10 +0100 Subject: [PATCH 07/39] libc++ bits and pieces --- src-ThirdParty/llvm/libcxx/src/new.cpp | 226 ++++++++++++++++++ .../llvm/libcxx/src/verbose_abort.cpp | 65 +++++ src/native/nativeaot/cxx-abi/string.cc | 12 + src/native/nativeaot/cxx-abi/terminate.cc | 17 ++ 4 files changed, 320 insertions(+) create mode 100644 src-ThirdParty/llvm/libcxx/src/new.cpp create mode 100644 src-ThirdParty/llvm/libcxx/src/verbose_abort.cpp create mode 100644 src/native/nativeaot/cxx-abi/string.cc create mode 100644 src/native/nativeaot/cxx-abi/terminate.cc diff --git a/src-ThirdParty/llvm/libcxx/src/new.cpp b/src-ThirdParty/llvm/libcxx/src/new.cpp new file mode 100644 index 00000000000..e010fe4c4f1 --- /dev/null +++ b/src-ThirdParty/llvm/libcxx/src/new.cpp @@ -0,0 +1,226 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "include/overridable_function.h" +#include <__assert> +#include <__memory/aligned_alloc.h> +#include +#include +#include + +#if !defined(__GLIBCXX__) && !defined(_LIBCPP_ABI_VCRUNTIME) + +// The code below is copied as-is into libc++abi's libcxxabi/src/stdlib_new_delete.cpp +// file. The version in this file is the canonical one. + +inline void __throw_bad_alloc_shim() { std::__throw_bad_alloc(); } + +# define _LIBCPP_ASSERT_SHIM(expr, str) _LIBCPP_ASSERT(expr, str) + +// ------------------ BEGIN COPY ------------------ +// Implement all new and delete operators as weak definitions +// in this shared library, so that they can be overridden by programs +// that define non-weak copies of the functions. + +static void* operator_new_impl(std::size_t size) { + if (size == 0) + size = 1; + void* p; + while ((p = std::malloc(size)) == nullptr) { + // If malloc fails and there is a new_handler, + // call it to try free up memory. + std::new_handler nh = std::get_new_handler(); + if (nh) + nh(); + else + break; + } + return p; +} + +_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void* operator new(std::size_t size) _THROW_BAD_ALLOC { + void* p = operator_new_impl(size); + if (p == nullptr) + __throw_bad_alloc_shim(); + return p; +} + +_LIBCPP_WEAK void* operator new(size_t size, const std::nothrow_t&) noexcept { +# if !_LIBCPP_HAS_EXCEPTIONS +# if _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION + _LIBCPP_ASSERT_SHIM( + !std::__is_function_overridden(static_cast(&operator new)), + "libc++ was configured with exceptions disabled and `operator new(size_t)` has been overridden, " + "but `operator new(size_t, nothrow_t)` has not been overridden. This is problematic because " + "`operator new(size_t, nothrow_t)` must call `operator new(size_t)`, which will terminate in case " + "it fails to allocate, making it impossible for `operator new(size_t, nothrow_t)` to fulfill its " + "contract (since it should return nullptr upon failure). Please make sure you override " + "`operator new(size_t, nothrow_t)` as well."); +# endif + + return operator_new_impl(size); +# else + void* p = nullptr; + try { + p = ::operator new(size); + } catch (...) { + } + return p; +# endif +} + +_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void* operator new[](size_t size) _THROW_BAD_ALLOC { + return ::operator new(size); +} + +_LIBCPP_WEAK void* operator new[](size_t size, const std::nothrow_t&) noexcept { +# if !_LIBCPP_HAS_EXCEPTIONS +# if _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION + _LIBCPP_ASSERT_SHIM( + !std::__is_function_overridden(static_cast(&operator new[])), + "libc++ was configured with exceptions disabled and `operator new[](size_t)` has been overridden, " + "but `operator new[](size_t, nothrow_t)` has not been overridden. This is problematic because " + "`operator new[](size_t, nothrow_t)` must call `operator new[](size_t)`, which will terminate in case " + "it fails to allocate, making it impossible for `operator new[](size_t, nothrow_t)` to fulfill its " + "contract (since it should return nullptr upon failure). Please make sure you override " + "`operator new[](size_t, nothrow_t)` as well."); +# endif + + return operator_new_impl(size); +# else + void* p = nullptr; + try { + p = ::operator new[](size); + } catch (...) { + } + return p; +# endif +} + +_LIBCPP_WEAK void operator delete(void* ptr) noexcept { std::free(ptr); } + +_LIBCPP_WEAK void operator delete(void* ptr, const std::nothrow_t&) noexcept { ::operator delete(ptr); } + +_LIBCPP_WEAK void operator delete(void* ptr, size_t) noexcept { ::operator delete(ptr); } + +_LIBCPP_WEAK void operator delete[](void* ptr) noexcept { ::operator delete(ptr); } + +_LIBCPP_WEAK void operator delete[](void* ptr, const std::nothrow_t&) noexcept { ::operator delete[](ptr); } + +_LIBCPP_WEAK void operator delete[](void* ptr, size_t) noexcept { ::operator delete[](ptr); } + +# if _LIBCPP_HAS_LIBRARY_ALIGNED_ALLOCATION + +static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignment) { + if (size == 0) + size = 1; + if (static_cast(alignment) < sizeof(void*)) + alignment = std::align_val_t(sizeof(void*)); + + // Try allocating memory. If allocation fails and there is a new_handler, + // call it to try free up memory, and try again until it succeeds, or until + // the new_handler decides to terminate. + void* p; + while ((p = std::__libcpp_aligned_alloc(static_cast(alignment), size)) == nullptr) { + std::new_handler nh = std::get_new_handler(); + if (nh) + nh(); + else + break; + } + return p; +} + +_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void* +operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC { + void* p = operator_new_aligned_impl(size, alignment); + if (p == nullptr) + __throw_bad_alloc_shim(); + return p; +} + +_LIBCPP_WEAK void* operator new(size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept { +# if !_LIBCPP_HAS_EXCEPTIONS +# if _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION + _LIBCPP_ASSERT_SHIM( + !std::__is_function_overridden(static_cast(&operator new)), + "libc++ was configured with exceptions disabled and `operator new(size_t, align_val_t)` has been overridden, " + "but `operator new(size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because " + "`operator new(size_t, align_val_t, nothrow_t)` must call `operator new(size_t, align_val_t)`, which will " + "terminate in case it fails to allocate, making it impossible for `operator new(size_t, align_val_t, nothrow_t)` " + "to fulfill its contract (since it should return nullptr upon failure). Please make sure you override " + "`operator new(size_t, align_val_t, nothrow_t)` as well."); +# endif + + return operator_new_aligned_impl(size, alignment); +# else + void* p = nullptr; + try { + p = ::operator new(size, alignment); + } catch (...) { + } + return p; +# endif +} + +_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void* +operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC { + return ::operator new(size, alignment); +} + +_LIBCPP_WEAK void* operator new[](size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept { +# if !_LIBCPP_HAS_EXCEPTIONS +# if _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION + _LIBCPP_ASSERT_SHIM( + !std::__is_function_overridden(static_cast(&operator new[])), + "libc++ was configured with exceptions disabled and `operator new[](size_t, align_val_t)` has been overridden, " + "but `operator new[](size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because " + "`operator new[](size_t, align_val_t, nothrow_t)` must call `operator new[](size_t, align_val_t)`, which will " + "terminate in case it fails to allocate, making it impossible for `operator new[](size_t, align_val_t, " + "nothrow_t)` to fulfill its contract (since it should return nullptr upon failure). Please make sure you " + "override " + "`operator new[](size_t, align_val_t, nothrow_t)` as well."); +# endif + + return operator_new_aligned_impl(size, alignment); +# else + void* p = nullptr; + try { + p = ::operator new[](size, alignment); + } catch (...) { + } + return p; +# endif +} + +_LIBCPP_WEAK void operator delete(void* ptr, std::align_val_t) noexcept { std::__libcpp_aligned_free(ptr); } + +_LIBCPP_WEAK void operator delete(void* ptr, std::align_val_t alignment, const std::nothrow_t&) noexcept { + ::operator delete(ptr, alignment); +} + +_LIBCPP_WEAK void operator delete(void* ptr, size_t, std::align_val_t alignment) noexcept { + ::operator delete(ptr, alignment); +} + +_LIBCPP_WEAK void operator delete[](void* ptr, std::align_val_t alignment) noexcept { + ::operator delete(ptr, alignment); +} + +_LIBCPP_WEAK void operator delete[](void* ptr, std::align_val_t alignment, const std::nothrow_t&) noexcept { + ::operator delete[](ptr, alignment); +} + +_LIBCPP_WEAK void operator delete[](void* ptr, size_t, std::align_val_t alignment) noexcept { + ::operator delete[](ptr, alignment); +} + +# endif // _LIBCPP_HAS_LIBRARY_ALIGNED_ALLOCATION +// ------------------ END COPY ------------------ + +#endif // !__GLIBCXX__ && !_LIBCPP_ABI_VCRUNTIME diff --git a/src-ThirdParty/llvm/libcxx/src/verbose_abort.cpp b/src-ThirdParty/llvm/libcxx/src/verbose_abort.cpp new file mode 100644 index 00000000000..fd6bc4943d6 --- /dev/null +++ b/src-ThirdParty/llvm/libcxx/src/verbose_abort.cpp @@ -0,0 +1,65 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <__config> +#include <__verbose_abort> +#include +#include +#include + +#ifdef __BIONIC__ +# include +extern "C" void android_set_abort_message(const char* msg); +#endif // __BIONIC__ + +#if defined(__APPLE__) && __has_include() +# include +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +_LIBCPP_WEAK void __libcpp_verbose_abort(char const* format, ...) _LIBCPP_VERBOSE_ABORT_NOEXCEPT { + // Write message to stderr. We do this before formatting into a + // buffer so that we still get some information out if that fails. + { + va_list list; + va_start(list, format); + std::vfprintf(stderr, format, list); + va_end(list); + } + + // Format the arguments into an allocated buffer for CrashReport & friends. + // We leak the buffer on purpose, since we're about to abort() anyway. + char* buffer; + (void)buffer; + va_list list; + va_start(list, format); + +#if defined(__APPLE__) && __has_include() + // Note that we should technically synchronize accesses here (by e.g. taking a lock), + // however concretely we're only setting a pointer, so the likelihood of a race here + // is low. + vasprintf(&buffer, format, list); + CRSetCrashLogMessage(buffer); +#elif defined(__BIONIC__) + vasprintf(&buffer, format, list); + + // Show error in tombstone. + android_set_abort_message(buffer); + + // Show error in logcat. + openlog("libc++", 0, 0); + syslog(LOG_CRIT, "%s", buffer); + closelog(); +#endif + va_end(list); + + std::abort(); +} + +_LIBCPP_END_NAMESPACE_STD diff --git a/src/native/nativeaot/cxx-abi/string.cc b/src/native/nativeaot/cxx-abi/string.cc new file mode 100644 index 00000000000..30d528e14ac --- /dev/null +++ b/src/native/nativeaot/cxx-abi/string.cc @@ -0,0 +1,12 @@ +// +// Defining the macro will make the the explicit instantations below truely hidden +// +#define _LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS + +#include + +_LIBCPP_BEGIN_NAMESPACE_STD + +template class __attribute__ ((__visibility__("hidden"))) basic_string; + +_LIBCPP_END_NAMESPACE_STD diff --git a/src/native/nativeaot/cxx-abi/terminate.cc b/src/native/nativeaot/cxx-abi/terminate.cc new file mode 100644 index 00000000000..8a9700dad94 --- /dev/null +++ b/src/native/nativeaot/cxx-abi/terminate.cc @@ -0,0 +1,17 @@ +// +// Simple implementation of std::terminate() for Xamarin.Android +// +// Does NOT support terminate handlers, since we don't use them. +// +#include +#include + +#include "helpers.hh" + +namespace std { + [[noreturn]] void + terminate () noexcept + { + xamarin::android::Helpers::abort_application ("std::terminate() called. Aborting."); + } +} From 232cfd8de41a90dc33c2fd605a4a8ddb4db9150a Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Mon, 12 Jan 2026 18:45:47 +0100 Subject: [PATCH 08/39] Still broken, baby steps --- src-ThirdParty/llvm/libcxx/src/new.cpp | 226 ---------------------- src/native/CMakeLists.txt | 17 +- src/native/CMakePresets.json.in | 30 +-- src/native/native.targets | 1 - src/native/nativeaot/cxx-abi/new.cc | 181 +++++++++++++++++ src/native/nativeaot/cxx-abi/terminate.cc | 2 +- src/native/nativeaot/host/CMakeLists.txt | 13 ++ 7 files changed, 209 insertions(+), 261 deletions(-) delete mode 100644 src-ThirdParty/llvm/libcxx/src/new.cpp create mode 100644 src/native/nativeaot/cxx-abi/new.cc diff --git a/src-ThirdParty/llvm/libcxx/src/new.cpp b/src-ThirdParty/llvm/libcxx/src/new.cpp deleted file mode 100644 index e010fe4c4f1..00000000000 --- a/src-ThirdParty/llvm/libcxx/src/new.cpp +++ /dev/null @@ -1,226 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "include/overridable_function.h" -#include <__assert> -#include <__memory/aligned_alloc.h> -#include -#include -#include - -#if !defined(__GLIBCXX__) && !defined(_LIBCPP_ABI_VCRUNTIME) - -// The code below is copied as-is into libc++abi's libcxxabi/src/stdlib_new_delete.cpp -// file. The version in this file is the canonical one. - -inline void __throw_bad_alloc_shim() { std::__throw_bad_alloc(); } - -# define _LIBCPP_ASSERT_SHIM(expr, str) _LIBCPP_ASSERT(expr, str) - -// ------------------ BEGIN COPY ------------------ -// Implement all new and delete operators as weak definitions -// in this shared library, so that they can be overridden by programs -// that define non-weak copies of the functions. - -static void* operator_new_impl(std::size_t size) { - if (size == 0) - size = 1; - void* p; - while ((p = std::malloc(size)) == nullptr) { - // If malloc fails and there is a new_handler, - // call it to try free up memory. - std::new_handler nh = std::get_new_handler(); - if (nh) - nh(); - else - break; - } - return p; -} - -_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void* operator new(std::size_t size) _THROW_BAD_ALLOC { - void* p = operator_new_impl(size); - if (p == nullptr) - __throw_bad_alloc_shim(); - return p; -} - -_LIBCPP_WEAK void* operator new(size_t size, const std::nothrow_t&) noexcept { -# if !_LIBCPP_HAS_EXCEPTIONS -# if _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION - _LIBCPP_ASSERT_SHIM( - !std::__is_function_overridden(static_cast(&operator new)), - "libc++ was configured with exceptions disabled and `operator new(size_t)` has been overridden, " - "but `operator new(size_t, nothrow_t)` has not been overridden. This is problematic because " - "`operator new(size_t, nothrow_t)` must call `operator new(size_t)`, which will terminate in case " - "it fails to allocate, making it impossible for `operator new(size_t, nothrow_t)` to fulfill its " - "contract (since it should return nullptr upon failure). Please make sure you override " - "`operator new(size_t, nothrow_t)` as well."); -# endif - - return operator_new_impl(size); -# else - void* p = nullptr; - try { - p = ::operator new(size); - } catch (...) { - } - return p; -# endif -} - -_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void* operator new[](size_t size) _THROW_BAD_ALLOC { - return ::operator new(size); -} - -_LIBCPP_WEAK void* operator new[](size_t size, const std::nothrow_t&) noexcept { -# if !_LIBCPP_HAS_EXCEPTIONS -# if _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION - _LIBCPP_ASSERT_SHIM( - !std::__is_function_overridden(static_cast(&operator new[])), - "libc++ was configured with exceptions disabled and `operator new[](size_t)` has been overridden, " - "but `operator new[](size_t, nothrow_t)` has not been overridden. This is problematic because " - "`operator new[](size_t, nothrow_t)` must call `operator new[](size_t)`, which will terminate in case " - "it fails to allocate, making it impossible for `operator new[](size_t, nothrow_t)` to fulfill its " - "contract (since it should return nullptr upon failure). Please make sure you override " - "`operator new[](size_t, nothrow_t)` as well."); -# endif - - return operator_new_impl(size); -# else - void* p = nullptr; - try { - p = ::operator new[](size); - } catch (...) { - } - return p; -# endif -} - -_LIBCPP_WEAK void operator delete(void* ptr) noexcept { std::free(ptr); } - -_LIBCPP_WEAK void operator delete(void* ptr, const std::nothrow_t&) noexcept { ::operator delete(ptr); } - -_LIBCPP_WEAK void operator delete(void* ptr, size_t) noexcept { ::operator delete(ptr); } - -_LIBCPP_WEAK void operator delete[](void* ptr) noexcept { ::operator delete(ptr); } - -_LIBCPP_WEAK void operator delete[](void* ptr, const std::nothrow_t&) noexcept { ::operator delete[](ptr); } - -_LIBCPP_WEAK void operator delete[](void* ptr, size_t) noexcept { ::operator delete[](ptr); } - -# if _LIBCPP_HAS_LIBRARY_ALIGNED_ALLOCATION - -static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignment) { - if (size == 0) - size = 1; - if (static_cast(alignment) < sizeof(void*)) - alignment = std::align_val_t(sizeof(void*)); - - // Try allocating memory. If allocation fails and there is a new_handler, - // call it to try free up memory, and try again until it succeeds, or until - // the new_handler decides to terminate. - void* p; - while ((p = std::__libcpp_aligned_alloc(static_cast(alignment), size)) == nullptr) { - std::new_handler nh = std::get_new_handler(); - if (nh) - nh(); - else - break; - } - return p; -} - -_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void* -operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC { - void* p = operator_new_aligned_impl(size, alignment); - if (p == nullptr) - __throw_bad_alloc_shim(); - return p; -} - -_LIBCPP_WEAK void* operator new(size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept { -# if !_LIBCPP_HAS_EXCEPTIONS -# if _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION - _LIBCPP_ASSERT_SHIM( - !std::__is_function_overridden(static_cast(&operator new)), - "libc++ was configured with exceptions disabled and `operator new(size_t, align_val_t)` has been overridden, " - "but `operator new(size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because " - "`operator new(size_t, align_val_t, nothrow_t)` must call `operator new(size_t, align_val_t)`, which will " - "terminate in case it fails to allocate, making it impossible for `operator new(size_t, align_val_t, nothrow_t)` " - "to fulfill its contract (since it should return nullptr upon failure). Please make sure you override " - "`operator new(size_t, align_val_t, nothrow_t)` as well."); -# endif - - return operator_new_aligned_impl(size, alignment); -# else - void* p = nullptr; - try { - p = ::operator new(size, alignment); - } catch (...) { - } - return p; -# endif -} - -_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void* -operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC { - return ::operator new(size, alignment); -} - -_LIBCPP_WEAK void* operator new[](size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept { -# if !_LIBCPP_HAS_EXCEPTIONS -# if _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION - _LIBCPP_ASSERT_SHIM( - !std::__is_function_overridden(static_cast(&operator new[])), - "libc++ was configured with exceptions disabled and `operator new[](size_t, align_val_t)` has been overridden, " - "but `operator new[](size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because " - "`operator new[](size_t, align_val_t, nothrow_t)` must call `operator new[](size_t, align_val_t)`, which will " - "terminate in case it fails to allocate, making it impossible for `operator new[](size_t, align_val_t, " - "nothrow_t)` to fulfill its contract (since it should return nullptr upon failure). Please make sure you " - "override " - "`operator new[](size_t, align_val_t, nothrow_t)` as well."); -# endif - - return operator_new_aligned_impl(size, alignment); -# else - void* p = nullptr; - try { - p = ::operator new[](size, alignment); - } catch (...) { - } - return p; -# endif -} - -_LIBCPP_WEAK void operator delete(void* ptr, std::align_val_t) noexcept { std::__libcpp_aligned_free(ptr); } - -_LIBCPP_WEAK void operator delete(void* ptr, std::align_val_t alignment, const std::nothrow_t&) noexcept { - ::operator delete(ptr, alignment); -} - -_LIBCPP_WEAK void operator delete(void* ptr, size_t, std::align_val_t alignment) noexcept { - ::operator delete(ptr, alignment); -} - -_LIBCPP_WEAK void operator delete[](void* ptr, std::align_val_t alignment) noexcept { - ::operator delete(ptr, alignment); -} - -_LIBCPP_WEAK void operator delete[](void* ptr, std::align_val_t alignment, const std::nothrow_t&) noexcept { - ::operator delete[](ptr, alignment); -} - -_LIBCPP_WEAK void operator delete[](void* ptr, size_t, std::align_val_t alignment) noexcept { - ::operator delete[](ptr, alignment); -} - -# endif // _LIBCPP_HAS_LIBRARY_ALIGNED_ALLOCATION -// ------------------ END COPY ------------------ - -#endif // !__GLIBCXX__ && !_LIBCPP_ABI_VCRUNTIME diff --git a/src/native/CMakeLists.txt b/src/native/CMakeLists.txt index ba9267c5623..38865e87c23 100644 --- a/src/native/CMakeLists.txt +++ b/src/native/CMakeLists.txt @@ -156,7 +156,15 @@ include(CheckLinkerFlag) # # General config # +file(REAL_PATH "../../" REPO_ROOT_DIR) +set(EXTERNAL_DIR "${REPO_ROOT_DIR}/external") +set(JAVA_INTEROP_SRC_PATH "${EXTERNAL_DIR}/Java.Interop/src/java-interop") +set(LIBUNWIND_SOURCE_DIR "${EXTERNAL_DIR}/libunwind") +set(ROBIN_MAP_DIR "${EXTERNAL_DIR}/robin-map") + set(XA_BUILD_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../bin/Build${XA_BUILD_CONFIGURATION}") +set(THIRD_PARTY_SOURCE_DIR "${REPO_ROOT_DIR}/src-ThirdParty") + include("${XA_BUILD_DIR}/xa_build_configuration.cmake") # @@ -218,13 +226,6 @@ else() message(FATAL "${ANDROID_ABI} is not supported for .NET 6+ builds") endif() - -file(REAL_PATH "../../" REPO_ROOT_DIR) -set(EXTERNAL_DIR "${REPO_ROOT_DIR}/external") -set(JAVA_INTEROP_SRC_PATH "${EXTERNAL_DIR}/Java.Interop/src/java-interop") -set(LIBUNWIND_SOURCE_DIR "${EXTERNAL_DIR}/libunwind") -set(ROBIN_MAP_DIR "${EXTERNAL_DIR}/robin-map") - # # Include directories # @@ -240,7 +241,7 @@ else() set(CLR_REPO_ROOT_PATH "${LOCAL_CORECLR_PATH}/../..") set(RUNTIME_INCLUDE_DIR "${CLR_REPO_ROOT_PATH}/src/native/corehost;${CLR_REPO_ROOT_PATH}/src/coreclr/hosts/inc") else() - set(RUNTIME_INCLUDE_DIR ${REPO_ROOT_DIR}/src-ThirdParty/dotnet/runtime) + set(RUNTIME_INCLUDE_DIR ${THIRD_PARTY_SOURCE_DIR}/dotnet/runtime) endif() endif() diff --git a/src/native/CMakePresets.json.in b/src/native/CMakePresets.json.in index fb5c77267de..8dd43ee2815 100644 --- a/src/native/CMakePresets.json.in +++ b/src/native/CMakePresets.json.in @@ -59,7 +59,7 @@ "hidden": true, "inherits": "common", "cacheVariables": { - "ANDROID_STL": "c++_static", + "ANDROID_STL": "none", "ANDROID_CPP_FEATURES": "no-rtti no-exceptions" } }, @@ -219,12 +219,7 @@ { "name": "nativeaot-default-debug-arm64-v8a", - "inherits": ["nativeaot-default-common", "common-debug", "nonmono-common-arm64-v8a"] - }, - - { - "name": "coreclr-default-debug-arm64-v8a", - "inherits": ["default-common", "common-debug", "nonmono-common-arm64-v8a"] + "inherits": ["nativeaot-default-common", "common-debug", "common-arm64-v8a"] }, { @@ -234,12 +229,7 @@ { "name": "nativeaot-default-release-arm64-v8a", - "inherits": ["nativeaot-default-common", "common-release", "nonmono-common-arm64-v8a"] - }, - - { - "name": "coreclr-default-release-arm64-v8a", - "inherits": ["default-common", "common-release", "nonmono-common-arm64-v8a"] + "inherits": ["nativeaot-default-common", "common-release", "common-arm64-v8a"] }, { @@ -327,12 +317,7 @@ { "name": "nativeaot-default-debug-x86_64", - "inherits": ["nativeaot-default-common", "common-debug", "nonmono-common-x86_64"] - }, - - { - "name": "coreclr-default-debug-x86_64", - "inherits": ["default-common", "common-debug", "nonmono-common-x86_64"] + "inherits": ["nativeaot-default-common", "common-debug", "common-x86_64"] }, { @@ -342,12 +327,7 @@ { "name": "nativeaot-default-release-x86_64", - "inherits": ["nativeaot-default-common", "common-release", "nonmono-common-x86_64"] - }, - - { - "name": "coreclr-default-release-x86_64", - "inherits": ["default-common", "common-release", "nonmono-common-x86_64"] + "inherits": ["nativeaot-default-common", "common-release", "common-x86_64"] }, { diff --git a/src/native/native.targets b/src/native/native.targets index 1f2e4d3cbe6..396e1d44b1f 100644 --- a/src/native/native.targets +++ b/src/native/native.targets @@ -158,7 +158,6 @@ Outputs="@(_ConfigureRuntimesOutputs)"> <_PresetPrefix Condition=" '$(CMakeRuntimeFlavor)' == 'NativeAOT' ">nativeaot- - <_PresetPrefix Condition=" '$(CMakeRuntimeFlavor)' == 'CoreCLR' ">coreclr- <_NoInline Condition=" '$(DoNotInlineMonodroid)' == 'true' ">-DDONT_INLINE=ON <_NoStrip Condition=" '$(DoNotStripMonodroid)' == 'true' ">-DSTRIP_DEBUG=OFF <_LocalDotNetRuntimePath Condition=" '$(CLRLocalRuntimePath)' != '' And '$(CMakeRuntimeFlavor)' == 'CoreCLR' ">-DLOCAL_CORECLR_PATH="$(CLRLocalRuntimePath)" diff --git a/src/native/nativeaot/cxx-abi/new.cc b/src/native/nativeaot/cxx-abi/new.cc new file mode 100644 index 00000000000..5aa1298aff0 --- /dev/null +++ b/src/native/nativeaot/cxx-abi/new.cc @@ -0,0 +1,181 @@ +// +// Code based on the original libc++ `libcxx/src/new.cpp` source, modified +// heavily for our use +// + +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <__assert> +#include <__memory/aligned_alloc.h> +#include +#include +#include + +#include + +#define _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE [[gnu::section ("__TEXT,__lcxx_override,regular,pure_instructions")]] + +namespace { + void* operator_new_impl(std::size_t size) + { + if (size == 0) { + size = 1; + } + + return std::malloc (size); + } +} + +_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE +[[gnu::weak]] +void* operator new (std::size_t size) +{ + void *p = operator_new_impl (size); + if (p == nullptr) { + xamarin::android::Helpers::abort_application ("Out of memory in the `new` operator"); + } + + return p; +} + +[[gnu::weak]] +void* operator new (size_t size, const std::nothrow_t&) noexcept +{ + return operator_new_impl (size); +} + +_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE +[[gnu::weak]] +void* operator new[] (size_t size) +{ + return ::operator new (size); +} + +[[gnu::weak]] +void* operator new[] (size_t size, const std::nothrow_t&) noexcept +{ + return operator_new_impl (size); +} + +[[gnu::weak]] +void operator delete(void* ptr) noexcept +{ + std::free (ptr); +} + +[[gnu::weak]] +void operator delete (void* ptr, const std::nothrow_t&) noexcept +{ + ::operator delete(ptr); +} + +[[gnu::weak]] +void operator delete (void* ptr, size_t) noexcept +{ + ::operator delete(ptr); +} + +[[gnu::weak]] +void operator delete[] (void* ptr) noexcept +{ + ::operator delete(ptr); +} + +[[gnu::weak]] +void operator delete[] (void* ptr, const std::nothrow_t&) noexcept +{ + ::operator delete[](ptr); +} + +[[gnu::weak]] +void operator delete[] (void* ptr, size_t) noexcept +{ + ::operator delete[](ptr); +} + +namespace { + void* operator_new_aligned_impl (std::size_t size, std::align_val_t alignment) + { + if (size == 0) { + size = 1; + } + + if (static_cast(alignment) < sizeof(void*)) { + alignment = std::align_val_t(sizeof(void*)); + } + + return std::__libcpp_aligned_alloc (static_cast(alignment), size); + } +} + +_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE +[[gnu::weak]] +void* operator new (std::size_t size, std::align_val_t alignment) +{ + void* p = operator_new_aligned_impl (size, alignment); + if (p == nullptr) { + xamarin::android::Helpers::abort_application ("Out of memory in the aligned `new` operator"); + } + + return p; +} + +[[gnu::weak]] +void* operator new (size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept +{ + return operator_new_aligned_impl (size, alignment); +} + +_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE +[[gnu::weak]] +void* operator new[] (size_t size, std::align_val_t alignment) +{ + return ::operator new (size, alignment); +} + +[[gnu::weak]] +void* operator new[] (size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept +{ + return operator_new_aligned_impl (size, alignment); +} + +[[gnu::weak]] +void operator delete (void* ptr, std::align_val_t) noexcept +{ + std::__libcpp_aligned_free(ptr); +} + +[[gnu::weak]] +void operator delete (void* ptr, std::align_val_t alignment, const std::nothrow_t&) noexcept +{ + ::operator delete(ptr, alignment); +} + +[[gnu::weak]] +void operator delete (void* ptr, size_t, std::align_val_t alignment) noexcept +{ + ::operator delete(ptr, alignment); +} + +[[gnu::weak]] +void operator delete[] (void* ptr, std::align_val_t alignment) noexcept +{ + ::operator delete(ptr, alignment); +} + +[[gnu::weak]] +void operator delete[] (void* ptr, std::align_val_t alignment, const std::nothrow_t&) noexcept +{ + ::operator delete[](ptr, alignment); +} + +[[gnu::weak]] void operator delete[] (void* ptr, size_t, std::align_val_t alignment) noexcept +{ + ::operator delete[](ptr, alignment); +} diff --git a/src/native/nativeaot/cxx-abi/terminate.cc b/src/native/nativeaot/cxx-abi/terminate.cc index 8a9700dad94..772eb2e5b5b 100644 --- a/src/native/nativeaot/cxx-abi/terminate.cc +++ b/src/native/nativeaot/cxx-abi/terminate.cc @@ -6,7 +6,7 @@ #include #include -#include "helpers.hh" +#include namespace std { [[noreturn]] void diff --git a/src/native/nativeaot/host/CMakeLists.txt b/src/native/nativeaot/host/CMakeLists.txt index 9f4ce16fb25..4bd9ceae924 100644 --- a/src/native/nativeaot/host/CMakeLists.txt +++ b/src/native/nativeaot/host/CMakeLists.txt @@ -14,6 +14,10 @@ if(DEBUG_BUILD) set(CMAKE_CXX_FLAGS_DEBUG ${XA_COMPILER_FLAGS_DEBUG}) endif() +# Extra source directories +set(LIBCXX_SOURCE_DIR "${THIRD_PARTY_SOURCE_DIR}/llvm/libcxx/src") +set(CXXABI_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../cxx-abi) + # Library directories set(XA_LIBRARY_OUTPUT_DIRECTORY "${XA_LIB_TOP_DIR}/lib/${ANDROID_RID}") @@ -35,6 +39,15 @@ set(XAMARIN_MONODROID_SOURCES ../runtime-base/android-system.cc + # libc++ sources + ${LIBCXX_SOURCE_DIR}/thread.cpp + ${LIBCXX_SOURCE_DIR}/verbose_abort.cpp + + # C++ ABI sources + ${CXXABI_SOURCE_DIR}/new.cc + ${CXXABI_SOURCE_DIR}/string.cc + ${CXXABI_SOURCE_DIR}/terminate.cc + # Sources from CoreCLR host ${CLR_SOURCES_PATH}/host/bridge-processing.cc ${CLR_SOURCES_PATH}/host/gc-bridge.cc From 344acd638e3293db355b0cdf98e227105c7e8e41 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Mon, 12 Jan 2026 19:16:22 +0100 Subject: [PATCH 09/39] Not there --- src/native/nativeaot/host/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/native/nativeaot/host/CMakeLists.txt b/src/native/nativeaot/host/CMakeLists.txt index 4bd9ceae924..78670aba9f5 100644 --- a/src/native/nativeaot/host/CMakeLists.txt +++ b/src/native/nativeaot/host/CMakeLists.txt @@ -40,7 +40,6 @@ set(XAMARIN_MONODROID_SOURCES ../runtime-base/android-system.cc # libc++ sources - ${LIBCXX_SOURCE_DIR}/thread.cpp ${LIBCXX_SOURCE_DIR}/verbose_abort.cpp # C++ ABI sources From 0262ef4145fe62e2c02616d82c686befa3981d8d Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Mon, 12 Jan 2026 19:21:36 +0100 Subject: [PATCH 10/39] Better, but still broken. TBC tomorrow --- src/native/nativeaot/host/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/native/nativeaot/host/CMakeLists.txt b/src/native/nativeaot/host/CMakeLists.txt index 78670aba9f5..5a542111ace 100644 --- a/src/native/nativeaot/host/CMakeLists.txt +++ b/src/native/nativeaot/host/CMakeLists.txt @@ -16,6 +16,7 @@ endif() # Extra source directories set(LIBCXX_SOURCE_DIR "${THIRD_PARTY_SOURCE_DIR}/llvm/libcxx/src") +set(BIONIC_SOURCE_DIR "${THIRD_PARTY_SOURCE_DIR}/bionic") set(CXXABI_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../cxx-abi) # Library directories @@ -39,6 +40,9 @@ set(XAMARIN_MONODROID_SOURCES ../runtime-base/android-system.cc + # Bionic sources + ${BIONIC_SOURCE_DIR}//cxa_guard.cc + # libc++ sources ${LIBCXX_SOURCE_DIR}/verbose_abort.cpp From fb60832f1f1b55be62ae846574aed17900fded53 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Tue, 13 Jan 2026 11:11:46 +0100 Subject: [PATCH 11/39] More libc++ imported. Build still broken. --- .../llvm/libcxx/src/condition_variable.cpp | 71 +++++++ .../src/condition_variable_destructor.cpp | 40 ++++ .../llvm/libcxx/src/include/atomic_support.h | 132 +++++++++++++ src-ThirdParty/llvm/libcxx/src/memory.cpp | 148 +++++++++++++++ src-ThirdParty/llvm/libcxx/src/mutex.cpp | 145 +++++++++++++++ .../llvm/libcxx/src/mutex_destructor.cpp | 42 +++++ src-ThirdParty/llvm/libcxx/src/thread.cpp | 173 ++++++++++++++++++ .../llvm/libcxxabi/src/stdlib_exception.cpp | 70 +++++++ src/native/native.targets | 7 +- src/native/nativeaot/cxx-abi/stdexcept.cc | 17 ++ src/native/nativeaot/cxx-abi/system_error.cc | 19 ++ src/native/nativeaot/host/CMakeLists.txt | 22 ++- 12 files changed, 878 insertions(+), 8 deletions(-) create mode 100644 src-ThirdParty/llvm/libcxx/src/condition_variable.cpp create mode 100644 src-ThirdParty/llvm/libcxx/src/condition_variable_destructor.cpp create mode 100644 src-ThirdParty/llvm/libcxx/src/include/atomic_support.h create mode 100644 src-ThirdParty/llvm/libcxx/src/memory.cpp create mode 100644 src-ThirdParty/llvm/libcxx/src/mutex.cpp create mode 100644 src-ThirdParty/llvm/libcxx/src/mutex_destructor.cpp create mode 100644 src-ThirdParty/llvm/libcxx/src/thread.cpp create mode 100644 src-ThirdParty/llvm/libcxxabi/src/stdlib_exception.cpp create mode 100644 src/native/nativeaot/cxx-abi/stdexcept.cc create mode 100644 src/native/nativeaot/cxx-abi/system_error.cc diff --git a/src-ThirdParty/llvm/libcxx/src/condition_variable.cpp b/src-ThirdParty/llvm/libcxx/src/condition_variable.cpp new file mode 100644 index 00000000000..db60571cf5f --- /dev/null +++ b/src-ThirdParty/llvm/libcxx/src/condition_variable.cpp @@ -0,0 +1,71 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include +#include + +#if defined(__ELF__) && defined(_LIBCPP_LINK_PTHREAD_LIB) +# pragma comment(lib, "pthread") +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +// ~condition_variable is defined elsewhere. + +void condition_variable::notify_one() noexcept { __libcpp_condvar_signal(&__cv_); } + +void condition_variable::notify_all() noexcept { __libcpp_condvar_broadcast(&__cv_); } + +void condition_variable::wait(unique_lock& lk) noexcept { + if (!lk.owns_lock()) + __throw_system_error(EPERM, "condition_variable::wait: mutex not locked"); + int ec = __libcpp_condvar_wait(&__cv_, lk.mutex()->native_handle()); + if (ec) + __throw_system_error(ec, "condition_variable wait failed"); +} + +void condition_variable::__do_timed_wait(unique_lock& lk, + chrono::time_point tp) noexcept { + using namespace chrono; + if (!lk.owns_lock()) + __throw_system_error(EPERM, "condition_variable::timed wait: mutex not locked"); + nanoseconds d = tp.time_since_epoch(); + if (d > nanoseconds(0x59682F000000E941)) + d = nanoseconds(0x59682F000000E941); + __libcpp_timespec_t ts; + seconds s = duration_cast(d); + typedef decltype(ts.tv_sec) ts_sec; + constexpr ts_sec ts_sec_max = numeric_limits::max(); + if (s.count() < ts_sec_max) { + ts.tv_sec = static_cast(s.count()); + ts.tv_nsec = static_cast((d - s).count()); + } else { + ts.tv_sec = ts_sec_max; + ts.tv_nsec = giga::num - 1; + } + int ec = __libcpp_condvar_timedwait(&__cv_, lk.mutex()->native_handle(), &ts); + if (ec != 0 && ec != ETIMEDOUT) + __throw_system_error(ec, "condition_variable timed_wait failed"); +} + +void notify_all_at_thread_exit(condition_variable& cond, unique_lock lk) { + auto& tl_ptr = __thread_local_data(); + // If this thread was not created using std::thread then it will not have + // previously allocated. + if (tl_ptr.get() == nullptr) { + tl_ptr.set_pointer(new __thread_struct); + } + __thread_local_data()->notify_all_at_thread_exit(&cond, lk.release()); +} + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS diff --git a/src-ThirdParty/llvm/libcxx/src/condition_variable_destructor.cpp b/src-ThirdParty/llvm/libcxx/src/condition_variable_destructor.cpp new file mode 100644 index 00000000000..f6ffe336859 --- /dev/null +++ b/src-ThirdParty/llvm/libcxx/src/condition_variable_destructor.cpp @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Define ~condition_variable. +// +// On some platforms ~condition_variable has been made trivial and the +// definition is only provided for ABI compatibility. + +#include <__config> +#include <__thread/support.h> + +#if _LIBCPP_ABI_VERSION == 1 || !_LIBCPP_HAS_TRIVIAL_CONDVAR_DESTRUCTION +# define NEEDS_CONDVAR_DESTRUCTOR +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#ifdef NEEDS_CONDVAR_DESTRUCTOR + +class _LIBCPP_EXPORTED_FROM_ABI condition_variable { + __libcpp_condvar_t __cv_ = _LIBCPP_CONDVAR_INITIALIZER; + +public: + _LIBCPP_HIDE_FROM_ABI constexpr condition_variable() noexcept = default; + + ~condition_variable(); + + condition_variable(const condition_variable&) = delete; + condition_variable& operator=(const condition_variable&) = delete; +}; + +condition_variable::~condition_variable() { __libcpp_condvar_destroy(&__cv_); } +#endif + +_LIBCPP_END_NAMESPACE_STD diff --git a/src-ThirdParty/llvm/libcxx/src/include/atomic_support.h b/src-ThirdParty/llvm/libcxx/src/include/atomic_support.h new file mode 100644 index 00000000000..410f64b2671 --- /dev/null +++ b/src-ThirdParty/llvm/libcxx/src/include/atomic_support.h @@ -0,0 +1,132 @@ +//===----------------------------------------------------------------------===//// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===//// + +#ifndef ATOMIC_SUPPORT_H +#define ATOMIC_SUPPORT_H + +#include <__config> +#include // for __libcpp_relaxed_load + +#if defined(__clang__) && __has_builtin(__atomic_load_n) && __has_builtin(__atomic_store_n) && \ + __has_builtin(__atomic_add_fetch) && __has_builtin(__atomic_exchange_n) && \ + __has_builtin(__atomic_compare_exchange_n) && defined(__ATOMIC_RELAXED) && defined(__ATOMIC_CONSUME) && \ + defined(__ATOMIC_ACQUIRE) && defined(__ATOMIC_RELEASE) && defined(__ATOMIC_ACQ_REL) && defined(__ATOMIC_SEQ_CST) +# define _LIBCPP_HAS_ATOMIC_BUILTINS +#elif defined(_LIBCPP_COMPILER_GCC) +# define _LIBCPP_HAS_ATOMIC_BUILTINS +#endif + +#if !defined(_LIBCPP_HAS_ATOMIC_BUILTINS) && _LIBCPP_HAS_THREADS +# if defined(_LIBCPP_WARNING) +_LIBCPP_WARNING("Building libc++ without __atomic builtins is unsupported") +# else +# warning Building libc++ without __atomic builtins is unsupported +# endif +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace { + +#if defined(_LIBCPP_HAS_ATOMIC_BUILTINS) && _LIBCPP_HAS_THREADS + +enum __libcpp_atomic_order { + _AO_Relaxed = __ATOMIC_RELAXED, + _AO_Consume = __ATOMIC_CONSUME, + _AO_Acquire = __ATOMIC_ACQUIRE, + _AO_Release = __ATOMIC_RELEASE, + _AO_Acq_Rel = __ATOMIC_ACQ_REL, + _AO_Seq = __ATOMIC_SEQ_CST +}; + +template +inline _LIBCPP_HIDE_FROM_ABI void __libcpp_atomic_store(_ValueType* __dest, _FromType __val, int __order = _AO_Seq) { + __atomic_store_n(__dest, __val, __order); +} + +template +inline _LIBCPP_HIDE_FROM_ABI void __libcpp_relaxed_store(_ValueType* __dest, _FromType __val) { + __atomic_store_n(__dest, __val, _AO_Relaxed); +} + +template +inline _LIBCPP_HIDE_FROM_ABI _ValueType __libcpp_atomic_load(_ValueType const* __val, int __order = _AO_Seq) { + return __atomic_load_n(__val, __order); +} + +template +inline _LIBCPP_HIDE_FROM_ABI _ValueType __libcpp_atomic_add(_ValueType* __val, _AddType __a, int __order = _AO_Seq) { + return __atomic_add_fetch(__val, __a, __order); +} + +template +inline _LIBCPP_HIDE_FROM_ABI _ValueType +__libcpp_atomic_exchange(_ValueType* __target, _ValueType __value, int __order = _AO_Seq) { + return __atomic_exchange_n(__target, __value, __order); +} + +template +inline _LIBCPP_HIDE_FROM_ABI bool __libcpp_atomic_compare_exchange( + _ValueType* __val, + _ValueType* __expected, + _ValueType __after, + int __success_order = _AO_Seq, + int __fail_order = _AO_Seq) { + return __atomic_compare_exchange_n(__val, __expected, __after, true, __success_order, __fail_order); +} + +#else // _LIBCPP_HAS_THREADS + +enum __libcpp_atomic_order { _AO_Relaxed, _AO_Consume, _AO_Acquire, _AO_Release, _AO_Acq_Rel, _AO_Seq }; + +template +inline _LIBCPP_HIDE_FROM_ABI void __libcpp_atomic_store(_ValueType* __dest, _FromType __val, int = 0) { + *__dest = __val; +} + +template +inline _LIBCPP_HIDE_FROM_ABI void __libcpp_relaxed_store(_ValueType* __dest, _FromType __val) { + *__dest = __val; +} + +template +inline _LIBCPP_HIDE_FROM_ABI _ValueType __libcpp_atomic_load(_ValueType const* __val, int = 0) { + return *__val; +} + +template +inline _LIBCPP_HIDE_FROM_ABI _ValueType __libcpp_atomic_add(_ValueType* __val, _AddType __a, int = 0) { + return *__val += __a; +} + +template +inline _LIBCPP_HIDE_FROM_ABI _ValueType +__libcpp_atomic_exchange(_ValueType* __target, _ValueType __value, int = _AO_Seq) { + _ValueType old = *__target; + *__target = __value; + return old; +} + +template +inline _LIBCPP_HIDE_FROM_ABI bool +__libcpp_atomic_compare_exchange(_ValueType* __val, _ValueType* __expected, _ValueType __after, int = 0, int = 0) { + if (*__val == *__expected) { + *__val = __after; + return true; + } + *__expected = *__val; + return false; +} + +#endif // _LIBCPP_HAS_THREADS + +} // namespace + +_LIBCPP_END_NAMESPACE_STD + +#endif // ATOMIC_SUPPORT_H diff --git a/src-ThirdParty/llvm/libcxx/src/memory.cpp b/src-ThirdParty/llvm/libcxx/src/memory.cpp new file mode 100644 index 00000000000..16190c242c1 --- /dev/null +++ b/src-ThirdParty/llvm/libcxx/src/memory.cpp @@ -0,0 +1,148 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <__config> +#ifdef _LIBCPP_DEPRECATED_ABI_LEGACY_LIBRARY_DEFINITIONS_FOR_INLINE_FUNCTIONS +# define _LIBCPP_SHARED_PTR_DEFINE_LEGACY_INLINE_FUNCTIONS +#endif + +#include + +#if _LIBCPP_HAS_THREADS +# include +# include +# if defined(__ELF__) && defined(_LIBCPP_LINK_PTHREAD_LIB) +# pragma comment(lib, "pthread") +# endif +#endif + +#include "include/atomic_support.h" + +_LIBCPP_BEGIN_NAMESPACE_STD + +bad_weak_ptr::~bad_weak_ptr() noexcept {} + +const char* bad_weak_ptr::what() const noexcept { return "bad_weak_ptr"; } + +__shared_count::~__shared_count() {} + +__shared_weak_count::~__shared_weak_count() {} + +#if defined(_LIBCPP_SHARED_PTR_DEFINE_LEGACY_INLINE_FUNCTIONS) +void __shared_count::__add_shared() noexcept { __libcpp_atomic_refcount_increment(__shared_owners_); } + +bool __shared_count::__release_shared() noexcept { + if (__libcpp_atomic_refcount_decrement(__shared_owners_) == -1) { + __on_zero_shared(); + return true; + } + return false; +} + +void __shared_weak_count::__add_shared() noexcept { __shared_count::__add_shared(); } + +void __shared_weak_count::__add_weak() noexcept { __libcpp_atomic_refcount_increment(__shared_weak_owners_); } + +void __shared_weak_count::__release_shared() noexcept { + if (__shared_count::__release_shared()) + __release_weak(); +} +#endif // _LIBCPP_SHARED_PTR_DEFINE_LEGACY_INLINE_FUNCTIONS + +void __shared_weak_count::__release_weak() noexcept { + // NOTE: The acquire load here is an optimization of the very + // common case where a shared pointer is being destructed while + // having no other contended references. + // + // BENEFIT: We avoid expensive atomic stores like XADD and STREX + // in a common case. Those instructions are slow and do nasty + // things to caches. + // + // IS THIS SAFE? Yes. During weak destruction, if we see that we + // are the last reference, we know that no-one else is accessing + // us. If someone were accessing us, then they would be doing so + // while the last shared / weak_ptr was being destructed, and + // that's undefined anyway. + // + // If we see anything other than a 0, then we have possible + // contention, and need to use an atomicrmw primitive. + // The same arguments don't apply for increment, where it is legal + // (though inadvisable) to share shared_ptr references between + // threads, and have them all get copied at once. The argument + // also doesn't apply for __release_shared, because an outstanding + // weak_ptr::lock() could read / modify the shared count. + if (__libcpp_atomic_load(&__shared_weak_owners_, _AO_Acquire) == 0) { + // no need to do this store, because we are about + // to destroy everything. + //__libcpp_atomic_store(&__shared_weak_owners_, -1, _AO_Release); + __on_zero_shared_weak(); + } else if (__libcpp_atomic_refcount_decrement(__shared_weak_owners_) == -1) + __on_zero_shared_weak(); +} + +__shared_weak_count* __shared_weak_count::lock() noexcept { + long object_owners = __libcpp_atomic_load(&__shared_owners_); + while (object_owners != -1) { + if (__libcpp_atomic_compare_exchange(&__shared_owners_, &object_owners, object_owners + 1)) + return this; + } + return nullptr; +} + +const void* __shared_weak_count::__get_deleter(const type_info&) const noexcept { return nullptr; } + +#if _LIBCPP_HAS_THREADS + +static constexpr std::size_t __sp_mut_count = 32; +static constinit __libcpp_mutex_t mut_back[__sp_mut_count] = { + _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, + _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, + _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, + _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, + _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, + _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, + _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, + _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER, _LIBCPP_MUTEX_INITIALIZER}; + +constexpr __sp_mut::__sp_mut(void* p) noexcept : __lx_(p) {} + +void __sp_mut::lock() noexcept { + auto m = static_cast<__libcpp_mutex_t*>(__lx_); + __libcpp_mutex_lock(m); +} + +void __sp_mut::unlock() noexcept { __libcpp_mutex_unlock(static_cast<__libcpp_mutex_t*>(__lx_)); } + +__sp_mut& __get_sp_mut(const void* p) { + static constinit __sp_mut muts[__sp_mut_count] = { + &mut_back[0], &mut_back[1], &mut_back[2], &mut_back[3], &mut_back[4], &mut_back[5], &mut_back[6], + &mut_back[7], &mut_back[8], &mut_back[9], &mut_back[10], &mut_back[11], &mut_back[12], &mut_back[13], + &mut_back[14], &mut_back[15], &mut_back[16], &mut_back[17], &mut_back[18], &mut_back[19], &mut_back[20], + &mut_back[21], &mut_back[22], &mut_back[23], &mut_back[24], &mut_back[25], &mut_back[26], &mut_back[27], + &mut_back[28], &mut_back[29], &mut_back[30], &mut_back[31]}; + return muts[hash()(p) & (__sp_mut_count - 1)]; +} + +#endif // _LIBCPP_HAS_THREADS + +void* align(size_t alignment, size_t size, void*& ptr, size_t& space) { + void* r = nullptr; + if (size <= space) { + char* p1 = static_cast(ptr); + char* p2 = reinterpret_cast(reinterpret_cast(p1 + (alignment - 1)) & -alignment); + size_t d = static_cast(p2 - p1); + if (d <= space - size) { + r = p2; + ptr = r; + space -= d; + } + } + return r; +} + +_LIBCPP_END_NAMESPACE_STD diff --git a/src-ThirdParty/llvm/libcxx/src/mutex.cpp b/src-ThirdParty/llvm/libcxx/src/mutex.cpp new file mode 100644 index 00000000000..2f8504d602d --- /dev/null +++ b/src-ThirdParty/llvm/libcxx/src/mutex.cpp @@ -0,0 +1,145 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <__assert> +#include <__thread/id.h> +#include <__utility/exception_guard.h> +#include +#include + +#include "include/atomic_support.h" + +#if defined(__ELF__) && defined(_LIBCPP_LINK_PTHREAD_LIB) +# pragma comment(lib, "pthread") +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +// ~mutex is defined elsewhere + +void mutex::lock() { + int ec = __libcpp_mutex_lock(&__m_); + if (ec) + __throw_system_error(ec, "mutex lock failed"); +} + +bool mutex::try_lock() noexcept { return __libcpp_mutex_trylock(&__m_); } + +void mutex::unlock() noexcept { + int ec = __libcpp_mutex_unlock(&__m_); + (void)ec; + _LIBCPP_ASSERT_VALID_EXTERNAL_API_CALL( + ec == 0, "call to mutex::unlock failed. A possible reason is that the mutex wasn't locked"); +} + +// recursive_mutex + +recursive_mutex::recursive_mutex() { + int ec = __libcpp_recursive_mutex_init(&__m_); + if (ec) + __throw_system_error(ec, "recursive_mutex constructor failed"); +} + +recursive_mutex::~recursive_mutex() { + int e = __libcpp_recursive_mutex_destroy(&__m_); + (void)e; + _LIBCPP_ASSERT_VALID_EXTERNAL_API_CALL(e == 0, "call to ~recursive_mutex() failed"); +} + +void recursive_mutex::lock() { + int ec = __libcpp_recursive_mutex_lock(&__m_); + if (ec) + __throw_system_error(ec, "recursive_mutex lock failed"); +} + +void recursive_mutex::unlock() noexcept { + int e = __libcpp_recursive_mutex_unlock(&__m_); + (void)e; + _LIBCPP_ASSERT_VALID_EXTERNAL_API_CALL( + e == 0, "call to recursive_mutex::unlock() failed. A possible reason is that the mutex wasn't locked"); +} + +bool recursive_mutex::try_lock() noexcept { return __libcpp_recursive_mutex_trylock(&__m_); } + +// timed_mutex + +timed_mutex::timed_mutex() : __locked_(false) {} + +timed_mutex::~timed_mutex() { lock_guard _(__m_); } + +void timed_mutex::lock() { + unique_lock lk(__m_); + while (__locked_) + __cv_.wait(lk); + __locked_ = true; +} + +bool timed_mutex::try_lock() noexcept { + unique_lock lk(__m_, try_to_lock); + if (lk.owns_lock() && !__locked_) { + __locked_ = true; + return true; + } + return false; +} + +void timed_mutex::unlock() noexcept { + lock_guard _(__m_); + __locked_ = false; + __cv_.notify_one(); +} + +// recursive_timed_mutex + +recursive_timed_mutex::recursive_timed_mutex() : __count_(0), __id_{} {} + +recursive_timed_mutex::~recursive_timed_mutex() { lock_guard _(__m_); } + +void recursive_timed_mutex::lock() { + __thread_id id = this_thread::get_id(); + unique_lock lk(__m_); + if (id == __id_) { + if (__count_ == numeric_limits::max()) + __throw_system_error(EAGAIN, "recursive_timed_mutex lock limit reached"); + ++__count_; + return; + } + while (__count_ != 0) + __cv_.wait(lk); + __count_ = 1; + __id_ = id; +} + +bool recursive_timed_mutex::try_lock() noexcept { + __thread_id id = this_thread::get_id(); + unique_lock lk(__m_, try_to_lock); + if (lk.owns_lock() && (__count_ == 0 || id == __id_)) { + if (__count_ == numeric_limits::max()) + return false; + ++__count_; + __id_ = id; + return true; + } + return false; +} + +void recursive_timed_mutex::unlock() noexcept { + unique_lock lk(__m_); + if (--__count_ == 0) { + __id_.__reset(); + lk.unlock(); + __cv_.notify_one(); + } +} + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS diff --git a/src-ThirdParty/llvm/libcxx/src/mutex_destructor.cpp b/src-ThirdParty/llvm/libcxx/src/mutex_destructor.cpp new file mode 100644 index 00000000000..9f991721f08 --- /dev/null +++ b/src-ThirdParty/llvm/libcxx/src/mutex_destructor.cpp @@ -0,0 +1,42 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Define ~mutex. +// +// On some platforms ~mutex has been made trivial and the definition is only +// provided for ABI compatibility. +// +// In order to avoid ODR violations within libc++ itself, we need to ensure +// that *nothing* sees the non-trivial mutex declaration. For this reason +// we re-declare the entire class in this file instead of using +// _LIBCPP_BUILDING_LIBRARY to change the definition in the headers. + +#include <__config> +#include <__thread/support.h> + +#if _LIBCPP_ABI_VERSION == 1 || !_LIBCPP_HAS_TRIVIAL_MUTEX_DESTRUCTION +# define NEEDS_MUTEX_DESTRUCTOR +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#ifdef NEEDS_MUTEX_DESTRUCTOR +class _LIBCPP_EXPORTED_FROM_ABI mutex { + __libcpp_mutex_t __m_ = _LIBCPP_MUTEX_INITIALIZER; + +public: + _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI constexpr mutex() = default; + mutex(const mutex&) = delete; + mutex& operator=(const mutex&) = delete; + ~mutex() noexcept; +}; + +mutex::~mutex() noexcept { __libcpp_mutex_destroy(&__m_); } +#endif // !NEEDS_MUTEX_DESTRUCTOR + +_LIBCPP_END_NAMESPACE_STD diff --git a/src-ThirdParty/llvm/libcxx/src/thread.cpp b/src-ThirdParty/llvm/libcxx/src/thread.cpp new file mode 100644 index 00000000000..73f22f12d8c --- /dev/null +++ b/src-ThirdParty/llvm/libcxx/src/thread.cpp @@ -0,0 +1,173 @@ +//===------------------------- thread.cpp----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <__thread/poll_with_backoff.h> +#include <__thread/timed_backoff_policy.h> +#include +#include +#include +#include +#include + +#if __has_include() +# include // for sysconf +#endif + +#if defined(__NetBSD__) +# pragma weak pthread_create // Do not create libpthread dependency +#endif + +#if defined(_LIBCPP_WIN32API) +# include +#endif + +#if defined(__ELF__) && defined(_LIBCPP_LINK_PTHREAD_LIB) +# pragma comment(lib, "pthread") +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +thread::~thread() { + if (!__libcpp_thread_isnull(&__t_)) + terminate(); +} + +void thread::join() { + int ec = EINVAL; + if (!__libcpp_thread_isnull(&__t_)) { + ec = __libcpp_thread_join(&__t_); + if (ec == 0) + __t_ = _LIBCPP_NULL_THREAD; + } + + if (ec) + __throw_system_error(ec, "thread::join failed"); +} + +void thread::detach() { + int ec = EINVAL; + if (!__libcpp_thread_isnull(&__t_)) { + ec = __libcpp_thread_detach(&__t_); + if (ec == 0) + __t_ = _LIBCPP_NULL_THREAD; + } + + if (ec) + __throw_system_error(ec, "thread::detach failed"); +} + +unsigned thread::hardware_concurrency() noexcept { +#if defined(_SC_NPROCESSORS_ONLN) + long result = sysconf(_SC_NPROCESSORS_ONLN); + // sysconf returns -1 if the name is invalid, the option does not exist or + // does not have a definite limit. + // if sysconf returns some other negative number, we have no idea + // what is going on. Default to something safe. + if (result < 0) + return 0; + return static_cast(result); +#elif defined(_LIBCPP_WIN32API) + SYSTEM_INFO info; + GetSystemInfo(&info); + return info.dwNumberOfProcessors; +#else // defined(CTL_HW) && defined(HW_NCPU) + // TODO: grovel through /proc or check cpuid on x86 and similar + // instructions on other architectures. +# if defined(_LIBCPP_WARNING) + _LIBCPP_WARNING("hardware_concurrency not yet implemented") +# else +# warning hardware_concurrency not yet implemented +# endif + return 0; // Means not computable [thread.thread.static] +#endif // defined(CTL_HW) && defined(HW_NCPU) +} + +namespace this_thread { + +void sleep_for(const chrono::nanoseconds& ns) { + if (ns > chrono::nanoseconds::zero()) { + __libcpp_thread_sleep_for(ns); + } +} + +} // namespace this_thread + +__thread_specific_ptr<__thread_struct>& __thread_local_data() { + // Even though __thread_specific_ptr's destructor doesn't actually destroy + // anything (see comments there), we can't call it at all because threads may + // outlive the static variable and calling its destructor means accessing an + // object outside of its lifetime, which is UB. + alignas(__thread_specific_ptr<__thread_struct>) static char __b[sizeof(__thread_specific_ptr<__thread_struct>)]; + static __thread_specific_ptr<__thread_struct>* __p = new (__b) __thread_specific_ptr<__thread_struct>(); + return *__p; +} + +// __thread_struct_imp + +template +class _LIBCPP_HIDDEN __hidden_allocator { +public: + typedef T value_type; + + T* allocate(size_t __n) { return static_cast(::operator new(__n * sizeof(T))); } + void deallocate(T* __p, size_t) { ::operator delete(static_cast(__p)); } + + size_t max_size() const { return size_t(~0) / sizeof(T); } +}; + +class _LIBCPP_HIDDEN __thread_struct_imp { + typedef vector<__assoc_sub_state*, __hidden_allocator<__assoc_sub_state*> > _AsyncStates; + typedef vector, __hidden_allocator > > _Notify; + + _AsyncStates async_states_; + _Notify notify_; + + __thread_struct_imp(const __thread_struct_imp&); + __thread_struct_imp& operator=(const __thread_struct_imp&); + +public: + __thread_struct_imp() {} + ~__thread_struct_imp(); + + void notify_all_at_thread_exit(condition_variable* cv, mutex* m); + void __make_ready_at_thread_exit(__assoc_sub_state* __s); +}; + +__thread_struct_imp::~__thread_struct_imp() { + for (_Notify::iterator i = notify_.begin(), e = notify_.end(); i != e; ++i) { + i->first->notify_all(); + i->second->unlock(); + } + for (_AsyncStates::iterator i = async_states_.begin(), e = async_states_.end(); i != e; ++i) { + (*i)->__make_ready(); + (*i)->__release_shared(); + } +} + +void __thread_struct_imp::notify_all_at_thread_exit(condition_variable* cv, mutex* m) { + notify_.push_back(pair(cv, m)); +} + +void __thread_struct_imp::__make_ready_at_thread_exit(__assoc_sub_state* __s) { + async_states_.push_back(__s); + __s->__add_shared(); +} + +// __thread_struct + +__thread_struct::__thread_struct() : __p_(new __thread_struct_imp) {} + +__thread_struct::~__thread_struct() { delete __p_; } + +void __thread_struct::notify_all_at_thread_exit(condition_variable* cv, mutex* m) { + __p_->notify_all_at_thread_exit(cv, m); +} + +void __thread_struct::__make_ready_at_thread_exit(__assoc_sub_state* __s) { __p_->__make_ready_at_thread_exit(__s); } + +_LIBCPP_END_NAMESPACE_STD diff --git a/src-ThirdParty/llvm/libcxxabi/src/stdlib_exception.cpp b/src-ThirdParty/llvm/libcxxabi/src/stdlib_exception.cpp new file mode 100644 index 00000000000..b1fc21f412a --- /dev/null +++ b/src-ThirdParty/llvm/libcxxabi/src/stdlib_exception.cpp @@ -0,0 +1,70 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include +#include + +namespace std +{ + +// exception + +exception::~exception() noexcept +{ +} + +const char* exception::what() const noexcept +{ + return "std::exception"; +} + +// bad_exception + +bad_exception::~bad_exception() noexcept +{ +} + +const char* bad_exception::what() const noexcept +{ + return "std::bad_exception"; +} + + +// bad_alloc + +bad_alloc::bad_alloc() noexcept +{ +} + +bad_alloc::~bad_alloc() noexcept +{ +} + +const char* +bad_alloc::what() const noexcept +{ + return "std::bad_alloc"; +} + +// bad_array_new_length + +bad_array_new_length::bad_array_new_length() noexcept +{ +} + +bad_array_new_length::~bad_array_new_length() noexcept +{ +} + +const char* +bad_array_new_length::what() const noexcept +{ + return "bad_array_new_length"; +} + +} // std diff --git a/src/native/native.targets b/src/native/native.targets index 396e1d44b1f..8ef84e50325 100644 --- a/src/native/native.targets +++ b/src/native/native.targets @@ -40,10 +40,10 @@ <_ConfigureRuntimesInputs Include="common\lz4\CMakeLists.txt" /> <_ConfigureRuntimesInputs Include="common\runtime-base\CMakeLists.txt" /> - <_ConfigureRuntimesOutputs Include="@(AndroidSupportedTargetJitAbi->'$(FlavorIntermediateOutputPath)\%(AndroidRID)-Debug\CMakeCache.txt')" /> + <_ConfigureRuntimesOutputs Include="@(AndroidSupportedTargetJitAbi->'$(FlavorIntermediateOutputPath)\%(AndroidRID)-Debug\CMakeCache.txt')" Condition=" '$(CMakeRuntimeFlavor)' != 'NativeAOT' " /> <_ConfigureRuntimesOutputs Include="@(AndroidSupportedTargetJitAbi->'$(FlavorIntermediateOutputPath)\%(AndroidRID)-Release\CMakeCache.txt')" /> - <_OutputDirsToCreate Include="$(FlavorIntermediateOutputPath)\%(AndroidSupportedTargetJitAbi.AndroidRID)-Debug" /> + <_OutputDirsToCreate Include="$(FlavorIntermediateOutputPath)\%(AndroidSupportedTargetJitAbi.AndroidRID)-Debug" Condition=" '$(CMakeRuntimeFlavor)' != 'NativeAOT' " /> <_OutputDirsToCreate Include="$(FlavorIntermediateOutputPath)\%(AndroidSupportedTargetJitAbi.AndroidRID)-Release" /> @@ -166,7 +166,7 @@ - <_ConfigureRuntimeCommands Include="@(AndroidSupportedTargetJitAbi)"> + <_ConfigureRuntimeCommands Include="@(AndroidSupportedTargetJitAbi)" Condition=" '$(CMakeRuntimeFlavor)' != 'NativeAOT' "> $(CmakePath) --preset $(_PresetPrefix)default-debug-%(AndroidSupportedTargetJitAbi.Identity) $(_CmakeAndroidFlags) $(FlavorIntermediateOutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)-Debug @@ -286,6 +286,7 @@ Inputs="@(_BuildAndroidRuntimesInputs)" Outputs="@(_BuildAndroidRuntimesOutputs)"> diff --git a/src/native/nativeaot/cxx-abi/stdexcept.cc b/src/native/nativeaot/cxx-abi/stdexcept.cc new file mode 100644 index 00000000000..96183d92237 --- /dev/null +++ b/src/native/nativeaot/cxx-abi/stdexcept.cc @@ -0,0 +1,17 @@ +#include +#include + +#include + +_LIBCPP_BEGIN_NAMESPACE_STD + +void __throw_runtime_error (const char* msg) +{ + char *message = nullptr; + int n = asprintf (&message, "runtime_error was thrown in -fno-exceptions mode with message \"%s\"", msg); + xamarin::android::Helpers::abort_application ( + n == -1 ? "runtime_error was thrown in -fno-exceptions mode" : message + ); +} + +_LIBCPP_END_NAMESPACE_STD diff --git a/src/native/nativeaot/cxx-abi/system_error.cc b/src/native/nativeaot/cxx-abi/system_error.cc new file mode 100644 index 00000000000..e03c7d4aff2 --- /dev/null +++ b/src/native/nativeaot/cxx-abi/system_error.cc @@ -0,0 +1,19 @@ +#include +#include + +#include <__system_error/throw_system_error.h> + +#include + +_LIBCPP_BEGIN_NAMESPACE_STD + +void __throw_system_error (int ev, const char* what_arg) +{ + char *message = nullptr; + int n = asprintf (&message, "system_error was thrown in -fno-exceptions mode with error %i and message \"%s\"", ev, what_arg); + xamarin::android::Helpers::abort_application ( + n == -1 ? "system_error was thrown in -fno-exceptions mode" : message + ); +} + +_LIBCPP_END_NAMESPACE_STD diff --git a/src/native/nativeaot/host/CMakeLists.txt b/src/native/nativeaot/host/CMakeLists.txt index 5a542111ace..1c3aa66aed5 100644 --- a/src/native/nativeaot/host/CMakeLists.txt +++ b/src/native/nativeaot/host/CMakeLists.txt @@ -16,8 +16,9 @@ endif() # Extra source directories set(LIBCXX_SOURCE_DIR "${THIRD_PARTY_SOURCE_DIR}/llvm/libcxx/src") +set(LIBCXXABI_SOURCE_DIR "${THIRD_PARTY_SOURCE_DIR}/llvm/libcxxabi/src") set(BIONIC_SOURCE_DIR "${THIRD_PARTY_SOURCE_DIR}/bionic") -set(CXXABI_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../cxx-abi) +set(LOCAL_CXXABI_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../cxx-abi") # Library directories set(XA_LIBRARY_OUTPUT_DIRECTORY "${XA_LIB_TOP_DIR}/lib/${ANDROID_RID}") @@ -44,12 +45,23 @@ set(XAMARIN_MONODROID_SOURCES ${BIONIC_SOURCE_DIR}//cxa_guard.cc # libc++ sources + ${LIBCXX_SOURCE_DIR}/condition_variable.cpp + ${LIBCXX_SOURCE_DIR}/condition_variable_destructor.cpp + ${LIBCXX_SOURCE_DIR}/memory.cpp + ${LIBCXX_SOURCE_DIR}/mutex.cpp + ${LIBCXX_SOURCE_DIR}/mutex_destructor.cpp + ${LIBCXX_SOURCE_DIR}/thread.cpp ${LIBCXX_SOURCE_DIR}/verbose_abort.cpp - # C++ ABI sources - ${CXXABI_SOURCE_DIR}/new.cc - ${CXXABI_SOURCE_DIR}/string.cc - ${CXXABI_SOURCE_DIR}/terminate.cc + # libc++abi sources + ${LIBCXXABI_SOURCE_DIR}/stdlib_exception.cpp + + # Local versions of C++ ABI sources + ${LOCAL_CXXABI_SOURCE_DIR}/new.cc + ${LOCAL_CXXABI_SOURCE_DIR}/stdexcept.cc + ${LOCAL_CXXABI_SOURCE_DIR}/string.cc + ${LOCAL_CXXABI_SOURCE_DIR}/system_error.cc + ${LOCAL_CXXABI_SOURCE_DIR}/terminate.cc # Sources from CoreCLR host ${CLR_SOURCES_PATH}/host/bridge-processing.cc From f071fc0a53ada344df001770f4d56e9735341c6b Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Tue, 13 Jan 2026 18:54:37 +0100 Subject: [PATCH 12/39] Compiles and links without errors. Not tested at runtime yet. --- .../llvm/libcxx/src/error_category.cpp | 37 ++ src-ThirdParty/llvm/libcxx/src/future.cpp | 197 ++++++++++ .../llvm/libcxx/src/include/config_elast.h | 51 +++ .../llvm/libcxx/src/include/refstring.h | 127 ++++++ .../src/support/runtime/stdexcept_default.ipp | 62 +++ .../llvm/libcxx/src/system_error.cpp | 369 ++++++++++++++++++ src/native/clr/host/host-shared.cc | 2 +- src/native/clr/host/os-bridge.cc | 21 +- .../clr/include/host/host-environment.hh | 21 +- .../include/runtime-base/android-system.hh | 11 +- src/native/clr/include/shared/log_types.hh | 46 +++ src/native/clr/runtime-base/util.cc | 44 ++- src/native/clr/shared/helpers.cc | 6 +- src/native/clr/shared/log_functions.cc | 21 + .../common/include/runtime-base/strings.hh | 43 +- .../include/runtime-base/timing-internal.hh | 35 +- src/native/nativeaot/cxx-abi/cxa_virtual.cc | 10 + src/native/nativeaot/cxx-abi/no_exceptions.cc | 26 ++ src/native/nativeaot/cxx-abi/stdexcept.cc | 3 + src/native/nativeaot/cxx-abi/system_error.cc | 16 +- src/native/nativeaot/host/CMakeLists.txt | 17 + .../nativeaot/host/bridge-processing.cc | 18 +- src/native/nativeaot/host/host.cc | 2 +- 23 files changed, 1149 insertions(+), 36 deletions(-) create mode 100644 src-ThirdParty/llvm/libcxx/src/error_category.cpp create mode 100644 src-ThirdParty/llvm/libcxx/src/future.cpp create mode 100644 src-ThirdParty/llvm/libcxx/src/include/config_elast.h create mode 100644 src-ThirdParty/llvm/libcxx/src/include/refstring.h create mode 100644 src-ThirdParty/llvm/libcxx/src/support/runtime/stdexcept_default.ipp create mode 100644 src-ThirdParty/llvm/libcxx/src/system_error.cpp create mode 100644 src/native/nativeaot/cxx-abi/cxa_virtual.cc create mode 100644 src/native/nativeaot/cxx-abi/no_exceptions.cc diff --git a/src-ThirdParty/llvm/libcxx/src/error_category.cpp b/src-ThirdParty/llvm/libcxx/src/error_category.cpp new file mode 100644 index 00000000000..8ae460fb5f1 --- /dev/null +++ b/src-ThirdParty/llvm/libcxx/src/error_category.cpp @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <__config> + +#ifdef _LIBCPP_DEPRECATED_ABI_LEGACY_LIBRARY_DEFINITIONS_FOR_INLINE_FUNCTIONS +# define _LIBCPP_ERROR_CATEGORY_DEFINE_LEGACY_INLINE_FUNCTIONS +#endif + +#include + +_LIBCPP_BEGIN_NAMESPACE_STD + +// class error_category + +#if defined(_LIBCPP_ERROR_CATEGORY_DEFINE_LEGACY_INLINE_FUNCTIONS) +error_category::error_category() noexcept {} +#endif + +error_category::~error_category() noexcept {} + +error_condition error_category::default_error_condition(int ev) const noexcept { return error_condition(ev, *this); } + +bool error_category::equivalent(int code, const error_condition& condition) const noexcept { + return default_error_condition(code) == condition; +} + +bool error_category::equivalent(const error_code& code, int condition) const noexcept { + return *this == code.category() && code.value() == condition; +} + +_LIBCPP_END_NAMESPACE_STD diff --git a/src-ThirdParty/llvm/libcxx/src/future.cpp b/src-ThirdParty/llvm/libcxx/src/future.cpp new file mode 100644 index 00000000000..04e6fb8db64 --- /dev/null +++ b/src-ThirdParty/llvm/libcxx/src/future.cpp @@ -0,0 +1,197 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include +#include + +_LIBCPP_BEGIN_NAMESPACE_STD + +class _LIBCPP_HIDDEN __future_error_category : public __do_message { +public: + virtual const char* name() const noexcept; + virtual string message(int ev) const; +}; + +const char* __future_error_category::name() const noexcept { return "future"; } + +_LIBCPP_DIAGNOSTIC_PUSH +_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wswitch") +_LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wswitch") + +string __future_error_category::message(int ev) const { + switch (static_cast(ev)) { + case future_errc(0): // For backwards compatibility with C++11 (LWG 2056) + case future_errc::broken_promise: + return string("The associated promise has been destructed prior " + "to the associated state becoming ready."); + case future_errc::future_already_retrieved: + return string("The future has already been retrieved from " + "the promise or packaged_task."); + case future_errc::promise_already_satisfied: + return string("The state of the promise has already been set."); + case future_errc::no_state: + return string("Operation not permitted on an object without " + "an associated state."); + } + return string("unspecified future_errc value\n"); +} + +_LIBCPP_DIAGNOSTIC_POP + +const error_category& future_category() noexcept { + union AvoidDestroyingFutureCategory { + __future_error_category future_error_category; + constexpr explicit AvoidDestroyingFutureCategory() : future_error_category() {} + ~AvoidDestroyingFutureCategory() {} + }; + constinit static AvoidDestroyingFutureCategory helper; + return helper.future_error_category; +} + +future_error::future_error(error_code __ec) : logic_error(__ec.message()), __ec_(__ec) {} + +future_error::~future_error() noexcept {} + +void __assoc_sub_state::__on_zero_shared() noexcept { delete this; } + +void __assoc_sub_state::set_value() { + unique_lock __lk(__mut_); + if (__has_value()) + __throw_future_error(future_errc::promise_already_satisfied); + __state_ |= __constructed | ready; + __cv_.notify_all(); +} + +void __assoc_sub_state::set_value_at_thread_exit() { + unique_lock __lk(__mut_); + if (__has_value()) + __throw_future_error(future_errc::promise_already_satisfied); + __state_ |= __constructed; + __thread_local_data()->__make_ready_at_thread_exit(this); +} + +void __assoc_sub_state::set_exception(exception_ptr __p) { + unique_lock __lk(__mut_); + if (__has_value()) + __throw_future_error(future_errc::promise_already_satisfied); + __exception_ = __p; + __state_ |= ready; + __cv_.notify_all(); +} + +void __assoc_sub_state::set_exception_at_thread_exit(exception_ptr __p) { + unique_lock __lk(__mut_); + if (__has_value()) + __throw_future_error(future_errc::promise_already_satisfied); + __exception_ = __p; + __thread_local_data()->__make_ready_at_thread_exit(this); +} + +void __assoc_sub_state::__make_ready() { + unique_lock __lk(__mut_); + __state_ |= ready; + __cv_.notify_all(); +} + +void __assoc_sub_state::copy() { + unique_lock __lk(__mut_); + __sub_wait(__lk); + if (__exception_ != nullptr) + rethrow_exception(__exception_); +} + +void __assoc_sub_state::wait() { + unique_lock __lk(__mut_); + __sub_wait(__lk); +} + +void __assoc_sub_state::__sub_wait(unique_lock& __lk) { + if (!__is_ready()) { + if (__state_ & static_cast(deferred)) { + __state_ &= ~static_cast(deferred); + __lk.unlock(); + __execute(); + } else + while (!__is_ready()) + __cv_.wait(__lk); + } +} + +void __assoc_sub_state::__execute() { __throw_future_error(future_errc::no_state); } + +future::future(__assoc_sub_state* __state) : __state_(__state) { __state_->__attach_future(); } + +future::~future() { + if (__state_) + __state_->__release_shared(); +} + +void future::get() { + unique_ptr<__shared_count, __release_shared_count> __(__state_); + __assoc_sub_state* __s = __state_; + __state_ = nullptr; + __s->copy(); +} + +promise::promise() : __state_(new __assoc_sub_state) {} + +promise::~promise() { + if (__state_) { +#if _LIBCPP_HAS_EXCEPTIONS + if (!__state_->__has_value() && __state_->use_count() > 1) + __state_->set_exception(make_exception_ptr(future_error(future_errc::broken_promise))); +#endif // _LIBCPP_HAS_EXCEPTIONS + __state_->__release_shared(); + } +} + +future promise::get_future() { + if (__state_ == nullptr) + __throw_future_error(future_errc::no_state); + return future(__state_); +} + +void promise::set_value() { + if (__state_ == nullptr) + __throw_future_error(future_errc::no_state); + __state_->set_value(); +} + +void promise::set_exception(exception_ptr __p) { + if (__state_ == nullptr) + __throw_future_error(future_errc::no_state); + __state_->set_exception(__p); +} + +void promise::set_value_at_thread_exit() { + if (__state_ == nullptr) + __throw_future_error(future_errc::no_state); + __state_->set_value_at_thread_exit(); +} + +void promise::set_exception_at_thread_exit(exception_ptr __p) { + if (__state_ == nullptr) + __throw_future_error(future_errc::no_state); + __state_->set_exception_at_thread_exit(__p); +} + +shared_future::~shared_future() { + if (__state_) + __state_->__release_shared(); +} + +shared_future& shared_future::operator=(const shared_future& __rhs) { + if (__rhs.__state_) + __rhs.__state_->__add_shared(); + if (__state_) + __state_->__release_shared(); + __state_ = __rhs.__state_; + return *this; +} + +_LIBCPP_END_NAMESPACE_STD diff --git a/src-ThirdParty/llvm/libcxx/src/include/config_elast.h b/src-ThirdParty/llvm/libcxx/src/include/config_elast.h new file mode 100644 index 00000000000..7edff2d9375 --- /dev/null +++ b/src-ThirdParty/llvm/libcxx/src/include/config_elast.h @@ -0,0 +1,51 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP_CONFIG_ELAST +#define _LIBCPP_CONFIG_ELAST + +#include <__config> + +#if defined(_LIBCPP_MSVCRT_LIKE) +# include +#else +# include +#endif + +// Note: _LIBCPP_ELAST needs to be defined only on platforms +// where strerror/strerror_r can't handle out-of-range errno values. +#if defined(ELAST) +# define _LIBCPP_ELAST ELAST +#elif defined(__LLVM_LIBC__) +// No _LIBCPP_ELAST needed for LLVM libc +#elif defined(_NEWLIB_VERSION) +# define _LIBCPP_ELAST __ELASTERROR +#elif defined(__NuttX__) +// No _LIBCPP_ELAST needed on NuttX +#elif defined(__Fuchsia__) +// No _LIBCPP_ELAST needed on Fuchsia +#elif defined(__wasi__) +// No _LIBCPP_ELAST needed on WASI +#elif defined(__EMSCRIPTEN__) +// No _LIBCPP_ELAST needed on Emscripten +#elif defined(__linux__) || _LIBCPP_HAS_MUSL_LIBC +# define _LIBCPP_ELAST 4095 +#elif defined(__APPLE__) +// No _LIBCPP_ELAST needed on Apple +#elif defined(__MVS__) +# define _LIBCPP_ELAST 1160 +#elif defined(_LIBCPP_MSVCRT_LIKE) +# define _LIBCPP_ELAST (_sys_nerr - 1) +#elif defined(_AIX) +# define _LIBCPP_ELAST 127 +#else +// Warn here so that the person doing the libcxx port has an easier time: +# warning ELAST for this platform not yet implemented +#endif + +#endif // _LIBCPP_CONFIG_ELAST diff --git a/src-ThirdParty/llvm/libcxx/src/include/refstring.h b/src-ThirdParty/llvm/libcxx/src/include/refstring.h new file mode 100644 index 00000000000..3e0ec7a97c7 --- /dev/null +++ b/src-ThirdParty/llvm/libcxx/src/include/refstring.h @@ -0,0 +1,127 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP_REFSTRING_H +#define _LIBCPP_REFSTRING_H + +#include "atomic_support.h" +#include <__config> +#include +#include +#include + +// MacOS and iOS used to ship with libstdc++, and still support old applications +// linking against libstdc++. The libc++ and libstdc++ exceptions are supposed +// to be ABI compatible, such that they can be thrown from one library and caught +// in the other. +// +// For that reason, we must look for libstdc++ in the same process and if found, +// check the string stored in the exception object to see if it is the GCC empty +// string singleton before manipulating the reference count. This is done so that +// if an exception is created with a zero-length string in libstdc++, libc++abi +// won't try to delete the memory. +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) || defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) +# define _LIBCPP_CHECK_FOR_GCC_EMPTY_STRING_STORAGE +# include +# include +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace __refstring_imp { +namespace { +typedef int count_t; + +struct _Rep_base { + std::size_t len; + std::size_t cap; + count_t count; +}; + +inline _Rep_base* rep_from_data(const char* data_) noexcept { + char* data = const_cast(data_); + return reinterpret_cast<_Rep_base*>(data - sizeof(_Rep_base)); +} + +inline char* data_from_rep(_Rep_base* rep) noexcept { + char* data = reinterpret_cast(rep); + return data + sizeof(*rep); +} + +#if defined(_LIBCPP_CHECK_FOR_GCC_EMPTY_STRING_STORAGE) +inline const char* compute_gcc_empty_string_storage() noexcept { + void* handle = dlopen("/usr/lib/libstdc++.6.dylib", RTLD_NOLOAD); + if (handle == nullptr) + return nullptr; + void* sym = dlsym(handle, "_ZNSs4_Rep20_S_empty_rep_storageE"); + if (sym == nullptr) + return nullptr; + return data_from_rep(reinterpret_cast<_Rep_base*>(sym)); +} + +inline const char* get_gcc_empty_string_storage() noexcept { + static const char* p = compute_gcc_empty_string_storage(); + return p; +} +#endif + +} // namespace +} // namespace __refstring_imp + +using namespace __refstring_imp; + +inline __libcpp_refstring::__libcpp_refstring(const char* msg) { + std::size_t len = strlen(msg); + _Rep_base* rep = static_cast<_Rep_base*>(::operator new(sizeof(*rep) + len + 1)); + rep->len = len; + rep->cap = len; + rep->count = 0; + char* data = data_from_rep(rep); + std::memcpy(data, msg, len + 1); + __imp_ = data; +} + +inline __libcpp_refstring::__libcpp_refstring(const __libcpp_refstring& s) noexcept : __imp_(s.__imp_) { + if (__uses_refcount()) + __libcpp_atomic_add(&rep_from_data(__imp_)->count, 1); +} + +inline __libcpp_refstring& __libcpp_refstring::operator=(__libcpp_refstring const& s) noexcept { + bool adjust_old_count = __uses_refcount(); + struct _Rep_base* old_rep = rep_from_data(__imp_); + __imp_ = s.__imp_; + if (__uses_refcount()) + __libcpp_atomic_add(&rep_from_data(__imp_)->count, 1); + if (adjust_old_count) { + if (__libcpp_atomic_add(&old_rep->count, count_t(-1)) < 0) { + ::operator delete(old_rep); + } + } + return *this; +} + +inline __libcpp_refstring::~__libcpp_refstring() { + if (__uses_refcount()) { + _Rep_base* rep = rep_from_data(__imp_); + if (__libcpp_atomic_add(&rep->count, count_t(-1)) < 0) { + ::operator delete(rep); + } + } +} + +inline bool __libcpp_refstring::__uses_refcount() const { +#if defined(_LIBCPP_CHECK_FOR_GCC_EMPTY_STRING_STORAGE) + return __imp_ != get_gcc_empty_string_storage(); +#else + return true; +#endif +} + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_REFSTRING_H diff --git a/src-ThirdParty/llvm/libcxx/src/support/runtime/stdexcept_default.ipp b/src-ThirdParty/llvm/libcxx/src/support/runtime/stdexcept_default.ipp new file mode 100644 index 00000000000..1f47a0325d7 --- /dev/null +++ b/src-ThirdParty/llvm/libcxx/src/support/runtime/stdexcept_default.ipp @@ -0,0 +1,62 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "../../include/refstring.h" + +/* For _LIBCPPABI_VERSION */ +#if !defined(_LIBCPP_BUILDING_HAS_NO_ABI_LIBRARY) && (defined(LIBCXX_BUILDING_LIBCXXABI) || defined(LIBCXXRT)) +# include +#endif + +static_assert(sizeof(std::__libcpp_refstring) == sizeof(const char*), ""); + +namespace std // purposefully not using versioning namespace +{ + +logic_error::logic_error(const string& msg) : __imp_(msg.c_str()) {} + +logic_error::logic_error(const char* msg) : __imp_(msg) {} + +logic_error::logic_error(const logic_error& le) noexcept : __imp_(le.__imp_) {} + +logic_error& logic_error::operator=(const logic_error& le) noexcept { + __imp_ = le.__imp_; + return *this; +} + +runtime_error::runtime_error(const string& msg) : __imp_(msg.c_str()) {} + +runtime_error::runtime_error(const char* msg) : __imp_(msg) {} + +runtime_error::runtime_error(const runtime_error& re) noexcept : __imp_(re.__imp_) {} + +runtime_error& runtime_error::operator=(const runtime_error& re) noexcept { + __imp_ = re.__imp_; + return *this; +} + +#if !defined(_LIBCPPABI_VERSION) && !defined(LIBSTDCXX) + +const char* logic_error::what() const noexcept { return __imp_.c_str(); } + +const char* runtime_error::what() const noexcept { return __imp_.c_str(); } + +logic_error::~logic_error() noexcept {} +domain_error::~domain_error() noexcept {} +invalid_argument::~invalid_argument() noexcept {} +length_error::~length_error() noexcept {} +out_of_range::~out_of_range() noexcept {} + +runtime_error::~runtime_error() noexcept {} +range_error::~range_error() noexcept {} +overflow_error::~overflow_error() noexcept {} +underflow_error::~underflow_error() noexcept {} + +#endif + +} // namespace std diff --git a/src-ThirdParty/llvm/libcxx/src/system_error.cpp b/src-ThirdParty/llvm/libcxx/src/system_error.cpp new file mode 100644 index 00000000000..164fb72621c --- /dev/null +++ b/src-ThirdParty/llvm/libcxx/src/system_error.cpp @@ -0,0 +1,369 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <__assert> +#include <__config> +#include <__system_error/throw_system_error.h> +#include <__verbose_abort> +#include +#include +#include +#include +#include +#include +#include +#include + +#include "include/config_elast.h" + +#if defined(_LIBCPP_WIN32API) +# include +# include +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if defined(_LIBCPP_WIN32API) + +namespace { +std::optional __win_err_to_errc(int err) { + switch (err) { + case ERROR_ACCESS_DENIED: + return errc::permission_denied; + case ERROR_ALREADY_EXISTS: + return errc::file_exists; + case ERROR_BAD_NETPATH: + return errc::no_such_file_or_directory; + case ERROR_BAD_PATHNAME: + return errc::no_such_file_or_directory; + case ERROR_BAD_UNIT: + return errc::no_such_device; + case ERROR_BROKEN_PIPE: + return errc::broken_pipe; + case ERROR_BUFFER_OVERFLOW: + return errc::filename_too_long; + case ERROR_BUSY: + return errc::device_or_resource_busy; + case ERROR_BUSY_DRIVE: + return errc::device_or_resource_busy; + case ERROR_CANNOT_MAKE: + return errc::permission_denied; + case ERROR_CANTOPEN: + return errc::io_error; + case ERROR_CANTREAD: + return errc::io_error; + case ERROR_CANTWRITE: + return errc::io_error; + case ERROR_CURRENT_DIRECTORY: + return errc::permission_denied; + case ERROR_DEV_NOT_EXIST: + return errc::no_such_device; + case ERROR_DEVICE_IN_USE: + return errc::device_or_resource_busy; + case ERROR_DIR_NOT_EMPTY: + return errc::directory_not_empty; + case ERROR_DIRECTORY: + return errc::invalid_argument; + case ERROR_DISK_FULL: + return errc::no_space_on_device; + case ERROR_FILE_EXISTS: + return errc::file_exists; + case ERROR_FILE_NOT_FOUND: + return errc::no_such_file_or_directory; + case ERROR_HANDLE_DISK_FULL: + return errc::no_space_on_device; + case ERROR_INVALID_ACCESS: + return errc::permission_denied; + case ERROR_INVALID_DRIVE: + return errc::no_such_device; + case ERROR_INVALID_FUNCTION: + return errc::function_not_supported; + case ERROR_INVALID_HANDLE: + return errc::invalid_argument; + case ERROR_INVALID_NAME: + return errc::no_such_file_or_directory; + case ERROR_INVALID_PARAMETER: + return errc::invalid_argument; + case ERROR_LOCK_VIOLATION: + return errc::no_lock_available; + case ERROR_LOCKED: + return errc::no_lock_available; + case ERROR_NEGATIVE_SEEK: + return errc::invalid_argument; + case ERROR_NOACCESS: + return errc::permission_denied; + case ERROR_NOT_ENOUGH_MEMORY: + return errc::not_enough_memory; + case ERROR_NOT_READY: + return errc::resource_unavailable_try_again; + case ERROR_NOT_SAME_DEVICE: + return errc::cross_device_link; + case ERROR_NOT_SUPPORTED: + return errc::not_supported; + case ERROR_OPEN_FAILED: + return errc::io_error; + case ERROR_OPEN_FILES: + return errc::device_or_resource_busy; + case ERROR_OPERATION_ABORTED: + return errc::operation_canceled; + case ERROR_OUTOFMEMORY: + return errc::not_enough_memory; + case ERROR_PATH_NOT_FOUND: + return errc::no_such_file_or_directory; + case ERROR_READ_FAULT: + return errc::io_error; + case ERROR_REPARSE_TAG_INVALID: + return errc::invalid_argument; + case ERROR_RETRY: + return errc::resource_unavailable_try_again; + case ERROR_SEEK: + return errc::io_error; + case ERROR_SHARING_VIOLATION: + return errc::permission_denied; + case ERROR_TOO_MANY_OPEN_FILES: + return errc::too_many_files_open; + case ERROR_WRITE_FAULT: + return errc::io_error; + case ERROR_WRITE_PROTECT: + return errc::permission_denied; + default: + return {}; + } +} +} // namespace +#endif + +namespace { +#if _LIBCPP_HAS_THREADS + +// GLIBC also uses 1024 as the maximum buffer size internally. +constexpr size_t strerror_buff_size = 1024; + +string do_strerror_r(int ev); + +# if defined(_LIBCPP_MSVCRT_LIKE) +string do_strerror_r(int ev) { + char buffer[strerror_buff_size]; + if (::strerror_s(buffer, strerror_buff_size, ev) == 0) + return string(buffer); + std::snprintf(buffer, strerror_buff_size, "unknown error %d", ev); + return string(buffer); +} +# else + +// Only one of the two following functions will be used, depending on +// the return type of strerror_r: + +// For the GNU variant, a char* return value: +__attribute__((unused)) const char* handle_strerror_r_return(char* strerror_return, char* buffer) { + // GNU always returns a string pointer in its return value. The + // string might point to either the input buffer, or a static + // buffer, but we don't care which. + return strerror_return; +} + +// For the POSIX variant: an int return value. +__attribute__((unused)) const char* handle_strerror_r_return(int strerror_return, char* buffer) { + // The POSIX variant either: + // - fills in the provided buffer and returns 0 + // - returns a positive error value, or + // - returns -1 and fills in errno with an error value. + if (strerror_return == 0) + return buffer; + + // Only handle EINVAL. Other errors abort. + int new_errno = strerror_return == -1 ? errno : strerror_return; + if (new_errno == EINVAL) + return ""; + + _LIBCPP_ASSERT_INTERNAL(new_errno == ERANGE, "unexpected error from ::strerror_r"); + // FIXME maybe? 'strerror_buff_size' is likely to exceed the + // maximum error size so ERANGE shouldn't be returned. + std::abort(); +} + +// This function handles both GNU and POSIX variants, dispatching to +// one of the two above functions. +string do_strerror_r(int ev) { + char buffer[strerror_buff_size]; + // Preserve errno around the call. (The C++ standard requires that + // system_error functions not modify errno). + const int old_errno = errno; + const char* error_message = handle_strerror_r_return(::strerror_r(ev, buffer, strerror_buff_size), buffer); + // If we didn't get any message, print one now. + if (!error_message[0]) { + std::snprintf(buffer, strerror_buff_size, "Unknown error %d", ev); + error_message = buffer; + } + errno = old_errno; + return string(error_message); +} +# endif + +#endif // _LIBCPP_HAS_THREADS + +string make_error_str(const error_code& ec, string what_arg) { + if (ec) { + if (!what_arg.empty()) { + what_arg += ": "; + } + what_arg += ec.message(); + } + return what_arg; +} + +string make_error_str(const error_code& ec) { + if (ec) { + return ec.message(); + } + return string(); +} +} // namespace + +string __do_message::message(int ev) const { +#if !_LIBCPP_HAS_THREADS + return string(::strerror(ev)); +#else + return do_strerror_r(ev); +#endif +} + +class _LIBCPP_HIDDEN __generic_error_category : public __do_message { +public: + virtual const char* name() const noexcept; + virtual string message(int ev) const; +}; + +const char* __generic_error_category::name() const noexcept { return "generic"; } + +string __generic_error_category::message(int ev) const { +#ifdef _LIBCPP_ELAST + if (ev > _LIBCPP_ELAST) + return string("unspecified generic_category error"); +#endif // _LIBCPP_ELAST + return __do_message::message(ev); +} + +const error_category& generic_category() noexcept { + union AvoidDestroyingGenericCategory { + __generic_error_category generic_error_category; + constexpr explicit AvoidDestroyingGenericCategory() : generic_error_category() {} + ~AvoidDestroyingGenericCategory() {} + }; + constinit static AvoidDestroyingGenericCategory helper; + return helper.generic_error_category; +} + +class _LIBCPP_HIDDEN __system_error_category : public __do_message { +public: + virtual const char* name() const noexcept; + virtual string message(int ev) const; + virtual error_condition default_error_condition(int ev) const noexcept; +}; + +const char* __system_error_category::name() const noexcept { return "system"; } + +string __system_error_category::message(int ev) const { +#ifdef _LIBCPP_WIN32API + std::string result; + char* str = nullptr; + unsigned long num_chars = ::FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, + ev, + 0, + reinterpret_cast(&str), + 0, + nullptr); + auto is_whitespace = [](char ch) { return ch == '\n' || ch == '\r' || ch == ' '; }; + while (num_chars > 0 && is_whitespace(str[num_chars - 1])) + --num_chars; + + if (num_chars) + result = std::string(str, num_chars); + else + result = "Unknown error"; + + LocalFree(str); + return result; +#else +# ifdef _LIBCPP_ELAST + if (ev > _LIBCPP_ELAST) + return string("unspecified system_category error"); +# endif // _LIBCPP_ELAST + return __do_message::message(ev); +#endif +} + +error_condition __system_error_category::default_error_condition(int ev) const noexcept { +#ifdef _LIBCPP_WIN32API + // Remap windows error codes to generic error codes if possible. + if (ev == 0) + return error_condition(0, generic_category()); + if (auto maybe_errc = __win_err_to_errc(ev)) + return error_condition(static_cast(*maybe_errc), generic_category()); + return error_condition(ev, system_category()); +#else +# ifdef _LIBCPP_ELAST + if (ev > _LIBCPP_ELAST) + return error_condition(ev, system_category()); +# endif // _LIBCPP_ELAST + return error_condition(ev, generic_category()); +#endif +} + +const error_category& system_category() noexcept { + union AvoidDestroyingSystemCategory { + __system_error_category system_error_category; + constexpr explicit AvoidDestroyingSystemCategory() : system_error_category() {} + ~AvoidDestroyingSystemCategory() {} + }; + constinit static AvoidDestroyingSystemCategory helper; + return helper.system_error_category; +} + +// error_condition + +string error_condition::message() const { return __cat_->message(__val_); } + +// error_code + +string error_code::message() const { return __cat_->message(__val_); } + +// system_error + +system_error::system_error(error_code ec, const string& what_arg) + : runtime_error(make_error_str(ec, what_arg)), __ec_(ec) {} + +system_error::system_error(error_code ec, const char* what_arg) + : runtime_error(make_error_str(ec, what_arg)), __ec_(ec) {} + +system_error::system_error(error_code ec) : runtime_error(make_error_str(ec)), __ec_(ec) {} + +system_error::system_error(int ev, const error_category& ecat, const string& what_arg) + : runtime_error(make_error_str(error_code(ev, ecat), what_arg)), __ec_(error_code(ev, ecat)) {} + +system_error::system_error(int ev, const error_category& ecat, const char* what_arg) + : runtime_error(make_error_str(error_code(ev, ecat), what_arg)), __ec_(error_code(ev, ecat)) {} + +system_error::system_error(int ev, const error_category& ecat) + : runtime_error(make_error_str(error_code(ev, ecat))), __ec_(error_code(ev, ecat)) {} + +system_error::~system_error() noexcept {} + +void __throw_system_error(int ev, const char* what_arg) { +#if _LIBCPP_HAS_EXCEPTIONS + std::__throw_system_error(error_code(ev, generic_category()), what_arg); +#else + // The above could also handle the no-exception case, but for size, avoid referencing system_category() unnecessarily. + _LIBCPP_VERBOSE_ABORT( + "system_error was thrown in -fno-exceptions mode with error %i and message \"%s\"", ev, what_arg); +#endif +} + +_LIBCPP_END_NAMESPACE_STD diff --git a/src/native/clr/host/host-shared.cc b/src/native/clr/host/host-shared.cc index 5015a7a7a34..3016e23f7ae 100644 --- a/src/native/clr/host/host-shared.cc +++ b/src/native/clr/host/host-shared.cc @@ -34,4 +34,4 @@ auto HostCommon::get_java_class_name_for_TypeManager (jclass klass) noexcept -> } return ret; -} \ No newline at end of file +} diff --git a/src/native/clr/host/os-bridge.cc b/src/native/clr/host/os-bridge.cc index 6fa2ac7b719..94f188791cf 100644 --- a/src/native/clr/host/os-bridge.cc +++ b/src/native/clr/host/os-bridge.cc @@ -95,7 +95,16 @@ void OSBridge::_write_stack_trace (FILE *to, const char *const from, LogCategori if ((category == LOG_GREF && Logger::gref_to_logcat ()) || (category == LOG_LREF && Logger::lref_to_logcat ())) { - log_debug (category, "{}"sv, line); + log_debug ( + category, +#if defined(XA_HOST_NATIVEAOT) + "%s", + line.data () +#else + "{}"sv, + line +#endif + ); } if (to == nullptr) { @@ -111,7 +120,15 @@ void OSBridge::_write_stack_trace (FILE *to, const char *const from, LogCategori void OSBridge::_monodroid_gref_log (const char *message) noexcept { if (Logger::gref_to_logcat ()) { - log_debug (LOG_GREF, "{}"sv, optional_string (message)); + log_debug ( + LOG_GREF, +#if defined(XA_HOST_NATIVEAOT) + "%s", +#else + "{}"sv, +#endif + optional_string (message) + ); } if (Logger::gref_log () == nullptr) { diff --git a/src/native/clr/include/host/host-environment.hh b/src/native/clr/include/host/host-environment.hh index 2c4dc95252d..1d3f08eca9a 100644 --- a/src/native/clr/include/host/host-environment.hh +++ b/src/native/clr/include/host/host-environment.hh @@ -88,10 +88,27 @@ namespace xamarin::android { static_local_string dir (home_len + relative_path.length ()); Util::path_combine (dir, home.get_string_view (), relative_path); - log_debug (LOG_DEFAULT, "Creating XDG directory: {}"sv, optional_string (dir.get ())); + log_debug ( + LOG_DEFAULT, +#if defined(XA_HOST_NATIVEAOT) + "Creating XDG directory: %s", +#else + "Creating XDG directory: {}"sv, +#endif + optional_string (dir.get ()) + ); int rv = Util::create_directory (dir.get (), Constants::DEFAULT_DIRECTORY_MODE); if (rv < 0 && errno != EEXIST) { - log_warn (LOG_DEFAULT, "Failed to create XDG directory {}. {}"sv, optional_string (dir.get ()), strerror (errno)); + log_warn ( + LOG_DEFAULT, +#if defined(XA_HOST_NATIVEAOT) + "Failed to create XDG directory %s. %s", +#else + "Failed to create XDG directory {}. {}"sv, +#endif + optional_string (dir.get ()), + strerror (errno) + ); } if (!environment_variable_name.empty ()) { diff --git a/src/native/clr/include/runtime-base/android-system.hh b/src/native/clr/include/runtime-base/android-system.hh index b60871a93c4..a70d94f4e84 100644 --- a/src/native/clr/include/runtime-base/android-system.hh +++ b/src/native/clr/include/runtime-base/android-system.hh @@ -91,7 +91,16 @@ namespace xamarin::android { } } - log_debug (LOG_DEFAULT, "Creating public update directory: `{}`", override_dir); + log_debug ( + LOG_DEFAULT, +#if defined(XA_HOST_NATIVEAOT) + "Creating public update directory: `%s`", + override_dir.data () +#else + "Creating public update directory: `{}`"sv, + override_dir +#endif + ); Util::create_public_directory (override_dir); } diff --git a/src/native/clr/include/shared/log_types.hh b/src/native/clr/include/shared/log_types.hh index 7aef6e20456..e836deb1bb5 100644 --- a/src/native/clr/include/shared/log_types.hh +++ b/src/native/clr/include/shared/log_types.hh @@ -17,6 +17,23 @@ #undef log_info #endif +#if defined (XA_HOST_NATIVEAOT) +#define DO_LOG_PRINTF(_level, _category_, _fmt_, ...) \ + do { \ + if ((log_categories & ((_category_))) != 0) { \ + ::log_ ## _level ## _nocheck_printf ((_category_), _fmt_ __VA_OPT__(,) __VA_ARGS__); \ + } \ + } while (0) + + +#define log_debug(_category_, _fmt_, ...) DO_LOG_PRINTF (debug, (_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) +#define log_info(_category_, _fmt_, ...) DO_LOG_PRINTF (info, (_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) +#define log_warn(_category_, _fmt_, ...) log_warn_printf ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) +#define log_error(_category_, _fmt_, ...) log_error_printf ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) +#define log_fatal(_category_, _fmt_, ...) log_fatal_printf ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) + +#else // def XA_HOST_NATIVEAOT + #define DO_LOG_FMT(_level, _category_, _fmt_, ...) \ do { \ if ((log_categories & ((_category_))) != 0) { \ @@ -42,6 +59,7 @@ // NOTE: _fmt_ takes arguments in the std::format style not the POSIX printf style #define log_fatal(_category_, _fmt_, ...) log_fatal_fmt ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) +#endif // ndef XA_HOST_NATIVEAOT namespace xamarin::android { // A slightly faster alternative to other log functions as it doesn't parse the message @@ -61,6 +79,8 @@ namespace xamarin::android { } } +void log_debug_nocheck_printf (LogCategories category, const char *format, ...) noexcept; + template [[gnu::always_inline]] static inline constexpr void log_debug_nocheck_fmt (LogCategories category, std::format_string fmt, Args&& ...args) { @@ -73,6 +93,8 @@ static inline constexpr void log_debug_nocheck (LogCategories category, std::str log_write (category, xamarin::android::LogLevel::Debug, message.data ()); } +void log_info_nocheck_printf (LogCategories category, const char *format, ...) noexcept; + template [[gnu::always_inline]] static inline constexpr void log_info_nocheck_fmt (LogCategories category, std::format_string fmt, Args&& ...args) { @@ -85,6 +107,14 @@ static inline constexpr void log_info_nocheck (LogCategories category, std::stri log_write (category, xamarin::android::LogLevel::Info, message.data ()); } +void log_warn_printf (LogCategories category, const char *format, ...) noexcept; + +[[gnu::always_inline]] +static inline constexpr void log_warn_printf (LogCategories category, std::string_view const& message) noexcept +{ + log_write (category, xamarin::android::LogLevel::Warn, message); +} + template [[gnu::always_inline]] static inline constexpr void log_warn_fmt (LogCategories category, std::format_string fmt, Args&& ...args) noexcept { @@ -97,6 +127,14 @@ static inline constexpr void log_warn_fmt (LogCategories category, std::string_v log_write (category, xamarin::android::LogLevel::Warn, message.data ()); } +void log_error_printf (LogCategories category, const char *format, ...) noexcept; + +[[gnu::always_inline]] +static inline constexpr void log_error_printf (LogCategories category, std::string_view const& message) noexcept +{ + log_write (category, xamarin::android::LogLevel::Error, message); +} + template [[gnu::always_inline]] static inline constexpr void log_error_fmt (LogCategories category, std::format_string fmt, Args&& ...args) noexcept { @@ -109,6 +147,14 @@ static inline constexpr void log_error_fmt (LogCategories category, std::string_ log_write (category, xamarin::android::LogLevel::Error, message.data ()); } +void log_fatal_printf (LogCategories category, const char *format, ...) noexcept; + +[[gnu::always_inline]] +static inline constexpr void log_fatal_printf (LogCategories category, std::string_view const& message) noexcept +{ + log_write (category, xamarin::android::LogLevel::Fatal, message); +} + template [[gnu::always_inline]] static inline constexpr void log_fatal_fmt (LogCategories category, std::format_string fmt, Args&& ...args) noexcept { diff --git a/src/native/clr/runtime-base/util.cc b/src/native/clr/runtime-base/util.cc index 8f59681a55f..47dc26fc334 100644 --- a/src/native/clr/runtime-base/util.cc +++ b/src/native/clr/runtime-base/util.cc @@ -58,7 +58,17 @@ Util::create_public_directory (std::string_view const& dir) // Try to change the mode, just in case chmod (dir.data (), 0777); } else { - log_warn (LOG_DEFAULT, "Failed to create directory '{}'. {}"sv, dir, std::strerror (errno)); + log_warn ( + LOG_DEFAULT, +#if defined(XA_HOST_NATIVEAOT) + "Failed to create directory '%s'. %s", + dir.data (), +#else + "Failed to create directory '{}'. {}"sv, + dir, +#endif + std::strerror (errno) + ); } } umask (m); @@ -72,7 +82,16 @@ Util::monodroid_fopen (std::string_view const& filename, std::string_view const& */ FILE *ret = fopen (filename.data (), mode.data ()); if (ret == nullptr) { - log_error (LOG_DEFAULT, "fopen failed for file {}: {}", filename, strerror (errno)); + log_error ( + LOG_DEFAULT, +#if defined(XA_HOST_NATIVEAOT) + "fopen failed for file %s: %s", +#else + "fopen failed for file {}: {}"sv, +#endif + filename, + strerror (errno) + ); return nullptr; } @@ -87,7 +106,16 @@ void Util::set_world_accessable (std::string_view const& path) } while (r == -1 && errno == EINTR); if (r == -1) { - log_error (LOG_DEFAULT, "chmod(\"{}\", 0664) failed: {}", path, strerror (errno)); + log_error ( + LOG_DEFAULT, +#if defined(XA_HOST_NATIVEAOT) + "chmod(\"%s\", 0664) failed: %s", +#else + "chmod(\"{}\", 0664) failed: {}"sv, +#endif + path, + strerror (errno) + ); } } @@ -99,7 +127,15 @@ auto Util::set_world_accessible (int fd) noexcept -> bool } while (r == -1 && errno == EINTR); if (r == -1) { - log_error (LOG_DEFAULT, "fchmod() failed: {}"sv, strerror (errno)); + log_error ( + LOG_DEFAULT, +#if defined(XA_HOST_NATIVEAOT) + "fchmod() failed: %s", +#else + "fchmod() failed: {}"sv, +#endif + strerror (errno) + ); return false; } diff --git a/src/native/clr/shared/helpers.cc b/src/native/clr/shared/helpers.cc index 7850592af5a..d5b6490ce7e 100644 --- a/src/native/clr/shared/helpers.cc +++ b/src/native/clr/shared/helpers.cc @@ -35,7 +35,11 @@ Helpers::abort_application (LogCategories category, const char *message, bool lo log_fatal ( category, - "Abort at {}:{}:{} ('{}')", +#if defined(XA_HOST_NATIVEAOT) + "Abort at %s:%u:%u ('%s')", +#else + "Abort at {}:{}:{} ('{}')"sv, +#endif file_name, sloc.line (), sloc.column (), diff --git a/src/native/clr/shared/log_functions.cc b/src/native/clr/shared/log_functions.cc index acd5e705c06..8d80c949547 100644 --- a/src/native/clr/shared/log_functions.cc +++ b/src/native/clr/shared/log_functions.cc @@ -73,6 +73,13 @@ log_error (LogCategories category, const char *format, ...) DO_LOG (ANDROID_LOG_ERROR, category, format, args); } +void log_error_printf (LogCategories category, const char *format, ...) noexcept +{ + va_list args; + + DO_LOG (ANDROID_LOG_ERROR, category, format, args); +} + void log_fatal (LogCategories category, const char *format, ...) { @@ -81,6 +88,13 @@ log_fatal (LogCategories category, const char *format, ...) DO_LOG (ANDROID_LOG_FATAL, category, format, args); } +void log_fatal_printf (LogCategories category, const char *format, ...) noexcept +{ + va_list args; + + DO_LOG (ANDROID_LOG_ERROR, category, format, args); +} + void log_info_nocheck (LogCategories category, const char *format, ...) { @@ -101,6 +115,13 @@ log_warn (LogCategories category, const char *format, ...) DO_LOG (ANDROID_LOG_WARN, category, format, args); } +// void log_warn_printf (LogCategories category, const char *format, ...) noexcept +// { +// va_list args; + +// DO_LOG (ANDROID_LOG_ERROR, category, format, args); +// } + void log_debug_nocheck (LogCategories category, const char *format, ...) { diff --git a/src/native/common/include/runtime-base/strings.hh b/src/native/common/include/runtime-base/strings.hh index 8e6bf8cbaea..3a2b0e630d6 100644 --- a/src/native/common/include/runtime-base/strings.hh +++ b/src/native/common/include/runtime-base/strings.hh @@ -205,7 +205,15 @@ namespace xamarin::android { } if (!can_access (start_index)) { - log_error (LOG_DEFAULT, "Cannot convert string to integer, index {} is out of range", start_index); + log_error ( + LOG_DEFAULT, +#if defined(XA_HOST_NATIVEAOT) + "Cannot convert string to integer, index %z is out of range", +#else + "Cannot convert string to integer, index {} is out of range"sv, +#endif + start_index + ); return false; } @@ -229,17 +237,44 @@ namespace xamarin::android { } if (out_of_range || errno == ERANGE) { - log_error (LOG_DEFAULT, "Value {} is out of range of this type ({}..{})", reinterpret_cast(s), static_cast(min), static_cast(max)); + log_error ( + LOG_DEFAULT, +#if defined(XA_HOST_NATIVEAOT) + "Value %s is out of range of this type (%lld..%llu)", +#else + "Value {} is out of range of this type ({}..{})"sv, +#endif + reinterpret_cast(s), + static_cast(min), + static_cast(max) + ); return false; } if (endp == s) { - log_error (LOG_DEFAULT, "Value {} does not represent a base {} integer", reinterpret_cast(s), base); + log_error ( + LOG_DEFAULT, +#if defined(XA_HOST_NATIVEAOT) + "Value %s does not represent a base %d integer", +#else + "Value {} does not represent a base {} integer"sv, +#endif + reinterpret_cast(s), + base + ); return false; } if (*endp != '\0') { - log_error (LOG_DEFAULT, "Value {} has non-numeric characters at the end", reinterpret_cast(s)); + log_error ( + LOG_DEFAULT, +#if defined(XA_HOST_NATIVEAOT) + "Value %s has non-numeric characters at the end", +#else + "Value {} has non-numeric characters at the end"sv, +#endif + reinterpret_cast(s) + ); return false; } diff --git a/src/native/common/include/runtime-base/timing-internal.hh b/src/native/common/include/runtime-base/timing-internal.hh index 31099b86322..9f87742563c 100644 --- a/src/native/common/include/runtime-base/timing-internal.hh +++ b/src/native/common/include/runtime-base/timing-internal.hh @@ -260,7 +260,16 @@ namespace xamarin::android { // likely we'll run out of memory way, way, way before that happens size_t old_size = events.capacity (); events.reserve (old_size << 1); - log_warn (LOG_TIMING, "Reallocated timing event buffer from {} to {}"sv, old_size, events.size ()); + log_warn ( + LOG_TIMING, +#if defined (XA_HOST_NATIVEAOT) + "Reallocated timing event buffer from %z to %z", +#else + "Reallocated timing event buffer from {} to {}"sv, +#endif + old_size, + events.size () + ); } } @@ -378,7 +387,15 @@ namespace xamarin::android { { struct timespec t; if (clock_gettime (CLOCK_MONOTONIC_RAW, &t) != 0) [[unlikely]] { - log_warn (LOG_TIMING, "clock_gettime failed for CLOCK_MONOTONIC_RAW: {}"sv, optional_string (strerror (errno))); + log_warn ( + LOG_TIMING, +#if defined(XA_HOST_NATIVEAOT) + "clock_gettime failed for CLOCK_MONOTONIC_RAW: %s", +#else + "clock_gettime failed for CLOCK_MONOTONIC_RAW: {}"sv, +#endif + optional_string (strerror (errno)) + ); return {}; // Results will be nonsensical, but no point in aborting the app } return time_point (chrono::seconds (t.tv_sec) + chrono::nanoseconds (t.tv_nsec)); @@ -496,7 +513,11 @@ namespace xamarin::android { log_warn ( LOG_TIMING, +#if defined(XA_HOST_NATIVEAOT) + "Unknown event kind '%u' logged", +#else "Unknown event kind '{}' logged"sv, +#endif static_cast>(kind) ); append_desc ("unknown event kind"sv); @@ -510,7 +531,15 @@ namespace xamarin::android { auto is_valid_event_index (size_t index, std::source_location sloc = std::source_location::current ()) const noexcept -> bool { if (index >= events.capacity ()) [[unlikely]] { - log_warn (LOG_TIMING, "Invalid event index passed to method '{}'"sv, sloc.function_name ()); + log_warn ( + LOG_TIMING, +#if defined(XA_HOST_NATIVEAOT) + "Invalid event index passed to method '%s'", +#else + "Invalid event index passed to method '{}'"sv, +#endif + sloc.function_name () + ); return false; } diff --git a/src/native/nativeaot/cxx-abi/cxa_virtual.cc b/src/native/nativeaot/cxx-abi/cxa_virtual.cc new file mode 100644 index 00000000000..0f20caf07c5 --- /dev/null +++ b/src/native/nativeaot/cxx-abi/cxa_virtual.cc @@ -0,0 +1,10 @@ +#include + +namespace __cxxabiv1 { + extern "C" { + [[noreturn]] + void __cxa_pure_virtual(void) { + xamarin::android::Helpers::abort_application ("Pure virtual function called!"); + } + } +} diff --git a/src/native/nativeaot/cxx-abi/no_exceptions.cc b/src/native/nativeaot/cxx-abi/no_exceptions.cc new file mode 100644 index 00000000000..e2c99538ae6 --- /dev/null +++ b/src/native/nativeaot/cxx-abi/no_exceptions.cc @@ -0,0 +1,26 @@ +#include + +#include + +namespace std { + exception_ptr::~exception_ptr () noexcept + { + xamarin::android::Helpers::abort_application ("exception_ptr not implemented\n"sv); + } + + exception_ptr::exception_ptr (const exception_ptr& other) noexcept : __ptr_(other.__ptr_) + { + xamarin::android::Helpers::abort_application ("exception_ptr not implemented\n"); + } + + exception_ptr& exception_ptr::operator= ([[maybe_unused]] const exception_ptr& other) noexcept + { + xamarin::android::Helpers::abort_application ("exception_ptr not yet implemented\n"); + } + + [[noreturn]] + void rethrow_exception ([[maybe_unused]] exception_ptr p) + { + xamarin::android::Helpers::abort_application ("exception_ptr not yet implemented\n"); + } +} // namespace std diff --git a/src/native/nativeaot/cxx-abi/stdexcept.cc b/src/native/nativeaot/cxx-abi/stdexcept.cc index 96183d92237..e45d51d856e 100644 --- a/src/native/nativeaot/cxx-abi/stdexcept.cc +++ b/src/native/nativeaot/cxx-abi/stdexcept.cc @@ -1,5 +1,8 @@ #include #include +#include + +#include #include diff --git a/src/native/nativeaot/cxx-abi/system_error.cc b/src/native/nativeaot/cxx-abi/system_error.cc index e03c7d4aff2..d27aa945f51 100644 --- a/src/native/nativeaot/cxx-abi/system_error.cc +++ b/src/native/nativeaot/cxx-abi/system_error.cc @@ -7,13 +7,13 @@ _LIBCPP_BEGIN_NAMESPACE_STD -void __throw_system_error (int ev, const char* what_arg) -{ - char *message = nullptr; - int n = asprintf (&message, "system_error was thrown in -fno-exceptions mode with error %i and message \"%s\"", ev, what_arg); - xamarin::android::Helpers::abort_application ( - n == -1 ? "system_error was thrown in -fno-exceptions mode" : message - ); -} +// void __throw_system_error (int ev, const char* what_arg) +// { +// char *message = nullptr; +// int n = asprintf (&message, "system_error was thrown in -fno-exceptions mode with error %i and message \"%s\"", ev, what_arg); +// xamarin::android::Helpers::abort_application ( +// n == -1 ? "system_error was thrown in -fno-exceptions mode" : message +// ); +// } _LIBCPP_END_NAMESPACE_STD diff --git a/src/native/nativeaot/host/CMakeLists.txt b/src/native/nativeaot/host/CMakeLists.txt index 1c3aa66aed5..014f9e8475b 100644 --- a/src/native/nativeaot/host/CMakeLists.txt +++ b/src/native/nativeaot/host/CMakeLists.txt @@ -47,9 +47,12 @@ set(XAMARIN_MONODROID_SOURCES # libc++ sources ${LIBCXX_SOURCE_DIR}/condition_variable.cpp ${LIBCXX_SOURCE_DIR}/condition_variable_destructor.cpp + ${LIBCXX_SOURCE_DIR}/error_category.cpp + ${LIBCXX_SOURCE_DIR}/future.cpp ${LIBCXX_SOURCE_DIR}/memory.cpp ${LIBCXX_SOURCE_DIR}/mutex.cpp ${LIBCXX_SOURCE_DIR}/mutex_destructor.cpp + ${LIBCXX_SOURCE_DIR}/system_error.cpp ${LIBCXX_SOURCE_DIR}/thread.cpp ${LIBCXX_SOURCE_DIR}/verbose_abort.cpp @@ -57,7 +60,9 @@ set(XAMARIN_MONODROID_SOURCES ${LIBCXXABI_SOURCE_DIR}/stdlib_exception.cpp # Local versions of C++ ABI sources + ${LOCAL_CXXABI_SOURCE_DIR}/cxa_virtual.cc ${LOCAL_CXXABI_SOURCE_DIR}/new.cc + ${LOCAL_CXXABI_SOURCE_DIR}/no_exceptions.cc ${LOCAL_CXXABI_SOURCE_DIR}/stdexcept.cc ${LOCAL_CXXABI_SOURCE_DIR}/string.cc ${LOCAL_CXXABI_SOURCE_DIR}/system_error.cc @@ -78,6 +83,18 @@ set(XAMARIN_MONODROID_SOURCES ${CLR_SOURCES_PATH}/shared/log_functions.cc ) +set_source_files_properties ( + ${LIBCXX_SOURCE_DIR}/exception.cpp + PROPERTIES + COMPILE_DEFINITIONS "LIBCXXRT" +) + +set_source_files_properties ( + ${LOCAL_CXXABI_SOURCE_DIR}/stdexcept.cc + PROPERTIES + INCLUDE_DIRECTORIES "${LIBCXX_SOURCE_DIR}" +) + list(APPEND LOCAL_CLANG_CHECK_SOURCES ${XAMARIN_MONODROID_SOURCES} ) diff --git a/src/native/nativeaot/host/bridge-processing.cc b/src/native/nativeaot/host/bridge-processing.cc index b87a49b431b..30bf0f948a0 100644 --- a/src/native/nativeaot/host/bridge-processing.cc +++ b/src/native/nativeaot/host/bridge-processing.cc @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -24,14 +24,14 @@ void BridgeProcessing::naot_initialize_on_runtime_init (JNIEnv *env) noexcept constexpr auto ABSENT = "absent"sv; constexpr auto PRESENT = "present"sv; - Helpers::abort_application ( - LOG_DEFAULT, - std::format ( - "Failed to find GCUserPeerable method(s): jiAddManagedReference ({}); jiClearManagedReferences ({})"sv, - GCUserPeerable_jiAddManagedReference == nullptr ? ABSENT : PRESENT, - GCUserPeerable_jiClearManagedReferences == nullptr ? ABSENT : PRESENT - ) - ); + // This is fugly, but more type safe than printf format parsing + std::string err_msg { "Failed to find GCUserPeerable method(s): jiAddManagedReference ("sv }; + err_msg.append (GCUserPeerable_jiAddManagedReference == nullptr ? ABSENT : PRESENT); + err_msg.append ("); jiClearManagedReferences ("sv); + err_msg.append (GCUserPeerable_jiClearManagedReferences == nullptr ? ABSENT : PRESENT); + err_msg.append (")"sv); + + Helpers::abort_application (LOG_DEFAULT, err_msg); } } diff --git a/src/native/nativeaot/host/host.cc b/src/native/nativeaot/host/host.cc index bf43f0f47de..bc57f1f349e 100644 --- a/src/native/nativeaot/host/host.cc +++ b/src/native/nativeaot/host/host.cc @@ -35,7 +35,7 @@ auto HostCommon::Java_JNI_OnLoad (JavaVM *vm, void *reserved) noexcept -> jint for (uint32_t i = 0; i < __jni_on_load_handler_count; i++) { log_debug ( LOG_ASSEMBLY, - "Calling JNI on-load init func '{}' ({:p})", + "Calling JNI on-load init func '%s' (%p)", optional_string (__jni_on_load_handler_names[i]), reinterpret_cast(__jni_on_load_handlers[i]) ); From 1143af9f407b491c366cbfe84a7bfc5f4bc6674f Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Tue, 13 Jan 2026 18:55:49 +0100 Subject: [PATCH 13/39] Not needed --- src/native/nativeaot/host/CMakeLists.txt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/native/nativeaot/host/CMakeLists.txt b/src/native/nativeaot/host/CMakeLists.txt index 014f9e8475b..4056101b460 100644 --- a/src/native/nativeaot/host/CMakeLists.txt +++ b/src/native/nativeaot/host/CMakeLists.txt @@ -83,12 +83,6 @@ set(XAMARIN_MONODROID_SOURCES ${CLR_SOURCES_PATH}/shared/log_functions.cc ) -set_source_files_properties ( - ${LIBCXX_SOURCE_DIR}/exception.cpp - PROPERTIES - COMPILE_DEFINITIONS "LIBCXXRT" -) - set_source_files_properties ( ${LOCAL_CXXABI_SOURCE_DIR}/stdexcept.cc PROPERTIES From 4bc24e73059fbae358dcad82d527bb5496d4337b Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Wed, 14 Jan 2026 11:20:32 +0100 Subject: [PATCH 14/39] Fix 2x oops --- src/native/common/include/runtime-base/strings.hh | 1 + 1 file changed, 1 insertion(+) diff --git a/src/native/common/include/runtime-base/strings.hh b/src/native/common/include/runtime-base/strings.hh index 3a2b0e630d6..7c085371cb9 100644 --- a/src/native/common/include/runtime-base/strings.hh +++ b/src/native/common/include/runtime-base/strings.hh @@ -10,6 +10,7 @@ #include #include +#include #include #if defined(XA_HOST_MONOVM) From 4ceb4007c9068d5e2845783df8c5a485d82c8d18 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Wed, 14 Jan 2026 17:39:01 +0100 Subject: [PATCH 15/39] A portion of `std::format` instances removed for NativeAOT More to face the axe --- src/native/clr/host/bridge-processing.cc | 82 +++++++++++++++++-- src/native/clr/include/constants.hh | 9 ++ .../clr/include/host/host-environment.hh | 11 ++- src/native/clr/include/runtime-base/util.hh | 43 ++++++++-- src/native/clr/shared/log_functions.cc | 14 ++-- .../common/include/runtime-base/strings.hh | 33 ++++++++ .../common/include/runtime-base/timing.hh | 27 +++++- src/native/common/include/shared/helpers.hh | 2 + 8 files changed, 198 insertions(+), 23 deletions(-) diff --git a/src/native/clr/host/bridge-processing.cc b/src/native/clr/host/bridge-processing.cc index 5ce16eb4923..e4c5a88e8cd 100644 --- a/src/native/clr/host/bridge-processing.cc +++ b/src/native/clr/host/bridge-processing.cc @@ -359,10 +359,32 @@ void BridgeProcessingShared::log_weak_to_gref (jobject weak, jobject handle) noe return; } - OSBridge::_monodroid_gref_log ( - std::format ("take_global_ref wref={:#x} -> handle={:#x}\n"sv, - reinterpret_cast (weak), - reinterpret_cast (handle)).data ()); + auto weak_p = reinterpret_cast (weak); + auto handle_p = reinterpret_cast (handle); + + if constexpr (Constants::is_nativeaot) { + constexpr size_t BufferSize = 128; + dynamic_local_string message; + bool formatted = format_printf ( + message, + "take_global_ref wref={:#x} -> handle={:#x}\n", + weak_p, handle_p + ); + + if (formatted) [[likely]] { + OSBridge::_monodroid_gref_log (message.get ()); + } else { + OSBridge::_monodroid_gref_log ("take_global_ref: failed to format log message\n"); + } + } else { + OSBridge::_monodroid_gref_log ( + std::format ( + "take_global_ref wref={:#x} -> handle={:#x}\n"sv, + weak_p, + handle_p + ).c_str () + ); + } } [[gnu::always_inline]] @@ -372,8 +394,26 @@ void BridgeProcessingShared::log_weak_ref_collected (jobject weak) noexcept return; } - OSBridge::_monodroid_gref_log ( - std::format ("handle {:#x}/W; was collected by a Java GC"sv, reinterpret_cast (weak)).data ()); + auto weak_p = reinterpret_cast (weak); + if constexpr (Constants::is_nativeaot) { + constexpr size_t BufferSize = 128; + dynamic_local_string message; + bool formatted = format_printf ( + message, + "handle 0x%x/W; was collected by a Java GC", + weak_p + ); + + if (formatted) [[likely]] { + OSBridge::_monodroid_gref_log (message.get ()); + } else { + OSBridge::_monodroid_gref_log ("handle was collected by a Java GC, failed to format full message"); + } + } else { + OSBridge::_monodroid_gref_log ( + std::format ("handle {:#x}/W; was collected by a Java GC"sv, weak_p).c_str () + ); + } } [[gnu::always_inline]] @@ -383,7 +423,24 @@ void BridgeProcessingShared::log_take_weak_global_ref (jobject handle) noexcept return; } - OSBridge::_monodroid_gref_log (std::format ("take_weak_global_ref handle={:#x}\n"sv, reinterpret_cast (handle)).data ()); + auto handle_p = reinterpret_cast (handle); + if constexpr (Constants::is_nativeaot) { + constexpr size_t BufferSize = 128; + dynamic_local_string message; + bool formatted = format_printf ( + message, + "take_weak_global_ref handle={:#x}\n", + handle_p + ); + + if (formatted) [[likely]] { + OSBridge::_monodroid_gref_log (message.get ()); + } else { + OSBridge::_monodroid_gref_log ("take_weak_global_ref handle taken, failed to format full message"); + } + } else { + OSBridge::_monodroid_gref_log (std::format ("take_weak_global_ref handle={:#x}\n"sv, handle_p).c_str ()); + } } [[gnu::always_inline]] @@ -432,5 +489,14 @@ void BridgeProcessingShared::log_gc_summary () noexcept } } - log_info (LOG_GC, "GC cleanup summary: {} objects tested - resurrecting {}.", total, alive); + log_info ( + LOG_GC, +#if defined(XA_HOST_NATIVEAOT) + "GC cleanup summary: %z objects tested - resurrecting %z.", +#else + "GC cleanup summary: {} objects tested - resurrecting {}."sv, +#endif + total, + alive + ); } diff --git a/src/native/clr/include/constants.hh b/src/native/clr/include/constants.hh index d9764c9cefb..c325c8d66d8 100644 --- a/src/native/clr/include/constants.hh +++ b/src/native/clr/include/constants.hh @@ -31,6 +31,15 @@ namespace xamarin::android { static constexpr bool is_release_build = false; static constexpr bool is_debug_build = true; #endif + +#if defined(XA_HOST_NATIVEAOT) + static constexpr bool is_clr = false; + static constexpr bool is_nativeaot = true; +#else + static constexpr bool is_clr = true; + static constexpr bool is_nativeaot = false; +#endif + static constexpr std::string_view MANGLED_ASSEMBLY_NAME_EXT { ".so" }; static constexpr std::string_view dso_suffix { ".so" }; static constexpr std::string_view DSO_PREFIX { "lib" }; diff --git a/src/native/clr/include/host/host-environment.hh b/src/native/clr/include/host/host-environment.hh index 1d3f08eca9a..aa2898524e1 100644 --- a/src/native/clr/include/host/host-environment.hh +++ b/src/native/clr/include/host/host-environment.hh @@ -42,7 +42,16 @@ namespace xamarin::android { 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)); + log_debug ( + LOG_DEFAULT, +#if defined(XA_HOST_NATIVEAOT) + " System property %s = '%s'", +#else + " System property {} = '{}'"sv, +#endif + optional_string (name), + optional_string (value) + ); } [[gnu::flatten, gnu::always_inline]] diff --git a/src/native/clr/include/runtime-base/util.hh b/src/native/clr/include/runtime-base/util.hh index 656a3d4a68a..94abddb170e 100644 --- a/src/native/clr/include/runtime-base/util.hh +++ b/src/native/clr/include/runtime-base/util.hh @@ -135,6 +135,7 @@ namespace xamarin::android { return fstatat (dirfd, file.data (), &sbuf, 0) == 0 && fs_entry_is_mode (sbuf, S_IFREG); } +#if !defined (XA_HOST_NATIVEAOT) static auto get_file_size_at (int dirfd, const char *file_name) noexcept -> std::optional { struct stat sbuf; @@ -150,13 +151,33 @@ namespace xamarin::android { { return get_file_size_at (dirfd, file_name.data ()); } +#endif // ndef XA_HOST_NATIVEAOT [[gnu::flatten, gnu::always_inline]] static void set_environment_variable (const char *name, const char *value) noexcept { - log_debug (LOG_DEFAULT, "Setting environment variable {} = '{}'", optional_string (name), optional_string (value)); + log_debug ( + LOG_DEFAULT, +#if defined(XA_HOST_NATIVEAOT) + "Setting environment variable %s = '%s'", +#else + "Setting environment variable {} = '{}'"sv, +#endif + optional_string (name), + optional_string (value) + ); + if (::setenv (name, value, 1) < 0) { - log_warn (LOG_DEFAULT, "Failed to set environment variable '{}': {}", name, ::strerror (errno)); + log_warn ( + LOG_DEFAULT, +#if defined(XA_HOST_NATIVEAOT) + "Failed to set environment variable '%s': %s", +#else + "Failed to set environment variable '{}': {}"sv, +#endif + name, + ::strerror (errno) + ); } } @@ -178,7 +199,19 @@ namespace xamarin::android { if (createDirectory) { int rv = create_directory (value.get_cstr (), mode); if (rv < 0 && errno != EEXIST) { - log_warn (LOG_DEFAULT, "Failed to create directory '{}' for environment variable '{}'. {}", value.get_string_view (), name, strerror (errno)); + log_warn ( + LOG_DEFAULT, +#if defined(XA_HOST_NATIVEAOT) + "Failed to create directory '%s' for environment variable '%s'. %s", + value.get_cstr (), + name.data (), +#else + "Failed to create directory '{}' for environment variable '{}'. {}"sv, + value.get_string_view (), + name, +#endif + ::strerror (errno) + ); } } set_environment_variable (name, value); @@ -195,6 +228,7 @@ namespace xamarin::android { return page_size; } +#if !defined (XA_HOST_NATIVEAOT) static detail::mmap_info mmap_file (int fd, uint32_t offset, size_t size, std::string_view const& filename) noexcept { detail::mmap_info file_info; @@ -225,7 +259,7 @@ namespace xamarin::android { log_info ( LOG_ASSEMBLY, - " mmap_start: {:<8p}; mmap_end: {:<8p} mmap_len: {:<12} file_start: {:<8p} file_end: {:<8p} file_len: {:<12} apk descriptor: {} file: {}", + " mmap_start: {:<8p}; mmap_end: {:<8p} mmap_len: {:<12} file_start: {:<8p} file_end: {:<8p} file_len: {:<12} apk descriptor: {} file: {}"sv, mmap_info.area, pointer_add (mmap_info.area, mmap_info.size), mmap_info.size, @@ -239,7 +273,6 @@ namespace xamarin::android { return file_info; } -#if !defined(XA_HOST_NATIVEAOT) [[gnu::always_inline]] static std::tuple get_wrapper_dso_payload_pointer_and_size (detail::mmap_info const& map_info, std::string_view const& file_name) noexcept { diff --git a/src/native/clr/shared/log_functions.cc b/src/native/clr/shared/log_functions.cc index 8d80c949547..740653b5721 100644 --- a/src/native/clr/shared/log_functions.cc +++ b/src/native/clr/shared/log_functions.cc @@ -107,6 +107,9 @@ log_info_nocheck (LogCategories category, const char *format, ...) DO_LOG (ANDROID_LOG_INFO, category, format, args); } +[[gnu::alias ("_Z16log_info_nocheck14_LogCategoriesPKcz")]] +void log_info_nocheck_printf (LogCategories category, const char *format, ...) noexcept; + void log_warn (LogCategories category, const char *format, ...) { @@ -115,12 +118,8 @@ log_warn (LogCategories category, const char *format, ...) DO_LOG (ANDROID_LOG_WARN, category, format, args); } -// void log_warn_printf (LogCategories category, const char *format, ...) noexcept -// { -// va_list args; - -// DO_LOG (ANDROID_LOG_ERROR, category, format, args); -// } +[[gnu::alias ("_Z8log_warn14_LogCategoriesPKcz")]] +void log_warn_printf (LogCategories category, const char *format, ...) noexcept; void log_debug_nocheck (LogCategories category, const char *format, ...) @@ -134,6 +133,9 @@ log_debug_nocheck (LogCategories category, const char *format, ...) DO_LOG (ANDROID_LOG_DEBUG, category, format, args); } +[[gnu::alias ("_Z17log_debug_nocheck14_LogCategoriesPKcz")]] +void log_debug_nocheck_printf (LogCategories category, const char *format, ...) noexcept; + namespace xamarin::android { void log_write (LogCategories category, LogLevel level, const char *message) noexcept diff --git a/src/native/common/include/runtime-base/strings.hh b/src/native/common/include/runtime-base/strings.hh index 7c085371cb9..203e7eff141 100644 --- a/src/native/common/include/runtime-base/strings.hh +++ b/src/native/common/include/runtime-base/strings.hh @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -945,6 +946,7 @@ namespace xamarin::android { } } + public: [[gnu::always_inline]] void resize_for_extra (size_t needed_space) noexcept { @@ -1017,4 +1019,35 @@ namespace xamarin::android { // Useful aliases using dynamic_local_property_string = dynamic_local_string; using dynamic_local_path_string = dynamic_local_string; + + // helpers + template + static inline auto format_printf (dynamic_local_string& dest, const char* format, ...) noexcept -> bool + { + va_list args; + va_start (args, format); + int n = vsnprintf (dest.get (), dest.size (), format, args); + va_end (args); + + if (n < 0) { + return false; + } + + auto res = static_cast(n); + if (res < dest.size ()) { + return true; + } + + // resize_for_extra adds one more byte for the NUL character + dest.resize_for_extra (res - dest.size ()); + if (dest.size () <= res) { + return false; + } + + va_start (args, format); + n = vsnprintf (dest.get (), dest.size (), format, args); + va_end (args); + + return n != -1; + } } diff --git a/src/native/common/include/runtime-base/timing.hh b/src/native/common/include/runtime-base/timing.hh index 6e883c240f8..31c8fc013bf 100644 --- a/src/native/common/include/runtime-base/timing.hh +++ b/src/native/common/include/runtime-base/timing.hh @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -10,6 +11,7 @@ #include #include "timing-internal.hh" +#include namespace xamarin::android { @@ -85,15 +87,34 @@ namespace xamarin::android using namespace std::literals; auto interval = seq->end - seq->start; // nanoseconds + auto seconds = static_cast((std::chrono::duration_cast(interval).count ())); + auto milliseconds = static_cast((std::chrono::duration_cast(interval)).count ()); + auto nanoseconds = static_cast((interval % 1ms).count ()); + +#if defined(XA_HOST_NATIVEAOT) + constexpr size_t BufferSize = 256; + dynamic_local_string log_message; + bool formatted = format_printf ( + log_message, + "%s; elapsed: %lu:%lu::%lu", + message == nullptr ? "" : message, + seconds, milliseconds, nanoseconds + ); + + if (formatted) [[likely]] { + log_write (LOG_TIMING, level, log_message.get ()); + } else { + log_error (LOG_TIMING, "format_printf failed. %s", strerror (errno)); + } +#else auto text = std::format ( "{}; elapsed: {}:{}::{}"sv, message == nullptr ? ""sv : message, - static_cast((std::chrono::duration_cast(interval).count ())), - static_cast((std::chrono::duration_cast(interval)).count ()), - static_cast((interval % 1ms).count ()) + seconds, milliseconds, nanoseconds ); log_write (LOG_TIMING, level, text.c_str ()); +#endif } private: diff --git a/src/native/common/include/shared/helpers.hh b/src/native/common/include/shared/helpers.hh index 22e29b40f25..2ab97262d3f 100644 --- a/src/native/common/include/shared/helpers.hh +++ b/src/native/common/include/shared/helpers.hh @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include #include #include From f1ed9d4fe203d63c334bc8b0a45c34e3405417bc Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 15 Jan 2026 12:08:56 +0100 Subject: [PATCH 16/39] More std::format eliminated from the NativeAOT host --- src/native/clr/host/gc-bridge.cc | 40 +++++++++-- src/native/clr/host/host-shared.cc | 10 ++- src/native/clr/host/os-bridge.cc | 41 ++++++++--- .../clr/include/shared/format-helpers.hh | 72 +++++++++++++++++++ 4 files changed, 149 insertions(+), 14 deletions(-) create mode 100644 src/native/clr/include/shared/format-helpers.hh diff --git a/src/native/clr/host/gc-bridge.cc b/src/native/clr/host/gc-bridge.cc index 6bf3a387e96..4cbfaf0b9a5 100644 --- a/src/native/clr/host/gc-bridge.cc +++ b/src/native/clr/host/gc-bridge.cc @@ -45,7 +45,7 @@ void GCBridge::trigger_java_gc (JNIEnv *env) noexcept env->ExceptionDescribe (); env->ExceptionClear (); - log_error (LOG_DEFAULT, "Java GC failed"); + log_error (LOG_DEFAULT, "Java GC failed"sv); } void GCBridge::mark_cross_references (MarkCrossReferencesArgs *args) noexcept @@ -85,10 +85,19 @@ void GCBridge::log_mark_cross_references_args_if_enabled (MarkCrossReferencesArg return; } - log_info (LOG_GC, "cross references callback invoked with {} sccs and {} xrefs.", args->ComponentCount, args->CrossReferenceCount); + log_info ( + LOG_GC, +#if defined(XA_HOST_NATIVEAOT) + "cross references callback invoked with %z sccs and %z xrefs.", +#else + "cross references callback invoked with {} sccs and {} xrefs."sv, +#endif + args->ComponentCount, + args->CrossReferenceCount + ); JNIEnv *env = OSBridge::ensure_jnienv (); - + for (size_t i = 0; i < args->ComponentCount; ++i) { const StronglyConnectedComponent &scc = args->Components [i]; log_info (LOG_GC, "group {} with {} objects", i, scc.Count); @@ -104,7 +113,11 @@ void GCBridge::log_mark_cross_references_args_if_enabled (MarkCrossReferencesArg for (size_t i = 0; i < args->CrossReferenceCount; ++i) { size_t source_index = args->CrossReferences [i].SourceGroupIndex; size_t dest_index = args->CrossReferences [i].DestinationGroupIndex; +#if defined(XA_HOST_NATIVEAOT) + log_info_nocheck_printf (LOG_GC, "xref [%z] %z -> %z", i, source_index, dest_index); +#else log_info_nocheck_fmt (LOG_GC, "xref [{}] {} -> {}", i, source_index, dest_index); +#endif } } @@ -118,9 +131,26 @@ void GCBridge::log_handle_context (JNIEnv *env, HandleContext *ctx) noexcept jclass java_class = env->GetObjectClass (handle); if (java_class != nullptr) { char *class_name = Host::get_java_class_name_for_TypeManager (java_class); - log_info (LOG_GC, "gref {:#x} [{}]", reinterpret_cast (handle), class_name); + log_info ( + LOG_GC, +#if defined(XA_HOST_NATIVEAOT) + "gref %p [%s]", +#else + "gref {:#x} [{}]"sv, +#endif + reinterpret_cast (handle), + class_name + ); free (class_name); } else { - log_info (LOG_GC, "gref {:#x} [unknown class]", reinterpret_cast (handle)); + log_info ( + LOG_GC, +#if defined(XA_HOST_NATIVEAOT) + "gref %p [unknown class]", +#else + "gref {:#x} [unknown class]"sv, +#endif + reinterpret_cast (handle) + ); } } diff --git a/src/native/clr/host/host-shared.cc b/src/native/clr/host/host-shared.cc index 3016e23f7ae..5d0341e6523 100644 --- a/src/native/clr/host/host-shared.cc +++ b/src/native/clr/host/host-shared.cc @@ -12,7 +12,15 @@ auto HostCommon::get_java_class_name_for_TypeManager (jclass klass) noexcept -> JNIEnv *env = OSBridge::ensure_jnienv (); jstring name = reinterpret_cast (env->CallObjectMethod (klass, Class_getName)); if (name == nullptr) { - log_error (LOG_DEFAULT, "Failed to obtain Java class name for object at {:p}", reinterpret_cast(klass)); + log_error ( + LOG_DEFAULT, +#if defined(XA_HOST_NATIVEAOT) + "Failed to obtain Java class name for object at %p " , +#else + "Failed to obtain Java class name for object at {:p}"sv, +#endif + reinterpret_cast(klass) + ); return nullptr; } diff --git a/src/native/clr/host/os-bridge.cc b/src/native/clr/host/os-bridge.cc index 94f188791cf..4ad4b7514b0 100644 --- a/src/native/clr/host/os-bridge.cc +++ b/src/native/clr/host/os-bridge.cc @@ -4,6 +4,7 @@ #include #include #include +#include #include using namespace xamarin::android; @@ -80,13 +81,13 @@ auto OSBridge::_monodroid_gref_dec () noexcept -> int void OSBridge::_write_stack_trace (FILE *to, const char *const from, LogCategories category) noexcept { if (from == nullptr) [[unlikely]] { - log_warn (category, "Unable to write stack trace, managed runtime passed a NULL string."); + log_warn (category, "Unable to write stack trace, managed runtime passed a NULL string."sv); return; } const std::string_view trace { from }; if (trace.empty ()) [[unlikely]] { - log_warn (category, "Empty stack trace passed by the managed runtime."); + log_warn (category, "Empty stack trace passed by the managed runtime."sv); return; } @@ -167,8 +168,12 @@ auto OSBridge::_monodroid_gref_log_new (jobject curHandle, char curType, jobject return c; } - const std::string log_line = std::format ( + const std::string log_line = format_string ( +#if defined(XA_HOST_NATIVEAOT) + "+g+ grefc %d gwrefc %d obj-handle %p/%c -> new-handle %p/%c from thread '%s'(%d)", +#else "+g+ grefc {} gwrefc {} obj-handle {:p}/{} -> new-handle {:p}/{} from thread '{}'({})"sv, +#endif c, gc_weak_gref_count, reinterpret_cast(curHandle), @@ -190,8 +195,12 @@ void OSBridge::_monodroid_gref_log_delete (jobject handle, char type, const char return; } - const std::string log_line = std::format ( + const std::string log_line = format_string ( +#if defined(XA_HOST_NATIVEAOT) + "-g- grefc %d gwrefc %d handle %p/%c from thread '%s'(%d)", +#else "-g- grefc {} gwrefc {} handle {:p}/{} from thread '{}'({})"sv, +#endif c, gc_weak_gref_count, reinterpret_cast(handle), @@ -210,8 +219,12 @@ void OSBridge::_monodroid_weak_gref_new (jobject curHandle, char curType, jobjec return; } - const std::string log_line = std::format ( + const std::string log_line = format_string ( +#if defined(XA_HOST_NATIVEAOT) + "+w+ grefc %d gwrefc %d obj-handle %p/%c -> new-handle %p/%c from thread '%s'(%d)", +#else "+w+ grefc {} gwrefc {} obj-handle {:p}/{} -> new-handle {:p}/{} from thread '{}'({})"sv, +#endif gc_gref_count, gc_weak_gref_count, reinterpret_cast(curHandle), @@ -232,8 +245,12 @@ OSBridge::_monodroid_lref_log_new (int lrefc, jobject handle, char type, const c return; } - const std::string log_line = std::format ( + const std::string log_line = format_string ( +#if defined(XA_HOST_NATIVEAOT) + "+l+ lrefc %d handle %p/%c from thread '%s'(%d)", +#else "+l+ lrefc {} handle {:p}/{} from thread '{}'({})"sv, +#endif lrefc, reinterpret_cast(handle), type, @@ -251,8 +268,12 @@ void OSBridge::_monodroid_weak_gref_delete (jobject handle, char type, const cha return; } - const std::string log_line = std::format ( + const std::string log_line = format_string ( +#if defined(XA_HOST_NATIVEAOT) + "-w- grefc %d gwrefc %d handle %p/%c from thread '%s'(%d)", +#else "-w- grefc {} gwrefc {} handle {:p}/{} from thread '{}'({})"sv, +#endif gc_gref_count, gc_weak_gref_count, reinterpret_cast(handle), @@ -270,8 +291,12 @@ void OSBridge::_monodroid_lref_log_delete (int lrefc, jobject handle, char type, return; } - const std::string log_line = std::format ( + const std::string log_line = format_string ( +#if defined(XA_HOST_NATIVEAOT) + "-l- lrefc %d handle %p/%c from thread '%s'(%d)", +#else "-l- lrefc {} handle {:p}/{} from thread '{}'({})"sv, +#endif lrefc, reinterpret_cast(handle), type, diff --git a/src/native/clr/include/shared/format-helpers.hh b/src/native/clr/include/shared/format-helpers.hh new file mode 100644 index 00000000000..2da0f1c5af0 --- /dev/null +++ b/src/native/clr/include/shared/format-helpers.hh @@ -0,0 +1,72 @@ +#pragma once + +#include + +#if defined(XA_HOST_NATIVEAOT) +#include +#include +#include +#else +#include +#endif + +#include + +namespace xamarin::android { +#if defined(XA_HOST_NATIVEAOT) + [[gnu::always_inline]] + static inline constexpr std::string format_string (const char *format, ...) noexcept + { + constexpr size_t BufferSize = 256; // Hopefully enough for most uses, to prevent dynamic allocation + dynamic_local_string dest; + + // Duplicates some code from format_printf in strings.hh, but it's not something we can avoid without + // obscuring the code, due to pecularities of how stdargs work (va_list is in undefined state after + // returning from the v* functions taking it as a parameter instead of ...) + va_list args; + va_start (args, format); + int n = vsnprintf (dest.get (), dest.size (), format, args); + va_end (args); + + auto log_with_errno = [](const char *msg) { + // Logging with separate calls to avoid memory allocation, we might be OOM-ing + log_write (LOG_DEFAULT, LogLevel::Warn, msg); + log_write (LOG_DEFAULT, LogLevel::Warn, strerror (errno)); + }; + + if (n < 0) [[unlikely]] { + log_with_errno ("Failed to format string"); + return ""; + } + + auto res = static_cast(n); + if (res < dest.size ()) [[likely]] { + return std::string (dest.get (), dest.size ()); + } + + // resize_for_extra adds one more byte for the NUL character + dest.resize_for_extra (res - dest.size ()); + if (dest.size () <= res) { + log_write (LOG_DEFAULT, LogLevel::Warn, "Failed to format string, buffer resize failed."); + return ""; + } + + va_start (args, format); + n = vsnprintf (dest.get (), dest.size (), format, args); + va_end (args); + + if (n < 0) [[unlikely]] { + log_with_errno ("Failed to format string after resize"); + return ""; + } + + return std::string (dest.get (), dest.size ()); + } +#else + template [[gnu::always_inline]] + static inline constexpr std::string format_string (std::format_string fmt, Args&& ...args) noexcept + { + return std::format (fmt, std::forward(args)...).c_str (); + } +#endif +} From 9833fcf88d4d32e84f9dd2b879ce4cba3693789a Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 15 Jan 2026 13:54:52 +0100 Subject: [PATCH 17/39] NativeAOT sample links correctly now --- src-ThirdParty/llvm/libcxx/src/atomic.cpp | 207 ++++++++ src-ThirdParty/llvm/libcxx/src/chrono.cpp | 265 ++++++++++ src-ThirdParty/llvm/libcxx/src/hash.cpp | 452 ++++++++++++++++++ .../libcxx/src/include/apple_availability.h | 34 ++ .../clr/runtime-base/android-system-shared.cc | 31 +- src/native/clr/runtime-base/logger.cc | 26 +- src/native/nativeaot/cxx-abi/new_helpers.cc | 5 + src/native/nativeaot/host/CMakeLists.txt | 6 +- 8 files changed, 1020 insertions(+), 6 deletions(-) create mode 100644 src-ThirdParty/llvm/libcxx/src/atomic.cpp create mode 100644 src-ThirdParty/llvm/libcxx/src/chrono.cpp create mode 100644 src-ThirdParty/llvm/libcxx/src/hash.cpp create mode 100644 src-ThirdParty/llvm/libcxx/src/include/apple_availability.h create mode 100644 src/native/nativeaot/cxx-abi/new_helpers.cc diff --git a/src-ThirdParty/llvm/libcxx/src/atomic.cpp b/src-ThirdParty/llvm/libcxx/src/atomic.cpp new file mode 100644 index 00000000000..c1af8d6f95a --- /dev/null +++ b/src-ThirdParty/llvm/libcxx/src/atomic.cpp @@ -0,0 +1,207 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <__thread/timed_backoff_policy.h> +#include +#include +#include +#include + +#include "include/apple_availability.h" + +#ifdef __linux__ + +# include +# include +# include + +// libc++ uses SYS_futex as a universal syscall name. However, on 32 bit architectures +// with a 64 bit time_t, we need to specify SYS_futex_time64. +# if !defined(SYS_futex) && defined(SYS_futex_time64) +# define SYS_futex SYS_futex_time64 +# endif +# define _LIBCPP_FUTEX(...) syscall(SYS_futex, __VA_ARGS__) + +#elif defined(__FreeBSD__) + +# include +# include + +# define _LIBCPP_FUTEX(...) syscall(SYS_futex, __VA_ARGS__) + +#elif defined(__OpenBSD__) + +# include + +// OpenBSD has no indirect syscalls +# define _LIBCPP_FUTEX(...) futex(__VA_ARGS__) + +#else // <- Add other operating systems here + +// Baseline needs no new headers + +# define _LIBCPP_FUTEX(...) syscall(SYS_futex, __VA_ARGS__) + +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#ifdef __linux__ + +static void +__libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr, __cxx_contention_t __val) { + static constexpr timespec __timeout = {2, 0}; + _LIBCPP_FUTEX(__ptr, FUTEX_WAIT_PRIVATE, __val, &__timeout, 0, 0); +} + +static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile* __ptr, bool __notify_one) { + _LIBCPP_FUTEX(__ptr, FUTEX_WAKE_PRIVATE, __notify_one ? 1 : INT_MAX, 0, 0, 0); +} + +#elif defined(__APPLE__) && defined(_LIBCPP_USE_ULOCK) + +extern "C" int __ulock_wait( + uint32_t operation, void* addr, uint64_t value, uint32_t timeout); /* timeout is specified in microseconds */ +extern "C" int __ulock_wake(uint32_t operation, void* addr, uint64_t wake_value); + +// https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/bsd/sys/ulock.h#L82 +# define UL_COMPARE_AND_WAIT64 5 +# define ULF_WAKE_ALL 0x00000100 + +static void +__libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr, __cxx_contention_t __val) { + static_assert(sizeof(__cxx_atomic_contention_t) == 8, "Waiting on 8 bytes value"); + __ulock_wait(UL_COMPARE_AND_WAIT64, const_cast<__cxx_atomic_contention_t*>(__ptr), __val, 0); +} + +static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile* __ptr, bool __notify_one) { + static_assert(sizeof(__cxx_atomic_contention_t) == 8, "Waking up on 8 bytes value"); + __ulock_wake( + UL_COMPARE_AND_WAIT64 | (__notify_one ? 0 : ULF_WAKE_ALL), const_cast<__cxx_atomic_contention_t*>(__ptr), 0); +} + +#elif defined(__FreeBSD__) && __SIZEOF_LONG__ == 8 +/* + * Since __cxx_contention_t is int64_t even on 32bit FreeBSD + * platforms, we have to use umtx ops that work on the long type, and + * limit its use to architectures where long and int64_t are synonyms. + */ + +static void +__libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr, __cxx_contention_t __val) { + _umtx_op(const_cast<__cxx_atomic_contention_t*>(__ptr), UMTX_OP_WAIT, __val, nullptr, nullptr); +} + +static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile* __ptr, bool __notify_one) { + _umtx_op(const_cast<__cxx_atomic_contention_t*>(__ptr), UMTX_OP_WAKE, __notify_one ? 1 : INT_MAX, nullptr, nullptr); +} + +#else // <- Add other operating systems here + +// Baseline is just a timed backoff + +static void +__libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr, __cxx_contention_t __val) { + __libcpp_thread_poll_with_backoff( + [=]() -> bool { return !__cxx_nonatomic_compare_equal(__cxx_atomic_load(__ptr, memory_order_relaxed), __val); }, + __libcpp_timed_backoff_policy()); +} + +static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile*, bool) {} + +#endif // __linux__ + +static constexpr size_t __libcpp_contention_table_size = (1 << 8); /* < there's no magic in this number */ + +struct alignas(64) /* aim to avoid false sharing */ __libcpp_contention_table_entry { + __cxx_atomic_contention_t __contention_state; + __cxx_atomic_contention_t __platform_state; + inline constexpr __libcpp_contention_table_entry() : __contention_state(0), __platform_state(0) {} +}; + +static __libcpp_contention_table_entry __libcpp_contention_table[__libcpp_contention_table_size]; + +static hash __libcpp_contention_hasher; + +static __libcpp_contention_table_entry* __libcpp_contention_state(void const volatile* p) { + return &__libcpp_contention_table[__libcpp_contention_hasher(p) & (__libcpp_contention_table_size - 1)]; +} + +/* Given an atomic to track contention and an atomic to actually wait on, which may be + the same atomic, we try to detect contention to avoid spuriously calling the platform. */ + +static void __libcpp_contention_notify(__cxx_atomic_contention_t volatile* __contention_state, + __cxx_atomic_contention_t const volatile* __platform_state, + bool __notify_one) { + if (0 != __cxx_atomic_load(__contention_state, memory_order_seq_cst)) + // We only call 'wake' if we consumed a contention bit here. + __libcpp_platform_wake_by_address(__platform_state, __notify_one); +} +static __cxx_contention_t +__libcpp_contention_monitor_for_wait(__cxx_atomic_contention_t volatile* /*__contention_state*/, + __cxx_atomic_contention_t const volatile* __platform_state) { + // We will monitor this value. + return __cxx_atomic_load(__platform_state, memory_order_acquire); +} +static void __libcpp_contention_wait(__cxx_atomic_contention_t volatile* __contention_state, + __cxx_atomic_contention_t const volatile* __platform_state, + __cxx_contention_t __old_value) { + __cxx_atomic_fetch_add(__contention_state, __cxx_contention_t(1), memory_order_seq_cst); + // We sleep as long as the monitored value hasn't changed. + __libcpp_platform_wait_on_address(__platform_state, __old_value); + __cxx_atomic_fetch_sub(__contention_state, __cxx_contention_t(1), memory_order_release); +} + +/* When the incoming atomic is the wrong size for the platform wait size, need to + launder the value sequence through an atomic from our table. */ + +static void __libcpp_atomic_notify(void const volatile* __location) { + auto const __entry = __libcpp_contention_state(__location); + // The value sequence laundering happens on the next line below. + __cxx_atomic_fetch_add(&__entry->__platform_state, __cxx_contention_t(1), memory_order_release); + __libcpp_contention_notify( + &__entry->__contention_state, + &__entry->__platform_state, + false /* when laundering, we can't handle notify_one */); +} +_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_one(void const volatile* __location) noexcept { + __libcpp_atomic_notify(__location); +} +_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_all(void const volatile* __location) noexcept { + __libcpp_atomic_notify(__location); +} +_LIBCPP_EXPORTED_FROM_ABI __cxx_contention_t __libcpp_atomic_monitor(void const volatile* __location) noexcept { + auto const __entry = __libcpp_contention_state(__location); + return __libcpp_contention_monitor_for_wait(&__entry->__contention_state, &__entry->__platform_state); +} +_LIBCPP_EXPORTED_FROM_ABI void +__libcpp_atomic_wait(void const volatile* __location, __cxx_contention_t __old_value) noexcept { + auto const __entry = __libcpp_contention_state(__location); + __libcpp_contention_wait(&__entry->__contention_state, &__entry->__platform_state, __old_value); +} + +/* When the incoming atomic happens to be the platform wait size, we still need to use the + table for the contention detection, but we can use the atomic directly for the wait. */ + +_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_one(__cxx_atomic_contention_t const volatile* __location) noexcept { + __libcpp_contention_notify(&__libcpp_contention_state(__location)->__contention_state, __location, true); +} +_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_all(__cxx_atomic_contention_t const volatile* __location) noexcept { + __libcpp_contention_notify(&__libcpp_contention_state(__location)->__contention_state, __location, false); +} +// This function is never used, but still exported for ABI compatibility. +_LIBCPP_EXPORTED_FROM_ABI __cxx_contention_t +__libcpp_atomic_monitor(__cxx_atomic_contention_t const volatile* __location) noexcept { + return __libcpp_contention_monitor_for_wait(&__libcpp_contention_state(__location)->__contention_state, __location); +} +_LIBCPP_EXPORTED_FROM_ABI void +__libcpp_atomic_wait(__cxx_atomic_contention_t const volatile* __location, __cxx_contention_t __old_value) noexcept { + __libcpp_contention_wait(&__libcpp_contention_state(__location)->__contention_state, __location, __old_value); +} + +_LIBCPP_END_NAMESPACE_STD diff --git a/src-ThirdParty/llvm/libcxx/src/chrono.cpp b/src-ThirdParty/llvm/libcxx/src/chrono.cpp new file mode 100644 index 00000000000..dbc0f617b4f --- /dev/null +++ b/src-ThirdParty/llvm/libcxx/src/chrono.cpp @@ -0,0 +1,265 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#if defined(__MVS__) +// As part of monotonic clock support on z/OS we need macro _LARGE_TIME_API +// to be defined before any system header to include definition of struct timespec64. +# define _LARGE_TIME_API +#endif + +#include <__system_error/throw_system_error.h> +#include // errno +#include + +#if defined(__MVS__) +# include <__support/ibm/gettod_zos.h> // gettimeofdayMonotonic +#endif + +#include "include/apple_availability.h" +#include // clock_gettime and CLOCK_{MONOTONIC,REALTIME,MONOTONIC_RAW} + +#if __has_include() +# include // _POSIX_TIMERS +#endif + +#if __has_include() +# include // for gettimeofday and timeval +#endif + +#if defined(__LLVM_LIBC__) +# define _LIBCPP_HAS_TIMESPEC_GET +#endif + +// OpenBSD and GPU do not have a fully conformant suite of POSIX timers, but +// it does have clock_gettime and CLOCK_MONOTONIC which is all we need. +#if defined(__APPLE__) || defined(__gnu_hurd__) || defined(__OpenBSD__) || defined(__AMDGPU__) || \ + defined(__NVPTX__) || (defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0) +# define _LIBCPP_HAS_CLOCK_GETTIME +#endif + +#if defined(_LIBCPP_WIN32API) +# define WIN32_LEAN_AND_MEAN +# define VC_EXTRA_LEAN +# include +# if _WIN32_WINNT >= _WIN32_WINNT_WIN8 +# include +# endif +#endif // defined(_LIBCPP_WIN32API) + +#if defined(__Fuchsia__) +# include +#endif + +#if __has_include() +# include +#endif + +#if defined(__ELF__) && defined(_LIBCPP_LINK_RT_LIB) +# pragma comment(lib, "rt") +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace chrono { + +// +// system_clock +// + +#if defined(_LIBCPP_WIN32API) + +# if _WIN32_WINNT < _WIN32_WINNT_WIN8 + +namespace { + +typedef void(WINAPI* GetSystemTimeAsFileTimePtr)(LPFILETIME); + +class GetSystemTimeInit { +public: + GetSystemTimeInit() { + fp = (GetSystemTimeAsFileTimePtr)(void*)GetProcAddress( + GetModuleHandleW(L"kernel32.dll"), "GetSystemTimePreciseAsFileTime"); + if (fp == nullptr) + fp = GetSystemTimeAsFileTime; + } + GetSystemTimeAsFileTimePtr fp; +}; + +// Pretend we're inside a system header so the compiler doesn't flag the use of the init_priority +// attribute with a value that's reserved for the implementation (we're the implementation). +# include "chrono_system_time_init.h" +} // namespace + +# endif + +static system_clock::time_point __libcpp_system_clock_now() { + // FILETIME is in 100ns units + using filetime_duration = + std::chrono::duration<__int64, std::ratio_multiply, nanoseconds::period>>; + + // The Windows epoch is Jan 1 1601, the Unix epoch Jan 1 1970. + static constexpr const seconds nt_to_unix_epoch{11644473600}; + + FILETIME ft; +# if (_WIN32_WINNT >= _WIN32_WINNT_WIN8 && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) || \ + (_WIN32_WINNT >= _WIN32_WINNT_WIN10) + GetSystemTimePreciseAsFileTime(&ft); +# elif !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + GetSystemTimeAsFileTime(&ft); +# else + GetSystemTimeAsFileTimeFunc.fp(&ft); +# endif + + filetime_duration d{(static_cast<__int64>(ft.dwHighDateTime) << 32) | static_cast<__int64>(ft.dwLowDateTime)}; + return system_clock::time_point(duration_cast(d - nt_to_unix_epoch)); +} + +#elif defined(_LIBCPP_HAS_TIMESPEC_GET) + +static system_clock::time_point __libcpp_system_clock_now() { + struct timespec ts; + if (timespec_get(&ts, TIME_UTC) != TIME_UTC) + __throw_system_error(errno, "timespec_get(TIME_UTC) failed"); + return system_clock::time_point(seconds(ts.tv_sec) + microseconds(ts.tv_nsec / 1000)); +} + +#elif defined(_LIBCPP_HAS_CLOCK_GETTIME) + +static system_clock::time_point __libcpp_system_clock_now() { + struct timespec tp; + if (0 != clock_gettime(CLOCK_REALTIME, &tp)) + __throw_system_error(errno, "clock_gettime(CLOCK_REALTIME) failed"); + return system_clock::time_point(seconds(tp.tv_sec) + microseconds(tp.tv_nsec / 1000)); +} + +#else + +static system_clock::time_point __libcpp_system_clock_now() { + timeval tv; + gettimeofday(&tv, 0); + return system_clock::time_point(seconds(tv.tv_sec) + microseconds(tv.tv_usec)); +} + +#endif + +_LIBCPP_DIAGNOSTIC_PUSH +_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wdeprecated") +const bool system_clock::is_steady; +_LIBCPP_DIAGNOSTIC_POP + +system_clock::time_point system_clock::now() noexcept { return __libcpp_system_clock_now(); } + +time_t system_clock::to_time_t(const time_point& t) noexcept { + return time_t(duration_cast(t.time_since_epoch()).count()); +} + +system_clock::time_point system_clock::from_time_t(time_t t) noexcept { return system_clock::time_point(seconds(t)); } + +// +// steady_clock +// +// Warning: If this is not truly steady, then it is non-conforming. It is +// better for it to not exist and have the rest of libc++ use system_clock +// instead. +// + +#if _LIBCPP_HAS_MONOTONIC_CLOCK + +# if defined(__APPLE__) + +// On Apple platforms, only CLOCK_UPTIME_RAW, CLOCK_MONOTONIC_RAW or +// mach_absolute_time are able to time functions in the nanosecond range. +// Furthermore, only CLOCK_MONOTONIC_RAW is truly monotonic, because it +// also counts cycles when the system is asleep. Thus, it is the only +// acceptable implementation of steady_clock. +static steady_clock::time_point __libcpp_steady_clock_now() { + struct timespec tp; + if (0 != clock_gettime(CLOCK_MONOTONIC_RAW, &tp)) + __throw_system_error(errno, "clock_gettime(CLOCK_MONOTONIC_RAW) failed"); + return steady_clock::time_point(seconds(tp.tv_sec) + nanoseconds(tp.tv_nsec)); +} + +# elif defined(_LIBCPP_WIN32API) + +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms644905(v=vs.85).aspx says: +// If the function fails, the return value is zero. +// On systems that run Windows XP or later, the function will always succeed +// and will thus never return zero. + +static LARGE_INTEGER __QueryPerformanceFrequency() { + LARGE_INTEGER val; + (void)QueryPerformanceFrequency(&val); + return val; +} + +static steady_clock::time_point __libcpp_steady_clock_now() { + static const LARGE_INTEGER freq = __QueryPerformanceFrequency(); + + LARGE_INTEGER counter; + (void)QueryPerformanceCounter(&counter); + auto seconds = counter.QuadPart / freq.QuadPart; + auto fractions = counter.QuadPart % freq.QuadPart; + auto dur = seconds * nano::den + fractions * nano::den / freq.QuadPart; + return steady_clock::time_point(steady_clock::duration(dur)); +} + +# elif defined(__MVS__) + +static steady_clock::time_point __libcpp_steady_clock_now() { + struct timespec64 ts; + if (0 != gettimeofdayMonotonic(&ts)) + __throw_system_error(errno, "failed to obtain time of day"); + + return steady_clock::time_point(seconds(ts.tv_sec) + nanoseconds(ts.tv_nsec)); +} + +# elif defined(__Fuchsia__) + +static steady_clock::time_point __libcpp_steady_clock_now() noexcept { + // Implicitly link against the vDSO system call ABI without + // requiring the final link to specify -lzircon explicitly when + // statically linking libc++. +# pragma comment(lib, "zircon") + + return steady_clock::time_point(nanoseconds(_zx_clock_get_monotonic())); +} + +# elif defined(_LIBCPP_HAS_TIMESPEC_GET) + +static steady_clock::time_point __libcpp_steady_clock_now() { + struct timespec ts; + if (timespec_get(&ts, TIME_MONOTONIC) != TIME_MONOTONIC) + __throw_system_error(errno, "timespec_get(TIME_MONOTONIC) failed"); + return steady_clock::time_point(seconds(ts.tv_sec) + microseconds(ts.tv_nsec / 1000)); +} + +# elif defined(_LIBCPP_HAS_CLOCK_GETTIME) + +static steady_clock::time_point __libcpp_steady_clock_now() { + struct timespec tp; + if (0 != clock_gettime(CLOCK_MONOTONIC, &tp)) + __throw_system_error(errno, "clock_gettime(CLOCK_MONOTONIC) failed"); + return steady_clock::time_point(seconds(tp.tv_sec) + nanoseconds(tp.tv_nsec)); +} + +# else +# error "Monotonic clock not implemented on this platform" +# endif + +_LIBCPP_DIAGNOSTIC_PUSH +_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wdeprecated") +const bool steady_clock::is_steady; +_LIBCPP_DIAGNOSTIC_POP + +steady_clock::time_point steady_clock::now() noexcept { return __libcpp_steady_clock_now(); } + +#endif // _LIBCPP_HAS_MONOTONIC_CLOCK + +} // namespace chrono + +_LIBCPP_END_NAMESPACE_STD diff --git a/src-ThirdParty/llvm/libcxx/src/hash.cpp b/src-ThirdParty/llvm/libcxx/src/hash.cpp new file mode 100644 index 00000000000..34b02b8eafc --- /dev/null +++ b/src-ThirdParty/llvm/libcxx/src/hash.cpp @@ -0,0 +1,452 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <__hash_table> +#include +#include +#include + +_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wtautological-constant-out-of-range-compare") + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace { + +// handle all next_prime(i) for i in [1, 210), special case 0 +const unsigned small_primes[] = { + 0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, + 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, + 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211}; + +// potential primes = 210*k + indices[i], k >= 1 +// these numbers are not divisible by 2, 3, 5 or 7 +// (or any integer 2 <= j <= 10 for that matter). +const unsigned indices[] = { + 1, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, + 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 121, 127, 131, 137, 139, + 143, 149, 151, 157, 163, 167, 169, 173, 179, 181, 187, 191, 193, 197, 199, 209}; + +} // namespace + +// Returns: If n == 0, returns 0. Else returns the lowest prime number that +// is greater than or equal to n. +// +// The algorithm creates a list of small primes, plus an open-ended list of +// potential primes. All prime numbers are potential prime numbers. However +// some potential prime numbers are not prime. In an ideal world, all potential +// prime numbers would be prime. Candidate prime numbers are chosen as the next +// highest potential prime. Then this number is tested for prime by dividing it +// by all potential prime numbers less than the sqrt of the candidate. +// +// This implementation defines potential primes as those numbers not divisible +// by 2, 3, 5, and 7. Other (common) implementations define potential primes +// as those not divisible by 2. A few other implementations define potential +// primes as those not divisible by 2 or 3. By raising the number of small +// primes which the potential prime is not divisible by, the set of potential +// primes more closely approximates the set of prime numbers. And thus there +// are fewer potential primes to search, and fewer potential primes to divide +// against. + +template +inline _LIBCPP_HIDE_FROM_ABI typename enable_if<_Sz == 4, void>::type __check_for_overflow(size_t N) { + if (N > 0xFFFFFFFB) + __throw_overflow_error("__next_prime overflow"); +} + +template +inline _LIBCPP_HIDE_FROM_ABI typename enable_if<_Sz == 8, void>::type __check_for_overflow(size_t N) { + if (N > 0xFFFFFFFFFFFFFFC5ull) + __throw_overflow_error("__next_prime overflow"); +} + +size_t __next_prime(size_t n) { + const size_t L = 210; + const size_t N = sizeof(small_primes) / sizeof(small_primes[0]); + // If n is small enough, search in small_primes + if (n <= small_primes[N - 1]) + return *std::lower_bound(small_primes, small_primes + N, n); + // Else n > largest small_primes + // Check for overflow + __check_for_overflow(n); + // Start searching list of potential primes: L * k0 + indices[in] + const size_t M = sizeof(indices) / sizeof(indices[0]); + // Select first potential prime >= n + // Known a-priori n >= L + size_t k0 = n / L; + size_t in = static_cast(std::lower_bound(indices, indices + M, n - k0 * L) - indices); + n = L * k0 + indices[in]; + while (true) { + // Divide n by all primes or potential primes (i) until: + // 1. The division is even, so try next potential prime. + // 2. The i > sqrt(n), in which case n is prime. + // It is known a-priori that n is not divisible by 2, 3, 5 or 7, + // so don't test those (j == 5 -> divide by 11 first). And the + // potential primes start with 211, so don't test against the last + // small prime. + for (size_t j = 5; j < N - 1; ++j) { + const std::size_t p = small_primes[j]; + const std::size_t q = n / p; + if (q < p) + return n; + if (n == q * p) + goto next; + } + // n wasn't divisible by small primes, try potential primes + { + size_t i = 211; + while (true) { + std::size_t q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 10; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 8; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 8; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 6; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 4; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 2; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + i += 10; + q = n / i; + if (q < i) + return n; + if (n == q * i) + break; + + // This will loop i to the next "plane" of potential primes + i += 2; + } + } + next: + // n is not prime. Increment n to next potential prime. + if (++in == M) { + ++k0; + in = 0; + } + n = L * k0 + indices[in]; + } +} + +_LIBCPP_END_NAMESPACE_STD diff --git a/src-ThirdParty/llvm/libcxx/src/include/apple_availability.h b/src-ThirdParty/llvm/libcxx/src/include/apple_availability.h new file mode 100644 index 00000000000..fc2ad150654 --- /dev/null +++ b/src-ThirdParty/llvm/libcxx/src/include/apple_availability.h @@ -0,0 +1,34 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP_SRC_INCLUDE_APPLE_AVAILABILITY_H +#define _LIBCPP_SRC_INCLUDE_APPLE_AVAILABILITY_H + +#if defined(__APPLE__) + +# if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) +# if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101500 +# define _LIBCPP_USE_ULOCK +# endif +# elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) +# if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 130000 +# define _LIBCPP_USE_ULOCK +# endif +# elif defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) +# if __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ >= 130000 +# define _LIBCPP_USE_ULOCK +# endif +# elif defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) +# if __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ >= 60000 +# define _LIBCPP_USE_ULOCK +# endif +# endif // __ENVIRONMENT_.*_VERSION_MIN_REQUIRED__ + +#endif // __APPLE__ + +#endif // _LIBCPP_SRC_INCLUDE_APPLE_AVAILABILITY_H diff --git a/src/native/clr/runtime-base/android-system-shared.cc b/src/native/clr/runtime-base/android-system-shared.cc index 2aa1d54b001..4a054d82485 100644 --- a/src/native/clr/runtime-base/android-system-shared.cc +++ b/src/native/clr/runtime-base/android-system-shared.cc @@ -36,7 +36,15 @@ AndroidSystem::monodroid__system_property_get (std::string_view const& name, cha char *buf = nullptr; if (sp_value_len < Constants::PROPERTY_VALUE_BUFFER_LEN) { size_t alloc_size = Helpers::add_with_overflow_check (Constants::PROPERTY_VALUE_BUFFER_LEN, 1uz); - log_warn (LOG_DEFAULT, "Buffer to store system property may be too small, will copy only {} bytes", sp_value_len); + log_warn ( + LOG_DEFAULT, +#if defined(XA_HOST_NATIVEAOT) + "Buffer to store system property may be too small, will copy only %z bytes", +#else + "Buffer to store system property may be too small, will copy only {} bytes"sv, +#endif + sp_value_len + ); buf = new char [alloc_size]; } @@ -81,10 +89,27 @@ AndroidSystem::get_max_gref_count_from_system () noexcept -> long } if (*e) { - log_warn (LOG_GC, "Unsupported '{}' value '{}'.", Constants::DEBUG_MONO_MAX_GREFC.data (), override.get ()); + log_warn ( + LOG_GC, +#if defined(XA_HOST_NATIVEAOT) + "Unsupported '%s' value '%s'.", +#else + "Unsupported '{}' value '{}'."sv, +#endif + Constants::DEBUG_MONO_MAX_GREFC.data (), + override.get () + ); } - log_warn (LOG_GC, "Overriding max JNI Global Reference count to {}", max); + log_warn ( + LOG_GC, +#if defined(XA_HOST_NATIVEAOT) + "Overriding max JNI Global Reference count to %lu", +#else + "Overriding max JNI Global Reference count to {}"sv, +#endif + max + ); } return max; diff --git a/src/native/clr/runtime-base/logger.cc b/src/native/clr/runtime-base/logger.cc index a61b68d4a76..4da1280ebee 100644 --- a/src/native/clr/runtime-base/logger.cc +++ b/src/native/clr/runtime-base/logger.cc @@ -52,7 +52,16 @@ auto Logger::open_file (LogCategories category, std::string_view const& custom_p { auto log_and_return = [&category](FILE *f, std::string_view const& path) -> FILE* { if (f != nullptr) { - log_debug (category, "Opened file '{}' for logging.", path); + log_debug ( + category, +#if defined(XA_HOST_NATIVEAOT) + "Opened file '%s' for logging.", + path.data () +#else + "Opened file '{}' for logging."sv, + path +#endif + ); } return f; }; @@ -162,7 +171,20 @@ Logger::init_logging_categories () noexcept auto file_name = segment.at (offset); if (!file_name.has_value ()) { - log_warn (LOG_DEFAULT, "Unable to set path to {} log file: {}", file_kind, to_string (file_name.error ())); + const std::string_view file_name_str = to_string (file_name.error ()); + + log_warn ( + LOG_DEFAULT, +#if defined(XA_HOST_NATIVEAOT) + "Unable to set path to %s log file: %s", + file_kind.data (), + file_name_str.data () +#else + "Unable to set path to {} log file: {}"sv, + file_kind, + file_name_str +#endif + ); return nullptr; } diff --git a/src/native/nativeaot/cxx-abi/new_helpers.cc b/src/native/nativeaot/cxx-abi/new_helpers.cc new file mode 100644 index 00000000000..9107dbf27b4 --- /dev/null +++ b/src/native/nativeaot/cxx-abi/new_helpers.cc @@ -0,0 +1,5 @@ +#include + +namespace std { // purposefully not versioned + const nothrow_t nothrow{}; +} diff --git a/src/native/nativeaot/host/CMakeLists.txt b/src/native/nativeaot/host/CMakeLists.txt index 4056101b460..19a10d3aee0 100644 --- a/src/native/nativeaot/host/CMakeLists.txt +++ b/src/native/nativeaot/host/CMakeLists.txt @@ -42,13 +42,16 @@ set(XAMARIN_MONODROID_SOURCES ../runtime-base/android-system.cc # Bionic sources - ${BIONIC_SOURCE_DIR}//cxa_guard.cc + ${BIONIC_SOURCE_DIR}/cxa_guard.cc # libc++ sources + ${LIBCXX_SOURCE_DIR}/atomic.cpp + ${LIBCXX_SOURCE_DIR}/chrono.cpp ${LIBCXX_SOURCE_DIR}/condition_variable.cpp ${LIBCXX_SOURCE_DIR}/condition_variable_destructor.cpp ${LIBCXX_SOURCE_DIR}/error_category.cpp ${LIBCXX_SOURCE_DIR}/future.cpp + ${LIBCXX_SOURCE_DIR}/hash.cpp ${LIBCXX_SOURCE_DIR}/memory.cpp ${LIBCXX_SOURCE_DIR}/mutex.cpp ${LIBCXX_SOURCE_DIR}/mutex_destructor.cpp @@ -62,6 +65,7 @@ set(XAMARIN_MONODROID_SOURCES # Local versions of C++ ABI sources ${LOCAL_CXXABI_SOURCE_DIR}/cxa_virtual.cc ${LOCAL_CXXABI_SOURCE_DIR}/new.cc + ${LOCAL_CXXABI_SOURCE_DIR}/new_helpers.cc ${LOCAL_CXXABI_SOURCE_DIR}/no_exceptions.cc ${LOCAL_CXXABI_SOURCE_DIR}/stdexcept.cc ${LOCAL_CXXABI_SOURCE_DIR}/string.cc From d6a0d53e077d63b36deee168b6ad9f69fa852766 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Fri, 16 Jan 2026 17:35:24 +0100 Subject: [PATCH 18/39] Beginnings of an LLVM sources update utility --- build-tools/scripts/LlvmUpdateInfo.cs.in | 9 + .../xaprepare/ConfigAndData/Configurables.cs | 1 + .../xaprepare/Scenarios/Scenario_Standard.cs | 1 + .../Steps/Step_Generate_LLVM_UpdateInfo.cs | 160 ++++++++++++++++++ tools/update-llvm-source/Program.cs | 11 ++ .../update-llvm-source.csproj | 21 +++ 6 files changed, 203 insertions(+) create mode 100644 build-tools/scripts/LlvmUpdateInfo.cs.in create mode 100644 build-tools/xaprepare/xaprepare/Steps/Step_Generate_LLVM_UpdateInfo.cs create mode 100644 tools/update-llvm-source/Program.cs create mode 100644 tools/update-llvm-source/update-llvm-source.csproj diff --git a/build-tools/scripts/LlvmUpdateInfo.cs.in b/build-tools/scripts/LlvmUpdateInfo.cs.in new file mode 100644 index 00000000000..ca9bbd3d01b --- /dev/null +++ b/build-tools/scripts/LlvmUpdateInfo.cs.in @@ -0,0 +1,9 @@ +using System; + +namespace Xamarin.Android.Tools; + +static class LlvmUpdateInfo +{ + public const string Revision = "@LLVM_PROJECT_REVISION@"; + public static readonly Uri BaseUrl = new Uri ("@LLVM_PROJECT_BASE_URL@"); +} diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs index 3acf50d5774..67184ae5bbe 100644 --- a/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs @@ -36,6 +36,7 @@ public static partial class Urls public static readonly Uri AndroidToolchain_AndroidUri = new Uri ("https://dl.google.com/android/repository/"); public static Uri BinutilsArchive = new Uri ($"https://github.com/dotnet/android-native-tools/releases/download/{BinutilsVersion}/xamarin-android-toolchain-{BinutilsVersion}.7z"); + public static Uri GoogleSourcesBase = new Uri ("https://android.googlesource.com"); } public static partial class Defaults diff --git a/build-tools/xaprepare/xaprepare/Scenarios/Scenario_Standard.cs b/build-tools/xaprepare/xaprepare/Scenarios/Scenario_Standard.cs index 1f98ecadf3f..c6a5e7cffdc 100644 --- a/build-tools/xaprepare/xaprepare/Scenarios/Scenario_Standard.cs +++ b/build-tools/xaprepare/xaprepare/Scenarios/Scenario_Standard.cs @@ -21,6 +21,7 @@ protected override void AddSteps (Context context) Steps.Add (new Step_InstallDotNetPreview ()); Steps.Add (new Step_InstallMicrosoftOpenJDK ()); Steps.Add (new Step_Android_SDK_NDK ()); + Steps.Add (new Step_Generate_LLVM_UpdateInfo ()); Steps.Add (new Step_GenerateFiles (atBuildStart: true)); Steps.Add (new Step_PrepareProps ()); Steps.Add (new Step_InstallGNUBinutils ()); diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_Generate_LLVM_UpdateInfo.cs b/build-tools/xaprepare/xaprepare/Steps/Step_Generate_LLVM_UpdateInfo.cs new file mode 100644 index 00000000000..a5ec049e2e3 --- /dev/null +++ b/build-tools/xaprepare/xaprepare/Steps/Step_Generate_LLVM_UpdateInfo.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using System.Xml; + +namespace Xamarin.Android.Prepare; + +class Step_Generate_LLVM_UpdateInfo : Step +{ + static ReadOnlySpan Utf8Bom => new byte[] { 0xEF, 0xBB, 0xBF }; + static readonly byte[] BidPropertyName = Encoding.UTF8.GetBytes ("bid"); + + public Step_Generate_LLVM_UpdateInfo () + : base ("Generating LLVM source update information") + {} + +#pragma warning disable CS1998 + protected override async Task Execute (Context context) + { + try { + if (!Generate (context)) { + Log.WarningLine ("Failed to generate LLVM update info. Attempt to update LLVM sources may fail."); + } + } catch (Exception ex) { + Log.WarningLine ($"Failed to generate LLVM update info. {ex.Message}"); + Log.DebugLine ($"Exception was thrown while generating LLVM update info."); + Log.DebugLine (ex.ToString ()); + } + + // This step isn't critical, we never fail. + return true; + } +#pragma warning restore CS1998 + + bool Generate (Context context) + { + // BUILD_INFO is a JSON document with build information, we need the "bid" component from there as it forms + // part of the toolchain manifest name + string? bid = GetBid (Path.Combine (Configurables.Paths.AndroidToolchainRootDirectory, "BUILD_INFO")); + if (String.IsNullOrEmpty (bid)) { + Log.DebugLine ("Unable to find LLVM toolchain bid information."); + return false; + } + + // Manifest contains GIT revisions of various NDK components. We need the LLVM project's one from there. + string toolchainManifestPath = Path.Combine (Configurables.Paths.AndroidToolchainRootDirectory, $"manifest_{bid}.xml"); + (string? llvmProjectPath, string? llvmProjectRevision) = GetLlvmProjectInfo (toolchainManifestPath); + + if (String.IsNullOrEmpty (llvmProjectPath)) { + Log.DebugLine ("Failed to read LLVM project path from the manifest."); + return false; + } + + if (String.IsNullOrEmpty (llvmProjectRevision)) { + Log.DebugLine ("Failed to read LLVM project GIT revision from the manifest."); + return false; + } + + Log.InfoLine ("LLVM project path: ", llvmProjectPath); + Log.InfoLine ("LLVM project revision: ", llvmProjectRevision); + + // Manifest uses https://googleplex-android.googlesource.com/ which is not accessible for mere mortals, + // therefore we need to use the public URL + var baseURIBuilder = new UriBuilder (Configurables.Urls.GoogleSourcesBase); + baseURIBuilder.Path = $"{llvmProjectPath}/+/{llvmProjectRevision}"; + Uri baseURI = baseURIBuilder.Uri; + + const string updateSourcesInputName = "LlvmUpdateInfo.cs.in"; + string updateInfoSourceInputPath = Path.Combine (Configurables.Paths.BuildToolsScriptsDir, updateSourcesInputName); + string updateInfoSourceOutputPath = Path.Combine (Configurables.Paths.BuildBinDir, Path.GetFileNameWithoutExtension (updateSourcesInputName)); + + Log.InfoLine ($"Generating LLVM update info sources."); + var updateInfoSource = new GeneratedPlaceholdersFile ( + new Dictionary (StringComparer.Ordinal) { + { "@LLVM_PROJECT_REVISION@", llvmProjectRevision }, + { "@LLVM_PROJECT_BASE_URL@", baseURI.ToString () }, + }, + updateInfoSourceInputPath, + updateInfoSourceOutputPath + ); + updateInfoSource.Generate (context); + + return true; + } + + (string? path, string? revision) GetLlvmProjectInfo (string manifestPath) + { + Log.DebugLine ($"Reading LLVM toolchain manifest from '{manifestPath}'"); + + if (!File.Exists (manifestPath)) { + Log.DebugLine ($"NDK LLVM manifest '{manifestPath}' not found"); + return (null, null); + } + + var readerSettings = new XmlReaderSettings { + ValidationType = ValidationType.None, + DtdProcessing = DtdProcessing.Ignore, + IgnoreWhitespace = true, + IgnoreComments = true, + IgnoreProcessingInstructions = true, + }; + using var reader = XmlReader.Create (manifestPath, readerSettings); + var doc = new XmlDocument (); + doc.Load (reader); + + XmlNode? llvmToolchain = doc.SelectSingleNode ("//manifest/project[@name='toolchain/llvm-project']"); + if (llvmToolchain == null) { + Log.DebugLine ("Failed to find LLVM toolchain info in the manifest."); + return (null, null); + } + + if (llvmToolchain.Attributes == null) { + Log.DebugLine ("Unable to read path and revision info about the LLVM toolchain, no attributes on the element."); + return (null, null); + } + + XmlAttribute? path = llvmToolchain.Attributes["path"]; + XmlAttribute? revision = llvmToolchain.Attributes["revision"]; + + return (path?.Value, revision?.Value); + } + + string? GetBid (string buildInfoPath) + { + Log.DebugLine ($"Reading LLVM toolchain build info from '{buildInfoPath}'"); + + ReadOnlySpan manifestBytes = File.ReadAllBytes (buildInfoPath); + + if (manifestBytes.StartsWith (Utf8Bom)) { + manifestBytes = manifestBytes.Slice (Utf8Bom.Length); + } + + string? bid = null; + var reader = new Utf8JsonReader (manifestBytes); + while (reader.Read ()) { + if (reader.TokenType != JsonTokenType.PropertyName) { + continue; + } + + if (!reader.ValueTextEquals (BidPropertyName)) { + continue; + } + + // let's assume the manifest document is formatted correctly + reader.Read (); + if (reader.TokenType != JsonTokenType.String) { + Log.DebugLine ($"Invalid token type '{reader.TokenType}' for the 'bid' property in LLVM manifest."); + return null; + } + + bid = reader.GetString (); + break; + } + + return bid; + } +} diff --git a/tools/update-llvm-source/Program.cs b/tools/update-llvm-source/Program.cs new file mode 100644 index 00000000000..f169bacb80e --- /dev/null +++ b/tools/update-llvm-source/Program.cs @@ -0,0 +1,11 @@ +namespace Xamarin.Android.Tools; + +class App +{ + // URLs finally look like: https://android.googlesource.com/toolchain/llvm-project/+/5e96669f06077099aa41290cdb4c5e6fa0f59349/libcxx/src/hash.cpp?format=TEXT + // Returned text is base64-encoded + static int Main () + { + return 0; + } +} diff --git a/tools/update-llvm-source/update-llvm-source.csproj b/tools/update-llvm-source/update-llvm-source.csproj new file mode 100644 index 00000000000..7ed2b48bdf5 --- /dev/null +++ b/tools/update-llvm-source/update-llvm-source.csproj @@ -0,0 +1,21 @@ + + + + + Microsoft Corporation + 2026 Microsoft Corporation + 0.0.1 + false + ../../bin/$(Configuration)/bin/update-llvm-source + Exe + $(DotNetStableTargetFramework) + Xamarin.Android.Tools + disable + enable + Major + + + + + + From f939ce66d43e65f7e16e1cfca345164ba6b33d5c Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Mon, 19 Jan 2026 14:08:07 +0100 Subject: [PATCH 19/39] Stage --- build-tools/scripts/LlvmUpdateInfo.cs.in | 1 + .../Steps/Step_Generate_LLVM_UpdateInfo.cs | 27 ++- tools/update-llvm-source/Program.cs | 201 +++++++++++++++++- .../update-llvm-source.csproj | 3 +- 4 files changed, 229 insertions(+), 3 deletions(-) diff --git a/build-tools/scripts/LlvmUpdateInfo.cs.in b/build-tools/scripts/LlvmUpdateInfo.cs.in index ca9bbd3d01b..cb9df2dcfcb 100644 --- a/build-tools/scripts/LlvmUpdateInfo.cs.in +++ b/build-tools/scripts/LlvmUpdateInfo.cs.in @@ -5,5 +5,6 @@ namespace Xamarin.Android.Tools; static class LlvmUpdateInfo { public const string Revision = "@LLVM_PROJECT_REVISION@"; + public const string Version = "@LLVM_PROJECT_VERSION@"; public static readonly Uri BaseUrl = new Uri ("@LLVM_PROJECT_BASE_URL@"); } diff --git a/build-tools/xaprepare/xaprepare/Steps/Step_Generate_LLVM_UpdateInfo.cs b/build-tools/xaprepare/xaprepare/Steps/Step_Generate_LLVM_UpdateInfo.cs index a5ec049e2e3..94e89a6c29c 100644 --- a/build-tools/xaprepare/xaprepare/Steps/Step_Generate_LLVM_UpdateInfo.cs +++ b/build-tools/xaprepare/xaprepare/Steps/Step_Generate_LLVM_UpdateInfo.cs @@ -59,8 +59,31 @@ bool Generate (Context context) return false; } + string? llvmProjectVersion = null; + string androidVersionPath = Path.Combine (Configurables.Paths.AndroidToolchainRootDirectory, "AndroidVersion.txt"); + if (Path.Exists (androidVersionPath)) { + try { + foreach (string line in File.ReadLines (androidVersionPath)) { + // In NDK r29 LLVM version was on the first line + llvmProjectVersion = line.Trim (); + break; + } + } catch (Exception ex) { + Log.DebugLine ($"Failed to read LLVM Android version file '{androidVersionPath}'"); + Log.DebugLine ("Exception was thrown:"); + Log.DebugLine (ex.ToString ()); + } + } else { + Log.WarningLine ($"LLVM Android version file not found at {androidVersionPath}"); + } + + if (String.IsNullOrEmpty (llvmProjectVersion)) { + llvmProjectVersion = ""; + } + Log.InfoLine ("LLVM project path: ", llvmProjectPath); Log.InfoLine ("LLVM project revision: ", llvmProjectRevision); + Log.InfoLine ("LLVM project version: ", llvmProjectVersion); // Manifest uses https://googleplex-android.googlesource.com/ which is not accessible for mere mortals, // therefore we need to use the public URL @@ -72,11 +95,13 @@ bool Generate (Context context) string updateInfoSourceInputPath = Path.Combine (Configurables.Paths.BuildToolsScriptsDir, updateSourcesInputName); string updateInfoSourceOutputPath = Path.Combine (Configurables.Paths.BuildBinDir, Path.GetFileNameWithoutExtension (updateSourcesInputName)); + Log.InfoLine (); Log.InfoLine ($"Generating LLVM update info sources."); var updateInfoSource = new GeneratedPlaceholdersFile ( new Dictionary (StringComparer.Ordinal) { - { "@LLVM_PROJECT_REVISION@", llvmProjectRevision }, { "@LLVM_PROJECT_BASE_URL@", baseURI.ToString () }, + { "@LLVM_PROJECT_REVISION@", llvmProjectRevision }, + { "@LLVM_PROJECT_VERSION@", llvmProjectVersion }, }, updateInfoSourceInputPath, updateInfoSourceOutputPath diff --git a/tools/update-llvm-source/Program.cs b/tools/update-llvm-source/Program.cs index f169bacb80e..3becb614f7f 100644 --- a/tools/update-llvm-source/Program.cs +++ b/tools/update-llvm-source/Program.cs @@ -1,11 +1,210 @@ +using System; +using System.IO; +using System.Net.Http; +using System.Net; +using System.Security.Cryptography.X509Certificates; +using System.Security.Cryptography; +using System.Text; +using System.Threading; + namespace Xamarin.Android.Tools; class App { + static readonly string LocalSourcesPath = Path.Combine ("..", "..", "src-ThirdParty", "llvm"); + static readonly TimeSpan ExceptionRetryInitialDelay = TimeSpan.FromSeconds (30); + static readonly TimeSpan WebRequestTimeout = TimeSpan.FromMinutes (60); + static readonly int ExceptionRetries = 5; + // URLs finally look like: https://android.googlesource.com/toolchain/llvm-project/+/5e96669f06077099aa41290cdb4c5e6fa0f59349/libcxx/src/hash.cpp?format=TEXT // Returned text is base64-encoded static int Main () { - return 0; + try { + Console.WriteLine ("Updating LLVM sources for NativeAOT runtime."); + Console.WriteLine (); + Console.WriteLine ($"NDK release: {XABuildConfig.NDKRelease}"); + Console.WriteLine ($"NDK revision: {XABuildConfig.NDKRevision}"); + Console.WriteLine ($"LLVM version: {LlvmUpdateInfo.Version}"); + Console.WriteLine ($"LLVM GIT revision: {LlvmUpdateInfo.Revision}"); + Console.WriteLine ($"LLVM GIT URL: {LlvmUpdateInfo.BaseUrl}"); + + return UpdateSources () ? 0 : 1; + } catch (Exception ex) { + Console.Error.WriteLine ("Failed to update LLVM sources. Exception was thrown:"); + Console.Error.WriteLine (ex.ToString ()); + Console.Error.WriteLine (); + return 1; + } + } + + static bool UpdateSources () + { + Console.WriteLine (); + Console.WriteLine ("Checking for updates:"); + var options = new EnumerationOptions { + RecurseSubdirectories = true, + }; + + foreach (string file in Directory.EnumerateFiles (LocalSourcesPath, "*.*", options)) { + UpdateSource (file); + } + + return true; + } + + static void UpdateSource (string localPath) + { + Console.WriteLine ($" * {localPath}"); + + var uriBuilder = new UriBuilder (LlvmUpdateInfo.BaseUrl); + uriBuilder.Path += "/" + Path.GetRelativePath (LocalSourcesPath, localPath); + uriBuilder.Query = "format=TEXT"; + + var fileUrl = uriBuilder.Uri; + string? tempFilePath = null; + + try { + tempFilePath = DownloadFile (fileUrl); + UpdateIfNecessary (localPath, tempFilePath); + } finally { + DeleteFile (tempFilePath); + } + } + + static void UpdateIfNecessary (string localPath, string? remotePath) + { + if (String.IsNullOrEmpty (remotePath)) { + throw new InvalidOperationException ("Remote file not downloaded properly."); + } + + var fi = new FileInfo (remotePath); + if (!fi.Exists) { + throw new InvalidOperationException ($"Remote file '{remotePath}' does not exist."); + } + + if (fi.Length == 0) { + throw new InvalidOperationException ($"Remove file '{remotePath}' is empty."); + } + + byte[] localHash = GetFileHash (localPath); + byte[] remoteHash = GetFileHash (remotePath); + + if (localHash.Equals (remoteHash)) { + Console.WriteLine ($" Local file is identical to the remote one. No need to update."); + return; + } + + Console.WriteLine ($" Local file is different to the remote one. Updating."); + File.Copy (remotePath, localPath, overwrite: true); + } + + static string DownloadFile (Uri url) + { + string targetFile = Path.GetTempFileName (); + Console.WriteLine ($" Downloading: {url}"); + + TimeSpan delay = ExceptionRetryInitialDelay; + for (int i = 0; i < ExceptionRetries; i++) { + try { + DoDownload (url, targetFile); + break; + } catch (Exception ex) { + Console.Error.WriteLine ($" Download of '{url}', attempt {i} failed: {ex.Message}"); + if (i < ExceptionRetries - 1) { + Console.Error.WriteLine ($" Retrying after delay ({delay})"); + WaitAWhile ($"Download {url}", i, ref delay); + } else { + throw; + } + } + } + + return targetFile; + } + + static void DoDownload (Uri url, string targetFile) + { + using var httpClient = CreateHttpClient (); + httpClient.Timeout = WebRequestTimeout; + HttpResponseMessage resp = httpClient.GetAsync (url, HttpCompletionOption.ResponseHeadersRead).Result; + resp.EnsureSuccessStatusCode (); + + using var fs = File.Open (targetFile, FileMode.Create, FileAccess.Write); + using var webStream = resp.Content.ReadAsStreamAsync ().Result; + var buf = new byte [16384]; + int nread; + + while ((nread = webStream.Read (buf, 0, buf.Length)) > 0) { + fs.Write (buf, 0, nread); + } + + fs.Flush (); + fs.Close (); + } + + static HttpClient CreateHttpClient () + { + // Originally from: https://github.com/dotnet/arcade/pull/15546 + // Configure the cert revocation check in a fail-open state to avoid intermittent failures + // on Mac if the endpoint is not available. This is only available on .NET Core, but has only been + // observed on Mac anyway. + + var handler = new SocketsHttpHandler (); + handler.SslOptions.CertificateChainPolicy = new X509ChainPolicy { + // Yes, check revocation. + // Yes, allow it to be downloaded if needed. + // Online is the default, but it doesn't hurt to be explicit. + RevocationMode = X509RevocationMode.Online, + // Roots never bother with revocation. + // ExcludeRoot is the default, but it doesn't hurt to be explicit. + RevocationFlag = X509RevocationFlag.ExcludeRoot, + // RevocationStatusUnknown at the EndEntity/Leaf certificate will not fail the chain build. + // RevocationStatusUnknown for any intermediate CA will not fail the chain build. + // IgnoreRootRevocationUnknown could also be specified, but it won't apply given ExcludeRoot above. + // The default is that all status codes are bad, this is not the default. + VerificationFlags = + X509VerificationFlags.IgnoreCertificateAuthorityRevocationUnknown | + X509VerificationFlags.IgnoreEndRevocationUnknown, + // Always use the "now" when building the chain, rather than the "now" of when this policy object was constructed. + VerificationTimeIgnored = true, + }; + + return new HttpClient (handler); + } + + static void DeleteFile (string? path) + { + if (String.IsNullOrEmpty (path) || !File.Exists (path)) { + return; + } + + try { + File.Delete (path); + } catch (Exception) { + // Swallow, doesn't really matter + } + } + static string HashToString (byte[] hash) + { + var sb = new StringBuilder (); + + foreach (byte b in hash) { + sb.Append ($"{b:x02}"); + } + return sb.ToString (); + } + + static byte[] GetFileHash (string path) + { + using Stream fs = File.OpenRead (path); + return SHA512.HashData (fs); } + + static void WaitAWhile (string what, int which, ref TimeSpan delay) + { + Thread.Sleep (delay); + delay = TimeSpan.FromMilliseconds (delay.TotalMilliseconds * 2); + } + } diff --git a/tools/update-llvm-source/update-llvm-source.csproj b/tools/update-llvm-source/update-llvm-source.csproj index 7ed2b48bdf5..036b9876099 100644 --- a/tools/update-llvm-source/update-llvm-source.csproj +++ b/tools/update-llvm-source/update-llvm-source.csproj @@ -8,7 +8,7 @@ false ../../bin/$(Configuration)/bin/update-llvm-source Exe - $(DotNetStableTargetFramework) + $(DotNetStableTargetFramework) Xamarin.Android.Tools disable enable @@ -17,5 +17,6 @@ + From 6b858ccee722cca425d657bdd5493faf74885c01 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Mon, 19 Jan 2026 15:48:29 +0100 Subject: [PATCH 20/39] Docs and some finishing touches to the updater --- Documentation/workflow/HowToUpdateNDK.md | 64 ++++++++++++++++++++ tools/update-llvm-source/Program.cs | 75 +++++++++++++++++++++--- 2 files changed, 132 insertions(+), 7 deletions(-) create mode 100644 Documentation/workflow/HowToUpdateNDK.md diff --git a/Documentation/workflow/HowToUpdateNDK.md b/Documentation/workflow/HowToUpdateNDK.md new file mode 100644 index 00000000000..ce1efb19a5a --- /dev/null +++ b/Documentation/workflow/HowToUpdateNDK.md @@ -0,0 +1,64 @@ +# How to update Android NDK + +For the most part, update of the NDK version used to build this repository is +very straightforward. The only complication arises from the fact that we carry +a copy of some LLVM source files, for its libc++ and libc++abi libraries. +The copied files are needed only by the `NativeAOT` host (see https://github.com/dotnet/runtime/issues/121172), +the `MonoVM` and `CoreCLR` hosts link against the two libraries directly. + +Our copy of LLVM sources *must* be updated *every time* we update the NDK version. + +## Update NDK reference in `xaprepare` + +Visit https://developer.android.com/ndk/downloads/index.html to obtain NDK revision +information then edit the `build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs` +file and update the `BuildAndroidPlatforms.AndroidNdkVersion` and `BuildAndroidPlatforms.AndroidNdkPkgRevision` +properties with the information obtained from the NDK distribution URL. + +## Update LLVM sources + +The best way to do it is by using the `tools/update-llvm-sources` utility, after runing `xaprepare`. + +You can run the utility directly with `dotnet tools/update-llvm-sources` or, if you are on a Unix +system, run `make update-llvm` from the top directory. + +### Details (should you need to update sources manually) + +Android NDK uses a fork of the upstream LLVM repository, currently +https://android.googlesource.com/toolchain/llvm-project and this is the repository updated tool +mentioned above uses to fetch the files. + +Android NDK has a manifest file for the LLVM toolchain which enumerates revisions of all the +components, however that file changes name in each release, based on information it yet another +manifest file, namely `${ANDROID_NDK_ROOT}/BUILD_INFO`. This is a JSON file, which contains a +number of properties, we are however interested only in one of them, named `bid`. Its value +is a string which is part of the second manifest, found in the `${ANDROID_NDK_ROOT}/manifest_${bid}.xml` +file. + +In the XML manifest, we can find an element named `project`, with its `name` attribute set to +`toolchain/llvm-project` - the `revision` attribute of that element is the Git revision we need +in order to access sources from the Google's `llvm-project` fork. + +Once you have the revision, you can either clone the Android fork repository and checkout the +revision, or visit the individual files in the browser. All the LLVM sources we copied are +contained in the `src-ThirdParty/llvm/` directory, with the subdirectories reflecting exactly +the `llvm-project` layout. This way, you can take a file path relative to `src-ThirdParty/llvm` and +form the file's URL as follows: + +``` +https://android.googlesource.com/toolchain/llvm-project/+/${LLVM_REVISION}/${RELATIVE_FILE_PATH} +``` + +Visiting this url will show you the file with syntax higlighting and line numbers, however it's +not the raw source, but rather its HTML rendering, useless for our purpose. In order to fetch the +raw source, we need to append `?format=TEXT` to the URL. Once visited in the browser (or fetched +using `curl` or `wget`), the resulting file will be downloaded but not yet ready for updating of +our copy. The downloaded file is encoded in the `base64` encoding and must be decoded before use. + +On Unix systems this can be done using the following command: + +```shell +$ base64 -d < downloaded_file.cpp > file.cpp +``` + +After that, the resulting file can be copied to its destination in our source tree. diff --git a/tools/update-llvm-source/Program.cs b/tools/update-llvm-source/Program.cs index 3becb614f7f..d58412771a8 100644 --- a/tools/update-llvm-source/Program.cs +++ b/tools/update-llvm-source/Program.cs @@ -1,3 +1,10 @@ +// +// This is a quick and dirty utility to update LLVM sources we copied locally +// for use with the NativeAOT runtime in order to avoid dependency on libc++, which +// NativeAOT cannot use on NDK r29 and newer, because of symbol conflicts (see https://github.com/dotnet/runtime/issues/121172) +// +// The utility is supposed to die fast and loud if anything goes wrong. +// using System; using System.IO; using System.Net.Http; @@ -63,16 +70,18 @@ static void UpdateSource (string localPath) var fileUrl = uriBuilder.Uri; string? tempFilePath = null; + string? decodedFilePath = null; try { tempFilePath = DownloadFile (fileUrl); - UpdateIfNecessary (localPath, tempFilePath); + decodedFilePath = UpdateIfNecessary (localPath, tempFilePath); } finally { DeleteFile (tempFilePath); + DeleteFile (decodedFilePath); } } - static void UpdateIfNecessary (string localPath, string? remotePath) + static string UpdateIfNecessary (string localPath, string? remotePath) { if (String.IsNullOrEmpty (remotePath)) { throw new InvalidOperationException ("Remote file not downloaded properly."); @@ -87,16 +96,66 @@ static void UpdateIfNecessary (string localPath, string? remotePath) throw new InvalidOperationException ($"Remove file '{remotePath}' is empty."); } + // Remote files are base64-encoded + string decodedFilePath = DecodeFile (remotePath); + byte[] localHash = GetFileHash (localPath); - byte[] remoteHash = GetFileHash (remotePath); + byte[] remoteHash = GetFileHash (decodedFilePath); - if (localHash.Equals (remoteHash)) { + if (ArraysAreEqual (localHash, remoteHash)) { Console.WriteLine ($" Local file is identical to the remote one. No need to update."); - return; + return decodedFilePath; } Console.WriteLine ($" Local file is different to the remote one. Updating."); - File.Copy (remotePath, localPath, overwrite: true); + File.Copy (decodedFilePath, localPath, overwrite: true); + + return decodedFilePath; + } + + static bool ArraysAreEqual (byte[] a, byte[] b) + { + if (a.Length != b.Length) { + return false; + } + + for (int i = 0; i < a.Length; i++) { + if (a[i] != b[i]) { + return false; + } + } + + return true; + } + + static string DecodeFile (string remotePath) + { + string decodedFilePath = Path.GetTempFileName (); + using var inFile = File.OpenRead (remotePath); + using var outFile = File.OpenWrite (decodedFilePath); + using var transform = new FromBase64Transform (FromBase64TransformMode.IgnoreWhiteSpaces); + byte[] outputBytes = new byte[transform.OutputBlockSize]; + + // Input sources are small, no need to do anything fancy here. + byte[] inputBytes = File.ReadAllBytes (remotePath); + + // Transform the data in chunks the size of InputBlockSize. + int i = 0; + while (inputBytes.Length - i > transform.InputBlockSize) { + int bytesWritten = transform.TransformBlock (inputBytes, i, transform.InputBlockSize, outputBytes, 0); + i += transform.InputBlockSize; + outFile.Write (outputBytes, 0, bytesWritten); + } + + // Transform the final block of data. + outputBytes = transform.TransformFinalBlock (inputBytes, i, inputBytes.Length - i); + outFile.Write (outputBytes, 0, outputBytes.Length); + outFile.Flush (); + outFile.Close (); + + // Free up any used resources. + transform.Clear (); + return decodedFilePath; } static string DownloadFile (Uri url) @@ -198,7 +257,9 @@ static string HashToString (byte[] hash) static byte[] GetFileHash (string path) { using Stream fs = File.OpenRead (path); - return SHA512.HashData (fs); + using var sha256 = SHA256.Create (); + + return sha256.ComputeHash (fs); } static void WaitAWhile (string what, int which, ref TimeSpan delay) From 0051a15c62413243dfeb3f233d3903d4479b23e8 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Tue, 20 Jan 2026 09:57:19 +0100 Subject: [PATCH 21/39] Update apkdesc files --- .../Base/BuildReleaseArm64SimpleDotNet.NativeAOT.apkdesc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.NativeAOT.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.NativeAOT.apkdesc index ece794fa0da..346843360c7 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.NativeAOT.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.NativeAOT.apkdesc @@ -8,10 +8,10 @@ "Size": 24224 }, "lib/arm64-v8a/libUnnamedProject.so": { - "Size": 4968680 + "Size": 4473168 }, "META-INF/BNDLTOOL.RSA": { - "Size": 1211 + "Size": 1223 }, "META-INF/BNDLTOOL.SF": { "Size": 1211 @@ -44,5 +44,5 @@ "Size": 1904 } }, - "PackageSize": 2094050 + "PackageSize": 1905634 } \ No newline at end of file From 6a023f86e4954c224e1a3aee21eff465a4f64298 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Wed, 21 Jan 2026 15:06:38 +0100 Subject: [PATCH 22/39] Should fix the multi-project NativeAOT builds --- .../Microsoft.Android.Sdk.NativeAOT.targets | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets index 5d8f0711b11..eebc426e4be 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets @@ -130,6 +130,7 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. $(_OriginalSuppressTrimAnalysisWarnings) + <_XANativeAotHostName>libnaot-android.$(Configuration.ToLower())-static-$(Configuration.ToLower()).a @@ -161,12 +162,13 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. - <_NdkLibs Include="@(RuntimePackAsset->WithMetadataValue('Filename', 'libnaot-android.$(Configuration.ToLower())-static-$(Configuration.ToLower())'))" /> + + <_XANativeAotHost Include="@(RuntimePackAsset->WithMetadataValue('Filename', '$(_XANativeAotHostName)'))" /> + <_XANativeAotHost Include="@(RuntimePackAsset->WithMetadataValue('DestinationSubPath', '$(_XANativeAotHostName)'))" /> + <_NdkLibs Include="@(_XANativeAotHost)" /> - - - - @@ -176,6 +178,8 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. + + From 66530a71d08bde8b1198b0c970ba0e817aa6f806 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 22 Jan 2026 12:00:34 +0100 Subject: [PATCH 23/39] Always specify configuration when building a solution This should fix NativeAOT builds of multi-project solutions. --- .../AndroidUpdateResourcesTest.cs | 1 + .../IncrementalBuildTest.cs | 2 ++ .../Xamarin.ProjectTools/Common/Builder.cs | 29 ++++++++++++++----- .../Common/SolutionBuilder.cs | 5 ++-- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AndroidUpdateResourcesTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AndroidUpdateResourcesTest.cs index f04aabcb3dd..bb4e0f8b647 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AndroidUpdateResourcesTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AndroidUpdateResourcesTest.cs @@ -1535,6 +1535,7 @@ public void SolutionBuildSeveralProjects ([Values] AndroidRuntime runtime) SolutionPath = Path.Combine (Root, path), MaxCpuCount = 4, BuildingInsideVisualStudio = false, // allow projects dependencies to build + Configuration = isRelease ? "Release" : "Debug", // MUST be set for NativeAOT builds }) { var apps = new List (); var app1 = new XamarinAndroidApplicationProject { diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs index 8e3600f20dd..15c1b013f8e 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs @@ -280,6 +280,7 @@ public void AllProjectsHaveSameOutputDirectory ([Values] AndroidRuntime runtime) var testPath = Path.Combine ("temp", TestName); var sb = new SolutionBuilder ("AllProjectsHaveSameOutputDirectory.sln") { SolutionPath = Path.Combine (Root, testPath), + Configuration = isRelease ? "Release" : "Debug", // MUST be set for NativeAOT builds }; var app1 = new XamarinAndroidApplicationProject () { @@ -315,6 +316,7 @@ public void BuildSolutionWithMultipleProjectsInParallel ([Values] AndroidRuntime var sb = new SolutionBuilder("BuildSolutionWithMultipleProjects.sln") { SolutionPath = Path.Combine (Root, testPath), MaxCpuCount = 4, + Configuration = "Release", // MUST be set for NativeAOT builds }; bool aotAssemblies = runtime switch { diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/Builder.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/Builder.cs index 753f6d7a9fa..d308dc19f9f 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/Builder.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/Builder.cs @@ -41,22 +41,22 @@ public class Builder : IDisposable /// /// public bool IsUnix { get; set; } - + /// /// This passes /p:BuildingInsideVisualStudio=True, command-line to MSBuild /// public bool BuildingInsideVisualStudio { get; set; } = true; - + /// /// Passes /m:N to MSBuild, defaults to null to omit the /m parameter completely. /// public int? MaxCpuCount { get; set; } - + /// /// Gets or sets the MSBuild logger verbosity level. /// public LoggerVerbosity Verbosity { get; set; } = LoggerVerbosity.Normal; - + /// /// Gets the output lines from the last build operation. /// @@ -81,29 +81,37 @@ public IEnumerable LastBuildOutput { return lastBuildOutput; } } - + /// /// Gets the duration of the last build operation. /// /// public TimeSpan LastBuildTime { get; protected set; } - + /// /// Gets or sets the filename for the build log file. /// /// public string BuildLogFile { get; set; } - + /// /// Gets or sets a value indicating whether to throw an exception when builds fail. /// public bool ThrowOnBuildFailure { get; set; } - + /// /// True if NuGet restore occurs automatically (default) /// public bool AutomaticNuGetRestore { get; set; } = true; + /// + /// If not `null` or empty, sets the configuration to use for the build. + /// This property must be set to `Release` for all the NativeAOT builds, since otherwise + /// the build will default to `Debug` when building a solution and that will cause the + /// **project** build to fail to locate the NativeAOT host library. + /// + public string? Configuration { get; set; } + /// /// Checks whether cross-compilers are available for the specified Android ABIs. /// @@ -247,6 +255,11 @@ protected bool BuildInternal (string projectOrSolution, string target, string [] var responseFile = Path.Combine (XABuildPaths.TestOutputDirectory, Path.GetDirectoryName (projectOrSolution), "project.rsp"); args.Append ("build "); + if (!String.IsNullOrEmpty (Configuration)) { + args.Append ("-c"); + args.Append (Configuration); + } + if (TestEnvironment.UseLocalBuildOutput) { psi.SetEnvironmentVariable ("DOTNETSDK_WORKLOAD_MANIFEST_ROOTS", TestEnvironment.WorkloadManifestOverridePath); psi.SetEnvironmentVariable ("DOTNETSDK_WORKLOAD_PACK_ROOTS", TestEnvironment.WorkloadPackOverridePath); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/SolutionBuilder.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/SolutionBuilder.cs index 8c3eea39321..7f9a4b5018a 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/SolutionBuilder.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/SolutionBuilder.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Text; using System.IO; @@ -31,7 +31,7 @@ public void Save () sb.AppendFormat ("Microsoft Visual Studio Solution File, Format Version {0}\r\n", "12.00"); sb.AppendFormat ("# Visual Studio {0}\r\n", "2012"); foreach (var p in Projects) { - sb.AppendFormat ("Project(\"{{{0}}}\") = \"{1}\", \"{2}\", \"{{{3}}}\"\r\n", p.ProjectTypeGuid, p.ProjectName, + sb.AppendFormat ("Project(\"{{{0}}}\") = \"{1}\", \"{2}\", \"{{{3}}}\"\r\n", p.ProjectTypeGuid, p.ProjectName, Path.Combine(p.ProjectName,p.ProjectFilePath), p.ProjectGuid); sb.Append ("EndProject\r\n"); } @@ -102,4 +102,3 @@ protected override void Dispose (bool disposing) } } } - From c2bf8e7aa60326fef9b3dd7357037608e0cd115b Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Fri, 23 Jan 2026 16:10:52 +0100 Subject: [PATCH 24/39] Ugh --- .../Tests/Xamarin.ProjectTools/Common/Builder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/Builder.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/Builder.cs index d308dc19f9f..6ac799198e0 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/Builder.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/Builder.cs @@ -256,7 +256,7 @@ protected bool BuildInternal (string projectOrSolution, string target, string [] args.Append ("build "); if (!String.IsNullOrEmpty (Configuration)) { - args.Append ("-c"); + args.Append ("-c "); args.Append (Configuration); } From f8c130b3bffafb49a447d58789d784551ed01ffb Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Mon, 26 Jan 2026 14:05:22 +0100 Subject: [PATCH 25/39] Ugh --- .../Tests/Xamarin.ProjectTools/Common/Builder.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/Builder.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/Builder.cs index 6ac799198e0..4e8cd375b75 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/Builder.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/Builder.cs @@ -256,8 +256,7 @@ protected bool BuildInternal (string projectOrSolution, string target, string [] args.Append ("build "); if (!String.IsNullOrEmpty (Configuration)) { - args.Append ("-c "); - args.Append (Configuration); + args.Append ($"-c {Configuration} "); } if (TestEnvironment.UseLocalBuildOutput) { From 696d4a8c543e8f7d77f0158a27714ecf60bbd3a6 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Mon, 26 Jan 2026 16:26:24 +0100 Subject: [PATCH 26/39] Fix after rebase From c3d6527df287e0454ad8ea810864df2271eca2c1 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Tue, 27 Jan 2026 11:24:22 +0100 Subject: [PATCH 27/39] Unbreak the BuildSolutionWithMultipleProjectsInParallel test for CoreCLR and NativeAOT --- .../targets/Microsoft.Android.Sdk.NativeAOT.targets | 2 +- .../Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets index eebc426e4be..ca57346a2ec 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets @@ -130,7 +130,7 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. $(_OriginalSuppressTrimAnalysisWarnings) - <_XANativeAotHostName>libnaot-android.$(Configuration.ToLower())-static-$(Configuration.ToLower()).a + <_XANativeAotHostName>libnaot-android.release-static-release.a diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs index 15c1b013f8e..4d526c5aa56 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs @@ -316,7 +316,6 @@ public void BuildSolutionWithMultipleProjectsInParallel ([Values] AndroidRuntime var sb = new SolutionBuilder("BuildSolutionWithMultipleProjects.sln") { SolutionPath = Path.Combine (Root, testPath), MaxCpuCount = 4, - Configuration = "Release", // MUST be set for NativeAOT builds }; bool aotAssemblies = runtime switch { From 0ec18ec9f3aa91fe714024a9ca0fe8210ef43e22 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 29 Jan 2026 11:08:09 +0100 Subject: [PATCH 28/39] Update apkdesc files --- .../BuildReleaseArm64SimpleDotNet.MonoVM.apkdesc | 12 ++++++------ .../BuildReleaseArm64XFormsDotNet.MonoVM.apkdesc | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.MonoVM.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.MonoVM.apkdesc index c8e07661c0b..51e9f4a845f 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.MonoVM.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.MonoVM.apkdesc @@ -14,10 +14,10 @@ "Size": 88056 }, "lib/arm64-v8a/lib_Mono.Android.dll.so": { - "Size": 117184 + "Size": 116536 }, "lib/arm64-v8a/lib_Mono.Android.Runtime.dll.so": { - "Size": 26328 + "Size": 23800 }, "lib/arm64-v8a/lib_System.Console.dll.so": { "Size": 24416 @@ -26,7 +26,7 @@ "Size": 25496 }, "lib/arm64-v8a/lib_System.Private.CoreLib.dll.so": { - "Size": 633928 + "Size": 633856 }, "lib/arm64-v8a/lib_System.Runtime.dll.so": { "Size": 20232 @@ -44,7 +44,7 @@ "Size": 36616 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 1386232 + "Size": 1466544 }, "lib/arm64-v8a/libmonosgen-2.0.so": { "Size": 3123608 @@ -62,7 +62,7 @@ "Size": 165536 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 19640 + "Size": 19608 }, "META-INF/BNDLTOOL.RSA": { "Size": 1223 @@ -98,5 +98,5 @@ "Size": 1904 } }, - "PackageSize": 3267093 + "PackageSize": 3283477 } \ No newline at end of file diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.MonoVM.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.MonoVM.apkdesc index fd8f2a40c19..91ab34c9fc0 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.MonoVM.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.MonoVM.apkdesc @@ -35,13 +35,13 @@ "Size": 25424 }, "lib/arm64-v8a/lib_Java.Interop.dll.so": { - "Size": 96664 + "Size": 96656 }, "lib/arm64-v8a/lib_Mono.Android.dll.so": { - "Size": 541152 + "Size": 540296 }, "lib/arm64-v8a/lib_Mono.Android.Runtime.dll.so": { - "Size": 26328 + "Size": 23800 }, "lib/arm64-v8a/lib_mscorlib.dll.so": { "Size": 21464 @@ -116,7 +116,7 @@ "Size": 27040 }, "lib/arm64-v8a/lib_System.Private.CoreLib.dll.so": { - "Size": 994736 + "Size": 994584 }, "lib/arm64-v8a/lib_System.Private.DataContractSerialization.dll.so": { "Size": 217984 @@ -239,7 +239,7 @@ "Size": 36616 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 1386232 + "Size": 1466544 }, "lib/arm64-v8a/libmonosgen-2.0.so": { "Size": 3123608 @@ -257,7 +257,7 @@ "Size": 165536 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 350616 + "Size": 350584 }, "META-INF/androidx.activity_activity.version": { "Size": 6 @@ -2480,5 +2480,5 @@ "Size": 812848 } }, - "PackageSize": 11131965 + "PackageSize": 11152445 } \ No newline at end of file From 5e676270b8480a3711b82f51552de52cbb03d06f Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Wed, 11 Feb 2026 16:53:07 +0100 Subject: [PATCH 29/39] Fix after rebase --- .../ConfigAndData/BuildAndroidPlatforms.cs | 6 ++-- src/native/CMakePresets.json.in | 28 ++++++++++++++++--- src/native/native.targets | 1 + 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs index 3543e0308e0..d2f3eed5907 100644 --- a/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs @@ -7,8 +7,10 @@ class BuildAndroidPlatforms { public const string AndroidNdkVersion = "29"; public const string AndroidNdkPkgRevision = "29.0.14206865"; - public const int NdkMinimumAPI = 21; - public const int NdkMinimumAPILegacy32 = 21; + + public static string NdkMinimumAPI => Context.Instance.Properties.GetRequiredValue (KnownProperties.AndroidMinimumDotNetApiLevel); + public static string NdkMinimumAPILegacy32 => NdkMinimumAPI; + public static string NdkMinimumNonMonoAPI => Context.Instance.Properties.GetRequiredValue (KnownProperties.AndroidMinimumNonMonoApiLevel); public static readonly List AllPlatforms = new List { new AndroidPlatform (apiName: "", apiLevel: 1, platformID: "1"), diff --git a/src/native/CMakePresets.json.in b/src/native/CMakePresets.json.in index 8dd43ee2815..4402d0889ba 100644 --- a/src/native/CMakePresets.json.in +++ b/src/native/CMakePresets.json.in @@ -219,7 +219,12 @@ { "name": "nativeaot-default-debug-arm64-v8a", - "inherits": ["nativeaot-default-common", "common-debug", "common-arm64-v8a"] + "inherits": ["nativeaot-default-common", "common-debug", "nonmono-common-arm64-v8a"] + }, + + { + "name": "coreclr-default-debug-arm64-v8a", + "inherits": ["default-common", "common-debug", "nonmono-common-arm64-v8a"] }, { @@ -229,7 +234,12 @@ { "name": "nativeaot-default-release-arm64-v8a", - "inherits": ["nativeaot-default-common", "common-release", "common-arm64-v8a"] + "inherits": ["nativeaot-default-common", "common-release", "nonmono-common-arm64-v8a"] + }, + + { + "name": "coreclr-default-release-arm64-v8a", + "inherits": ["default-common", "common-release", "nonmono-common-arm64-v8a"] }, { @@ -317,7 +327,12 @@ { "name": "nativeaot-default-debug-x86_64", - "inherits": ["nativeaot-default-common", "common-debug", "common-x86_64"] + "inherits": ["nativeaot-default-common", "common-debug", "nonmono-common-x86_64"] + }, + + { + "name": "coreclr-default-debug-x86_64", + "inherits": ["default-common", "common-debug", "nonmono-common-x86_64"] }, { @@ -327,7 +342,12 @@ { "name": "nativeaot-default-release-x86_64", - "inherits": ["nativeaot-default-common", "common-release", "common-x86_64"] + "inherits": ["nativeaot-default-common", "common-release", "nonmono-common-x86_64"] + }, + + { + "name": "coreclr-default-release-x86_64", + "inherits": ["default-common", "common-release", "nonmono-common-x86_64"] }, { diff --git a/src/native/native.targets b/src/native/native.targets index 8ef84e50325..7610a83f6a6 100644 --- a/src/native/native.targets +++ b/src/native/native.targets @@ -158,6 +158,7 @@ Outputs="@(_ConfigureRuntimesOutputs)"> <_PresetPrefix Condition=" '$(CMakeRuntimeFlavor)' == 'NativeAOT' ">nativeaot- + <_PresetPrefix Condition=" '$(CMakeRuntimeFlavor)' == 'CoreCLR' ">coreclr- <_NoInline Condition=" '$(DoNotInlineMonodroid)' == 'true' ">-DDONT_INLINE=ON <_NoStrip Condition=" '$(DoNotStripMonodroid)' == 'true' ">-DSTRIP_DEBUG=OFF <_LocalDotNetRuntimePath Condition=" '$(CLRLocalRuntimePath)' != '' And '$(CMakeRuntimeFlavor)' == 'CoreCLR' ">-DLOCAL_CORECLR_PATH="$(CLRLocalRuntimePath)" From 6b246a3744412beeea6a4664360e3a238c00d99d Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 12 Feb 2026 09:54:22 +0100 Subject: [PATCH 30/39] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Documentation/workflow/HowToUpdateNDK.md | 4 ++-- .../xaprepare/xaprepare/ConfigAndData/Configurables.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/workflow/HowToUpdateNDK.md b/Documentation/workflow/HowToUpdateNDK.md index ce1efb19a5a..b4db2fd3dc9 100644 --- a/Documentation/workflow/HowToUpdateNDK.md +++ b/Documentation/workflow/HowToUpdateNDK.md @@ -17,7 +17,7 @@ properties with the information obtained from the NDK distribution URL. ## Update LLVM sources -The best way to do it is by using the `tools/update-llvm-sources` utility, after runing `xaprepare`. +The best way to do it is by using the `tools/update-llvm-sources` utility, after running `xaprepare`. You can run the utility directly with `dotnet tools/update-llvm-sources` or, if you are on a Unix system, run `make update-llvm` from the top directory. @@ -49,7 +49,7 @@ form the file's URL as follows: https://android.googlesource.com/toolchain/llvm-project/+/${LLVM_REVISION}/${RELATIVE_FILE_PATH} ``` -Visiting this url will show you the file with syntax higlighting and line numbers, however it's +Visiting this url will show you the file with syntax highlighting and line numbers, however it's not the raw source, but rather its HTML rendering, useless for our purpose. In order to fetch the raw source, we need to append `?format=TEXT` to the URL. Once visited in the browser (or fetched using `curl` or `wget`), the resulting file will be downloaded but not yet ready for updating of diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs index 67184ae5bbe..8bab7af6f13 100644 --- a/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs @@ -36,7 +36,7 @@ public static partial class Urls public static readonly Uri AndroidToolchain_AndroidUri = new Uri ("https://dl.google.com/android/repository/"); public static Uri BinutilsArchive = new Uri ($"https://github.com/dotnet/android-native-tools/releases/download/{BinutilsVersion}/xamarin-android-toolchain-{BinutilsVersion}.7z"); - public static Uri GoogleSourcesBase = new Uri ("https://android.googlesource.com"); + public static readonly Uri GoogleSourcesBase = new Uri ("https://android.googlesource.com"); } public static partial class Defaults From 6159a5efeca05ed1d82bf0f51027ef8ea1b5f6c5 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 12 Feb 2026 10:02:30 +0100 Subject: [PATCH 31/39] Address feedback --- Documentation/workflow/HowToUpdateNDK.md | 4 ++-- src/native/clr/host/bridge-processing.cc | 8 ++++---- src/native/clr/host/gc-bridge.cc | 8 ++++---- src/native/clr/host/host-shared.cc | 2 +- src/native/clr/include/shared/format-helpers.hh | 4 ++-- src/native/clr/runtime-base/android-system-shared.cc | 2 +- src/native/clr/shared/log_functions.cc | 2 +- src/native/common/include/runtime-base/strings.hh | 2 +- src/native/nativeaot/cxx-abi/terminate.cc | 2 +- tools/update-llvm-source/Program.cs | 4 ++-- tools/update-llvm-source/update-llvm-source.csproj | 2 +- 11 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Documentation/workflow/HowToUpdateNDK.md b/Documentation/workflow/HowToUpdateNDK.md index b4db2fd3dc9..c0a70e98a3a 100644 --- a/Documentation/workflow/HowToUpdateNDK.md +++ b/Documentation/workflow/HowToUpdateNDK.md @@ -17,9 +17,9 @@ properties with the information obtained from the NDK distribution URL. ## Update LLVM sources -The best way to do it is by using the `tools/update-llvm-sources` utility, after running `xaprepare`. +The best way to do it is by using the `tools/update-llvm-source` utility, after runing `xaprepare`. -You can run the utility directly with `dotnet tools/update-llvm-sources` or, if you are on a Unix +You can run the utility directly with `dotnet tools/update-llvm-source` or, if you are on a Unix system, run `make update-llvm` from the top directory. ### Details (should you need to update sources manually) diff --git a/src/native/clr/host/bridge-processing.cc b/src/native/clr/host/bridge-processing.cc index e4c5a88e8cd..d3487668424 100644 --- a/src/native/clr/host/bridge-processing.cc +++ b/src/native/clr/host/bridge-processing.cc @@ -367,7 +367,7 @@ void BridgeProcessingShared::log_weak_to_gref (jobject weak, jobject handle) noe dynamic_local_string message; bool formatted = format_printf ( message, - "take_global_ref wref={:#x} -> handle={:#x}\n", + "take_global_ref wref=%p -> handle=%p\n", weak_p, handle_p ); @@ -390,7 +390,7 @@ void BridgeProcessingShared::log_weak_to_gref (jobject weak, jobject handle) noe [[gnu::always_inline]] void BridgeProcessingShared::log_weak_ref_collected (jobject weak) noexcept { - if (Logger::gc_spew_enabled ()) [[likely]] { + if (!Logger::gc_spew_enabled ()) [[likely]] { return; } @@ -400,7 +400,7 @@ void BridgeProcessingShared::log_weak_ref_collected (jobject weak) noexcept dynamic_local_string message; bool formatted = format_printf ( message, - "handle 0x%x/W; was collected by a Java GC", + "handle 0x%p/W; was collected by a Java GC", weak_p ); @@ -429,7 +429,7 @@ void BridgeProcessingShared::log_take_weak_global_ref (jobject handle) noexcept dynamic_local_string message; bool formatted = format_printf ( message, - "take_weak_global_ref handle={:#x}\n", + "take_weak_global_ref handle=%p\n", handle_p ); diff --git a/src/native/clr/host/gc-bridge.cc b/src/native/clr/host/gc-bridge.cc index 4cbfaf0b9a5..c46f32d319e 100644 --- a/src/native/clr/host/gc-bridge.cc +++ b/src/native/clr/host/gc-bridge.cc @@ -88,7 +88,7 @@ void GCBridge::log_mark_cross_references_args_if_enabled (MarkCrossReferencesArg log_info ( LOG_GC, #if defined(XA_HOST_NATIVEAOT) - "cross references callback invoked with %z sccs and %z xrefs.", + "cross references callback invoked with %zu sccs and %zu xrefs.", #else "cross references callback invoked with {} sccs and {} xrefs."sv, #endif @@ -114,7 +114,7 @@ void GCBridge::log_mark_cross_references_args_if_enabled (MarkCrossReferencesArg size_t source_index = args->CrossReferences [i].SourceGroupIndex; size_t dest_index = args->CrossReferences [i].DestinationGroupIndex; #if defined(XA_HOST_NATIVEAOT) - log_info_nocheck_printf (LOG_GC, "xref [%z] %z -> %z", i, source_index, dest_index); + log_info_nocheck_printf (LOG_GC, "xref [%zu] %zu -> %zu", i, source_index, dest_index); #else log_info_nocheck_fmt (LOG_GC, "xref [{}] {} -> {}", i, source_index, dest_index); #endif @@ -134,7 +134,7 @@ void GCBridge::log_handle_context (JNIEnv *env, HandleContext *ctx) noexcept log_info ( LOG_GC, #if defined(XA_HOST_NATIVEAOT) - "gref %p [%s]", + "gref 0x%x [%s]", #else "gref {:#x} [{}]"sv, #endif @@ -146,7 +146,7 @@ void GCBridge::log_handle_context (JNIEnv *env, HandleContext *ctx) noexcept log_info ( LOG_GC, #if defined(XA_HOST_NATIVEAOT) - "gref %p [unknown class]", + "gref 0x%x [unknown class]", #else "gref {:#x} [unknown class]"sv, #endif diff --git a/src/native/clr/host/host-shared.cc b/src/native/clr/host/host-shared.cc index 5d0341e6523..d74ad30b00c 100644 --- a/src/native/clr/host/host-shared.cc +++ b/src/native/clr/host/host-shared.cc @@ -15,7 +15,7 @@ auto HostCommon::get_java_class_name_for_TypeManager (jclass klass) noexcept -> log_error ( LOG_DEFAULT, #if defined(XA_HOST_NATIVEAOT) - "Failed to obtain Java class name for object at %p " , + "Failed to obtain Java class name for object at %p", #else "Failed to obtain Java class name for object at {:p}"sv, #endif diff --git a/src/native/clr/include/shared/format-helpers.hh b/src/native/clr/include/shared/format-helpers.hh index 2da0f1c5af0..461b0395823 100644 --- a/src/native/clr/include/shared/format-helpers.hh +++ b/src/native/clr/include/shared/format-helpers.hh @@ -41,7 +41,7 @@ namespace xamarin::android { auto res = static_cast(n); if (res < dest.size ()) [[likely]] { - return std::string (dest.get (), dest.size ()); + return std::string (dest.get (), dest.length ()); } // resize_for_extra adds one more byte for the NUL character @@ -60,7 +60,7 @@ namespace xamarin::android { return ""; } - return std::string (dest.get (), dest.size ()); + return std::string (dest.get (), dest.length ()); } #else template [[gnu::always_inline]] diff --git a/src/native/clr/runtime-base/android-system-shared.cc b/src/native/clr/runtime-base/android-system-shared.cc index 4a054d82485..f607217d2e6 100644 --- a/src/native/clr/runtime-base/android-system-shared.cc +++ b/src/native/clr/runtime-base/android-system-shared.cc @@ -39,7 +39,7 @@ AndroidSystem::monodroid__system_property_get (std::string_view const& name, cha log_warn ( LOG_DEFAULT, #if defined(XA_HOST_NATIVEAOT) - "Buffer to store system property may be too small, will copy only %z bytes", + "Buffer to store system property may be too small, will copy only %zu bytes", #else "Buffer to store system property may be too small, will copy only {} bytes"sv, #endif diff --git a/src/native/clr/shared/log_functions.cc b/src/native/clr/shared/log_functions.cc index 740653b5721..43dbab229a6 100644 --- a/src/native/clr/shared/log_functions.cc +++ b/src/native/clr/shared/log_functions.cc @@ -92,7 +92,7 @@ void log_fatal_printf (LogCategories category, const char *format, ...) noexcept { va_list args; - DO_LOG (ANDROID_LOG_ERROR, category, format, args); + DO_LOG (ANDROID_LOG_FATAL, category, format, args); } void diff --git a/src/native/common/include/runtime-base/strings.hh b/src/native/common/include/runtime-base/strings.hh index 203e7eff141..f7bad8ea904 100644 --- a/src/native/common/include/runtime-base/strings.hh +++ b/src/native/common/include/runtime-base/strings.hh @@ -210,7 +210,7 @@ namespace xamarin::android { log_error ( LOG_DEFAULT, #if defined(XA_HOST_NATIVEAOT) - "Cannot convert string to integer, index %z is out of range", + "Cannot convert string to integer, index %zu is out of range", #else "Cannot convert string to integer, index {} is out of range"sv, #endif diff --git a/src/native/nativeaot/cxx-abi/terminate.cc b/src/native/nativeaot/cxx-abi/terminate.cc index 772eb2e5b5b..abbe7781531 100644 --- a/src/native/nativeaot/cxx-abi/terminate.cc +++ b/src/native/nativeaot/cxx-abi/terminate.cc @@ -1,5 +1,5 @@ // -// Simple implementation of std::terminate() for Xamarin.Android +// Simple implementation of std::terminate() for .NET for Android // // Does NOT support terminate handlers, since we don't use them. // diff --git a/tools/update-llvm-source/Program.cs b/tools/update-llvm-source/Program.cs index d58412771a8..cf689cfcd1a 100644 --- a/tools/update-llvm-source/Program.cs +++ b/tools/update-llvm-source/Program.cs @@ -14,7 +14,7 @@ using System.Text; using System.Threading; -namespace Xamarin.Android.Tools; +namespace Microsoft.Android.Tools; class App { @@ -93,7 +93,7 @@ static string UpdateIfNecessary (string localPath, string? remotePath) } if (fi.Length == 0) { - throw new InvalidOperationException ($"Remove file '{remotePath}' is empty."); + throw new InvalidOperationException ($"Remote file '{remotePath}' is empty."); } // Remote files are base64-encoded diff --git a/tools/update-llvm-source/update-llvm-source.csproj b/tools/update-llvm-source/update-llvm-source.csproj index 036b9876099..268a4215774 100644 --- a/tools/update-llvm-source/update-llvm-source.csproj +++ b/tools/update-llvm-source/update-llvm-source.csproj @@ -9,7 +9,7 @@ ../../bin/$(Configuration)/bin/update-llvm-source Exe $(DotNetStableTargetFramework) - Xamarin.Android.Tools + Microsoft.Android.Tools disable enable Major From c17e7d590c406029f378948e8fc186c93d00ab36 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Fri, 13 Feb 2026 13:01:20 +0100 Subject: [PATCH 32/39] Fix linking NAOT apps with API level > 21 --- build-tools/scripts/Ndk.projitems.in | 8 ++-- .../Microsoft.Android.Sdk.NativeAOT.targets | 37 ++++++------------- .../Xamarin.Android.Build.Tasks.targets | 4 +- .../Xamarin.Android.Common.props.in | 10 ++--- 4 files changed, 22 insertions(+), 37 deletions(-) diff --git a/build-tools/scripts/Ndk.projitems.in b/build-tools/scripts/Ndk.projitems.in index fbc98d84a4b..c00022546fe 100644 --- a/build-tools/scripts/Ndk.projitems.in +++ b/build-tools/scripts/Ndk.projitems.in @@ -11,8 +11,8 @@ @NDK_X86_API_NET@ @NDK_X86_64_API@ @NDK_X86_64_API_NET@ - @NDK_ARM64_V8A_API_NON_MONO@ - @NDK_X86_64_API_NON_MONO@ + @NDK_ARM64_V8A_API_NON_MONO@ + @NDK_X86_64_API_NON_MONO@ @@ -32,7 +32,7 @@ Condition=" $(AndroidSupportedTargetJitAbisForConditionalChecks.Contains (':arm64-v8a:')) "> $(AndroidNdkApiLevel_ArmV8a) $(AndroidNdkApiLevel_Arm64) - $(AndroidNdkApiLevelNonMono_Arm64) + $(AndroidNdkAPiLevelNonMono_Arm64) android-arm64 True True @@ -55,7 +55,7 @@ Condition=" $(AndroidSupportedTargetJitAbisForConditionalChecks.Contains (':x86_64:')) "> $(AndroidNdkApiLevel_X86_64) $(AndroidNdkApiLevel_X64) - $(AndroidNdkApiLevelNonMono_X64) + $(AndroidNdkAPiLevelNonMono_X64) android-x64 True True diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets index ca57346a2ec..89ace556366 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets @@ -80,32 +80,8 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. <_NdkWrapperScriptExt Condition=" $([MSBuild]::IsOSPlatform('windows')) ">.cmd <_NdkSysrootDir>$(_AndroidNdkDirectory)toolchains/llvm/prebuilt/$(_NdkPrebuiltAbi)/sysroot/usr/lib/$(_NdkSysrootAbi)/ <_NdkBinDir>$(_AndroidNdkDirectory)toolchains/llvm/prebuilt/$(_NdkPrebuiltAbi)/bin/ - - - $(_NdkAbi)-linux-android$(_NDKApiLevel)-clang$(_NdkWrapperScriptExt) - $(_NdkAbi)-linux-android$(_NDKApiLevel)-clang$(_NdkWrapperScriptExt) + $(_NdkBinDir)clang++ + $(_NdkBinDir)clang++ llvm-objcopy @@ -131,8 +107,17 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. $(_OriginalSuppressTrimAnalysisWarnings) <_XANativeAotHostName>libnaot-android.release-static-release.a + <_NDKApiLevel Condition=" '$(RuntimeIdentifier)' == 'android-arm64' ">$(AndroidNdkApiLevel_Arm64) + <_NDKArch Condition=" '$(RuntimeIdentifier)' == 'android-arm64' ">aarch64 + <_NDKApiLevel Condition=" '$(RuntimeIdentifier)' == 'android-x64' ">$(AndroidNdkAPiLevel_X64) + <_NDKArch Condition=" '$(RuntimeIdentifier)' == 'android-x64' ">x86_64 + + diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets index 047f37ba58f..e27e197ef52 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets @@ -150,10 +150,10 @@ <_XACommonPropsReplacement Include="@NDK_PKG_REVISION@=26.3.11579264" /> <_XACommonPropsReplacement Include="@NDK_ARM64_V8A_API@=$(AndroidNdkApiLevel_ArmV8a)" /> - <_XACommonPropsReplacement Include="@NDK_ARM64_V8A_API_NON_MONO@=$(AndroidNdkApiLevelNonMono_Arm64)" /> + <_XACommonPropsReplacement Include="@NDK_ARM64_V8A_API_NON_MONO@=$(AndroidNdkAPiLevelNonMono_Arm64)" /> <_XACommonPropsReplacement Include="@NDK_ARMEABI_V7_API@=$(AndroidNdkApiLevel_ArmV7a)" /> <_XACommonPropsReplacement Include="@NDK_X86_64_API@=$(AndroidNdkApiLevel_X86_64)" /> - <_XACommonPropsReplacement Include="@NDK_X86_64_API_NON_MONO@=$(AndroidNdkApiLevelNonMono_X64)" /> + <_XACommonPropsReplacement Include="@NDK_X86_64_API_NON_MONO@=$(AndroidNdkAPiLevelNonMono_X64)" /> <_XACommonPropsReplacement Include="@NDK_X86_API@=$(AndroidNdkApiLevel_X86)" /> <_XACommonPropsReplacement Include="@PACKAGE_VERSION_BUILD@=$(XAVersionCommitCount)" /> <_XACommonPropsReplacement Include="@PACKAGE_VERSION@=$(ProductVersion)" /> diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.props.in b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.props.in index 02589ee9384..6f3e9f58235 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.props.in +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.props.in @@ -35,25 +35,25 @@ true @NDK_ARM64_V8A_API_NON_MONO@ - @NDK_X86_64_API_NON_MONO@ + @NDK_X86_64_API_NON_MONO@ @NDK_ARMEABI_V7_API@ - + @NDK_ARM64_V8A_API@ - + @NDK_ARM64_V8A_API_NON_MONO@ @NDK_X86_API@ - + @NDK_X86_64_API@ - + @NDK_X86_64_API_NON_MONO@ From 1efb6707a6a97d663460fd9d1b865d5b0c12dc3f Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Fri, 13 Feb 2026 13:59:01 +0100 Subject: [PATCH 33/39] Address feedback --- build-tools/scripts/Ndk.projitems.in | 8 ++++---- .../Xamarin.Android.Build.Tasks.targets | 4 ++-- .../Xamarin.Android.Common.props.in | 10 +++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-tools/scripts/Ndk.projitems.in b/build-tools/scripts/Ndk.projitems.in index c00022546fe..fbc98d84a4b 100644 --- a/build-tools/scripts/Ndk.projitems.in +++ b/build-tools/scripts/Ndk.projitems.in @@ -11,8 +11,8 @@ @NDK_X86_API_NET@ @NDK_X86_64_API@ @NDK_X86_64_API_NET@ - @NDK_ARM64_V8A_API_NON_MONO@ - @NDK_X86_64_API_NON_MONO@ + @NDK_ARM64_V8A_API_NON_MONO@ + @NDK_X86_64_API_NON_MONO@ @@ -32,7 +32,7 @@ Condition=" $(AndroidSupportedTargetJitAbisForConditionalChecks.Contains (':arm64-v8a:')) "> $(AndroidNdkApiLevel_ArmV8a) $(AndroidNdkApiLevel_Arm64) - $(AndroidNdkAPiLevelNonMono_Arm64) + $(AndroidNdkApiLevelNonMono_Arm64) android-arm64 True True @@ -55,7 +55,7 @@ Condition=" $(AndroidSupportedTargetJitAbisForConditionalChecks.Contains (':x86_64:')) "> $(AndroidNdkApiLevel_X86_64) $(AndroidNdkApiLevel_X64) - $(AndroidNdkAPiLevelNonMono_X64) + $(AndroidNdkApiLevelNonMono_X64) android-x64 True True diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets index e27e197ef52..047f37ba58f 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets @@ -150,10 +150,10 @@ <_XACommonPropsReplacement Include="@NDK_PKG_REVISION@=26.3.11579264" /> <_XACommonPropsReplacement Include="@NDK_ARM64_V8A_API@=$(AndroidNdkApiLevel_ArmV8a)" /> - <_XACommonPropsReplacement Include="@NDK_ARM64_V8A_API_NON_MONO@=$(AndroidNdkAPiLevelNonMono_Arm64)" /> + <_XACommonPropsReplacement Include="@NDK_ARM64_V8A_API_NON_MONO@=$(AndroidNdkApiLevelNonMono_Arm64)" /> <_XACommonPropsReplacement Include="@NDK_ARMEABI_V7_API@=$(AndroidNdkApiLevel_ArmV7a)" /> <_XACommonPropsReplacement Include="@NDK_X86_64_API@=$(AndroidNdkApiLevel_X86_64)" /> - <_XACommonPropsReplacement Include="@NDK_X86_64_API_NON_MONO@=$(AndroidNdkAPiLevelNonMono_X64)" /> + <_XACommonPropsReplacement Include="@NDK_X86_64_API_NON_MONO@=$(AndroidNdkApiLevelNonMono_X64)" /> <_XACommonPropsReplacement Include="@NDK_X86_API@=$(AndroidNdkApiLevel_X86)" /> <_XACommonPropsReplacement Include="@PACKAGE_VERSION_BUILD@=$(XAVersionCommitCount)" /> <_XACommonPropsReplacement Include="@PACKAGE_VERSION@=$(ProductVersion)" /> diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.props.in b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.props.in index 6f3e9f58235..02589ee9384 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.props.in +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.props.in @@ -35,25 +35,25 @@ true @NDK_ARM64_V8A_API_NON_MONO@ - @NDK_X86_64_API_NON_MONO@ + @NDK_X86_64_API_NON_MONO@ @NDK_ARMEABI_V7_API@ - + @NDK_ARM64_V8A_API@ - + @NDK_ARM64_V8A_API_NON_MONO@ @NDK_X86_API@ - + @NDK_X86_64_API@ - + @NDK_X86_64_API_NON_MONO@ From 3bff3383621be042b4b572f115c46a6a0ca549f7 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Mon, 16 Feb 2026 11:27:48 +0100 Subject: [PATCH 34/39] Fix NAOT builds on Windows --- .../Microsoft.Android.Sdk.NativeAOT.targets | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets index 89ace556366..aa3a31c529d 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets @@ -80,8 +80,25 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. <_NdkWrapperScriptExt Condition=" $([MSBuild]::IsOSPlatform('windows')) ">.cmd <_NdkSysrootDir>$(_AndroidNdkDirectory)toolchains/llvm/prebuilt/$(_NdkPrebuiltAbi)/sysroot/usr/lib/$(_NdkSysrootAbi)/ <_NdkBinDir>$(_AndroidNdkDirectory)toolchains/llvm/prebuilt/$(_NdkPrebuiltAbi)/bin/ - $(_NdkBinDir)clang++ - $(_NdkBinDir)clang++ + + + $(_NdkAbi)-linux-android$(_NDKApiLevel)-clang$(_NdkWrapperScriptExt) + $(_NdkAbi)-linux-android$(_NDKApiLevel)-clang$(_NdkWrapperScriptExt) llvm-objcopy @@ -106,18 +123,8 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. $(_OriginalSuppressTrimAnalysisWarnings) - <_XANativeAotHostName>libnaot-android.release-static-release.a - <_NDKApiLevel Condition=" '$(RuntimeIdentifier)' == 'android-arm64' ">$(AndroidNdkApiLevel_Arm64) - <_NDKArch Condition=" '$(RuntimeIdentifier)' == 'android-arm64' ">aarch64 - <_NDKApiLevel Condition=" '$(RuntimeIdentifier)' == 'android-x64' ">$(AndroidNdkAPiLevel_X64) - <_NDKArch Condition=" '$(RuntimeIdentifier)' == 'android-x64' ">x86_64 - - From 6f1f90dd30554aae0626dfd537c298bccfbd0a07 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Mon, 16 Feb 2026 11:33:04 +0100 Subject: [PATCH 35/39] Update comments --- .../targets/Microsoft.Android.Sdk.NativeAOT.targets | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets index aa3a31c529d..08a50677aa4 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets @@ -91,11 +91,18 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. confused by the forward slash in the path (even though it's a valid path separator character on Windows) and signal the following error: - EXEC : error : Invalid pattern is specified in "path:pattern". + SetupOSSpecificProps: + where /Q "C:\Android\android-sdk\ndk\29.0.14206865\toolchains/llvm/prebuilt/windows-x86_64/bin/clang++" + 1>EXEC : error : Invalid pattern is specified in "path:pattern". + The command "where /Q "C:\Android\android-sdk\ndk\29.0.14206865\toolchains/llvm/prebuilt/windows-x86_64/bin/clang++"" exited with code 2. This could be fixed by making sure $(_NdkBinDir) path is fixed-up to use the canonical form of path separation for the host OS, but it's simpler to just use the NDK-specific compiler/linker wrapper script name. + + Also, do NOT use `clang++` here because it will link the dynamic libc++ library into the + application. If, for some reason, `clang++` has to be used, `` need to be + added to pass `-nostdlib` to it. --> $(_NdkAbi)-linux-android$(_NDKApiLevel)-clang$(_NdkWrapperScriptExt) $(_NdkAbi)-linux-android$(_NDKApiLevel)-clang$(_NdkWrapperScriptExt) From 31453ad72435227dfed04c9b0b082f075d27038b Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Mon, 16 Feb 2026 12:04:32 +0100 Subject: [PATCH 36/39] Work around a bug in NativeAOT targets --- .../Microsoft.Android.Sdk.NativeAOT.targets | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets index 08a50677aa4..03bbacb24c0 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets @@ -59,6 +59,21 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. + + + + + + + + @@ -130,6 +145,7 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. $(_OriginalSuppressTrimAnalysisWarnings) + <_XANativeAotHostName>libnaot-android.release-static-release.a From 683b104efcfaa37defb617daf3b46697f5a60f34 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Wed, 18 Feb 2026 09:30:57 +0100 Subject: [PATCH 37/39] Fix after rebase From 228583346535b0dd47dba41ccac9daffa283dec8 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 19 Feb 2026 09:48:38 +0100 Subject: [PATCH 38/39] Really...? --- src/native/nativeaot/host/bridge-processing.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/native/nativeaot/host/bridge-processing.cc b/src/native/nativeaot/host/bridge-processing.cc index 30bf0f948a0..52dcf3ec8c0 100644 --- a/src/native/nativeaot/host/bridge-processing.cc +++ b/src/native/nativeaot/host/bridge-processing.cc @@ -24,7 +24,7 @@ void BridgeProcessing::naot_initialize_on_runtime_init (JNIEnv *env) noexcept constexpr auto ABSENT = "absent"sv; constexpr auto PRESENT = "present"sv; - // This is fugly, but more type safe than printf format parsing + // This is ugly, but more type safe than printf format parsing std::string err_msg { "Failed to find GCUserPeerable method(s): jiAddManagedReference ("sv }; err_msg.append (GCUserPeerable_jiAddManagedReference == nullptr ? ABSENT : PRESENT); err_msg.append ("); jiClearManagedReferences ("sv); From d1c06fdd899ad281f057b5b61e5240f7efcd2a4b Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Tue, 24 Feb 2026 10:11:36 +0100 Subject: [PATCH 39/39] Fix after rebase --- .../BuildReleaseArm64SimpleDotNet.MonoVM.apkdesc | 12 ++++++------ .../BuildReleaseArm64XFormsDotNet.MonoVM.apkdesc | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.MonoVM.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.MonoVM.apkdesc index 51e9f4a845f..c8e07661c0b 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.MonoVM.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.MonoVM.apkdesc @@ -14,10 +14,10 @@ "Size": 88056 }, "lib/arm64-v8a/lib_Mono.Android.dll.so": { - "Size": 116536 + "Size": 117184 }, "lib/arm64-v8a/lib_Mono.Android.Runtime.dll.so": { - "Size": 23800 + "Size": 26328 }, "lib/arm64-v8a/lib_System.Console.dll.so": { "Size": 24416 @@ -26,7 +26,7 @@ "Size": 25496 }, "lib/arm64-v8a/lib_System.Private.CoreLib.dll.so": { - "Size": 633856 + "Size": 633928 }, "lib/arm64-v8a/lib_System.Runtime.dll.so": { "Size": 20232 @@ -44,7 +44,7 @@ "Size": 36616 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 1466544 + "Size": 1386232 }, "lib/arm64-v8a/libmonosgen-2.0.so": { "Size": 3123608 @@ -62,7 +62,7 @@ "Size": 165536 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 19608 + "Size": 19640 }, "META-INF/BNDLTOOL.RSA": { "Size": 1223 @@ -98,5 +98,5 @@ "Size": 1904 } }, - "PackageSize": 3283477 + "PackageSize": 3267093 } \ No newline at end of file diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.MonoVM.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.MonoVM.apkdesc index 91ab34c9fc0..fd8f2a40c19 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.MonoVM.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.MonoVM.apkdesc @@ -35,13 +35,13 @@ "Size": 25424 }, "lib/arm64-v8a/lib_Java.Interop.dll.so": { - "Size": 96656 + "Size": 96664 }, "lib/arm64-v8a/lib_Mono.Android.dll.so": { - "Size": 540296 + "Size": 541152 }, "lib/arm64-v8a/lib_Mono.Android.Runtime.dll.so": { - "Size": 23800 + "Size": 26328 }, "lib/arm64-v8a/lib_mscorlib.dll.so": { "Size": 21464 @@ -116,7 +116,7 @@ "Size": 27040 }, "lib/arm64-v8a/lib_System.Private.CoreLib.dll.so": { - "Size": 994584 + "Size": 994736 }, "lib/arm64-v8a/lib_System.Private.DataContractSerialization.dll.so": { "Size": 217984 @@ -239,7 +239,7 @@ "Size": 36616 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 1466544 + "Size": 1386232 }, "lib/arm64-v8a/libmonosgen-2.0.so": { "Size": 3123608 @@ -257,7 +257,7 @@ "Size": 165536 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 350584 + "Size": 350616 }, "META-INF/androidx.activity_activity.version": { "Size": 6 @@ -2480,5 +2480,5 @@ "Size": 812848 } }, - "PackageSize": 11152445 + "PackageSize": 11131965 } \ No newline at end of file