From e7dd7d3cdc624cb0dcd24dcb22869a699b624499 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Mon, 17 Mar 2025 12:52:25 +0100 Subject: [PATCH 01/20] 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 | 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 f767821a506..8508a68781c 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 = "28c"; - public const string AndroidNdkPkgRevision = "28.2.13676358"; + public const string AndroidNdkVersion = "29-beta1"; + public const string AndroidNdkPkgRevision = "29.0.13113456"; public const int NdkMinimumAPI = 21; public const int NdkMinimumAPILegacy32 = 21; From 29c32380687c1b675504866b32b8b7d7fb089ba8 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Wed, 2 Jul 2025 09:42:40 +0200 Subject: [PATCH 02/20] 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 8508a68781c..3cc50722bee 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 a9b7024ff2e5c5ada891de4d804c8102823c433d Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 7 Aug 2025 16:31:47 +0200 Subject: [PATCH 03/20] 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 3cc50722bee..24759d0d80e 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 7f6323049970f4d89b6e65dad9d2ad0d5dc93796 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 16 Oct 2025 09:15:13 +0200 Subject: [PATCH 04/20] Fix after rebase From eea721db257cb37a8df7d0cf7cbb89122ec3c721 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 16 Oct 2025 09:16:11 +0200 Subject: [PATCH 05/20] 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 24759d0d80e..8bc4193eb15 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 d7c8c01cb1e50cebac269d92b93a4e59c9d979e6 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Fri, 9 Jan 2026 14:30:09 +0100 Subject: [PATCH 06/20] Disable linking against libc++ Step 1. Cause breakage --- .../targets/Microsoft.Android.Sdk.NativeAOT.targets | 4 ++-- 1 file changed, 2 insertions(+), 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 ba0ae385391..f10deb35148 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 @@ -121,8 +121,8 @@ 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 dfcf63f708691547a2ecc39464494f0a708c8a33 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Fri, 9 Jan 2026 20:46:10 +0100 Subject: [PATCH 07/20] 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 436bac2feec273462332fb9763465fdc2a4331f7 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Mon, 12 Jan 2026 18:45:47 +0100 Subject: [PATCH 08/20] Still broken, baby steps --- src-ThirdParty/llvm/libcxx/src/new.cpp | 226 ---------------------- src/native/CMakeLists.txt | 17 +- src/native/CMakePresets.json.in | 40 ++-- src/native/native.targets | 13 +- 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, 240 insertions(+), 252 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 3b65133c34a..e0e587ce728 100644 --- a/src/native/CMakeLists.txt +++ b/src/native/CMakeLists.txt @@ -155,7 +155,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") # @@ -221,13 +229,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 # @@ -243,7 +244,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 79a3ac46282..9a163e9e286 100644 --- a/src/native/CMakePresets.json.in +++ b/src/native/CMakePresets.json.in @@ -54,25 +54,23 @@ }, { - "name": "analyzers-common", + "name": "nativeaot-default-common", "hidden": true, "inherits": "common", "cacheVariables": { - "ANDROID_STL": "c++_static", - "ANDROID_CPP_FEATURES": "rtti exceptions" + "ANDROID_STL": "none", + "ANDROID_CPP_FEATURES": "no-rtti no-exceptions" } }, { - "name": "default-debug", - "hidden": true, - "inherits": ["default-common", "common-debug"] - }, - - { - "name": "default-release", + "name": "analyzers-common", "hidden": true, - "inherits": ["default-common", "common-release"] + "inherits": "common", + "cacheVariables": { + "ANDROID_STL": "c++_static", + "ANDROID_CPP_FEATURES": "rtti exceptions" + } }, { @@ -196,11 +194,21 @@ "inherits": ["default-common", "common-debug", "common-arm64-v8a"] }, + { + "name": "nativeaot-default-debug-arm64-v8a", + "inherits": ["nativeaot-default-common", "common-debug", "common-arm64-v8a"] + }, + { "name": "default-release-arm64-v8a", "inherits": ["default-common", "common-release", "common-arm64-v8a"] }, + { + "name": "nativeaot-default-release-arm64-v8a", + "inherits": ["nativeaot-default-common", "common-release", "common-arm64-v8a"] + }, + { "name": "analyzers-debug-arm64-v8a", "hidden": true, @@ -284,11 +292,21 @@ "inherits": ["default-common", "common-debug", "common-x86_64"] }, + { + "name": "nativeaot-default-debug-x86_64", + "inherits": ["nativeaot-default-common", "common-debug", "common-x86_64"] + }, + { "name": "default-release-x86_64", "inherits": ["default-common", "common-release", "common-x86_64"] }, + { + "name": "nativeaot-default-release-x86_64", + "inherits": ["nativeaot-default-common", "common-release", "common-x86_64"] + }, + { "name": "analyzers-debug-x86_64", "hidden": true, diff --git a/src/native/native.targets b/src/native/native.targets index 9cd05f59b3a..90797d35cee 100644 --- a/src/native/native.targets +++ b/src/native/native.targets @@ -118,6 +118,7 @@ Inputs="@(_ConfigureRuntimesInputs)" Outputs="@(_ConfigureRuntimesOutputs)"> + <_PresetPrefix Condition=" '$(CMakeRuntimeFlavor)' == 'NativeAOT' ">nativeaot- <_NoInline Condition=" '$(DoNotInlineMonodroid)' == 'true' ">-DDONT_INLINE=ON <_NoStrip Condition=" '$(DoNotStripMonodroid)' == 'true' ">-DSTRIP_DEBUG=OFF <_LocalDotNetRuntimePath Condition=" '$(CLRLocalRuntimePath)' != '' And '$(CMakeRuntimeFlavor)' == 'CoreCLR' ">-DLOCAL_CORECLR_PATH="$(CLRLocalRuntimePath)" @@ -128,13 +129,13 @@ <_ConfigureRuntimeCommands Include="@(AndroidSupportedTargetJitAbi)"> $(CmakePath) - --preset default-debug-%(AndroidSupportedTargetJitAbi.Identity) $(_CmakeAndroidFlags) + --preset $(_PresetPrefix)default-debug-%(AndroidSupportedTargetJitAbi.Identity) $(_CmakeAndroidFlags) $(FlavorIntermediateOutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)-Debug <_ConfigureRuntimeCommands Include="@(AndroidSupportedTargetJitAbi)"> $(CmakePath) - --preset default-release-%(AndroidSupportedTargetJitAbi.Identity) $(_CmakeAndroidFlags) + --preset $(_PresetPrefix)default-release-%(AndroidSupportedTargetJitAbi.Identity) $(_CmakeAndroidFlags) $(FlavorIntermediateOutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)-Release @@ -142,25 +143,25 @@ <_ConfigureRuntimeCommands Include="@(AndroidSupportedTargetJitAbi)"> $(CmakePath) - --preset asan-debug-%(AndroidSupportedTargetJitAbi.Identity) $(_CmakeAndroidFlags) + --preset $(_PresetPrefix)asan-debug-%(AndroidSupportedTargetJitAbi.Identity) $(_CmakeAndroidFlags) $(FlavorIntermediateOutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)-asan-Debug <_ConfigureRuntimeCommands Include="@(AndroidSupportedTargetJitAbi)"> $(CmakePath) - --preset asan-release-%(AndroidSupportedTargetJitAbi.Identity) $(_CmakeAndroidFlags) + --preset $(_PresetPrefix)asan-release-%(AndroidSupportedTargetJitAbi.Identity) $(_CmakeAndroidFlags) $(FlavorIntermediateOutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)-asan-Release <_ConfigureRuntimeCommands Include="@(AndroidSupportedTargetJitAbi)"> $(CmakePath) - --preset ubsan-debug-%(AndroidSupportedTargetJitAbi.Identity) $(_CmakeAndroidFlags) + --preset $(_PresetPrefix)ubsan-debug-%(AndroidSupportedTargetJitAbi.Identity) $(_CmakeAndroidFlags) $(FlavorIntermediateOutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)-ubsan-Debug <_ConfigureRuntimeCommands Include="@(AndroidSupportedTargetJitAbi)"> $(CmakePath) - --preset ubsan-release-%(AndroidSupportedTargetJitAbi.Identity) $(_CmakeAndroidFlags) + --preset $(_PresetPrefix)ubsan-release-%(AndroidSupportedTargetJitAbi.Identity) $(_CmakeAndroidFlags) $(FlavorIntermediateOutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)-ubsan-Release 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 a2373449e63ad63bc62916b53e7667dea0b065d0 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Mon, 12 Jan 2026 19:16:22 +0100 Subject: [PATCH 09/20] 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 022fa2c38178cf5ce3119985cb4b66c9c9e1b76c Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Mon, 12 Jan 2026 19:21:36 +0100 Subject: [PATCH 10/20] 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 e2e6e5f48f3b72c423d21c8e68413943370feb6c Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Tue, 13 Jan 2026 11:11:46 +0100 Subject: [PATCH 11/20] 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 90797d35cee..e4a1be4b314 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" /> @@ -127,7 +127,7 @@ - <_ConfigureRuntimeCommands Include="@(AndroidSupportedTargetJitAbi)"> + <_ConfigureRuntimeCommands Include="@(AndroidSupportedTargetJitAbi)" Condition=" '$(CMakeRuntimeFlavor)' != 'NativeAOT' "> $(CmakePath) --preset $(_PresetPrefix)default-debug-%(AndroidSupportedTargetJitAbi.Identity) $(_CmakeAndroidFlags) $(FlavorIntermediateOutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)-Debug @@ -248,6 +248,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 313af37ab6ea691f0341e02328cab6b86b1a7139 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Tue, 13 Jan 2026 18:54:37 +0100 Subject: [PATCH 12/20] 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 62ef015464240b9c2c6d01662184b1d93bb62037 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Tue, 13 Jan 2026 18:55:49 +0100 Subject: [PATCH 13/20] 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 9e195512637202dafe7a1bd501241553b147cb72 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Wed, 14 Jan 2026 11:20:32 +0100 Subject: [PATCH 14/20] Fix 2x oops --- src/native/common/include/runtime-base/strings.hh | 1 + src/native/native.targets | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) 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) diff --git a/src/native/native.targets b/src/native/native.targets index e4a1be4b314..4f3085d6811 100644 --- a/src/native/native.targets +++ b/src/native/native.targets @@ -231,7 +231,6 @@ - <_BuildAndroidRuntimesOutputs Include="@(AndroidSupportedTargetJitAbi->'$(OutputPath)\%(AndroidRID)\libnaot-android.debug.so')" /> <_BuildAndroidRuntimesOutputs Include="@(AndroidSupportedTargetJitAbi->'$(OutputPath)\%(AndroidRID)\libnaot-android.release.so')" /> From 2dbf9b035dae81a190577a72b7095e8d13c199d2 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Wed, 14 Jan 2026 17:39:01 +0100 Subject: [PATCH 15/20] 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 6ca63b0f3d00d6dbf492f45fdf262d16127cf42d Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 15 Jan 2026 12:08:56 +0100 Subject: [PATCH 16/20] 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 3cdeeea47d85b24961230fc5673fd9a2e75bd870 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 15 Jan 2026 13:54:52 +0100 Subject: [PATCH 17/20] 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 6777243906ba092c01c353b32c436c6931909cb7 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Fri, 16 Jan 2026 17:35:24 +0100 Subject: [PATCH 18/20] 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 d450f8c581e83f385071f76cb3132b07921444ad Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Mon, 19 Jan 2026 14:08:07 +0100 Subject: [PATCH 19/20] 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 6dce9c91a0106d61e5dcb217f15325537affc1f8 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Mon, 19 Jan 2026 15:48:29 +0100 Subject: [PATCH 20/20] 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)