diff --git a/.gitmodules b/.gitmodules index 20bbd195b37..06bad4aef97 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,6 +22,9 @@ path = external/xamarin-android-tools url = https://github.com/xamarin/xamarin-android-tools branch = main +[submodule "external/libunwind"] + path = external/libunwind + url = https://github.com/libunwind/libunwind.git [submodule "external/xxHash"] path = external/xxHash url = https://github.com/Cyan4973/xxHash.git diff --git a/Configuration.props b/Configuration.props index 932f8b0a107..68373be4123 100644 --- a/Configuration.props +++ b/Configuration.props @@ -104,6 +104,8 @@ True $(MSBuildThisFileDirectory)external\opentk $(MSBuildThisFileDirectory)external\sqlite + $(MSBuildThisFileDirectory)external\libunwind + $(BootstrapOutputDirectory)\libunwind $(MSBuildThisFileDirectory) $(MSBuildThisFileDirectory)src-ThirdParty\ armeabi-v7a;x86 @@ -144,6 +146,8 @@ $([System.IO.Path]::GetFullPath ('$(JavaInteropSourceDirectory)')) $([System.IO.Path]::GetFullPath ('$(MonoSourceDirectory)')) $([System.IO.Path]::GetFullPath ('$(SqliteSourceDirectory)')) + $([System.IO.Path]::GetFullPath ('$(LibUnwindSourceDirectory)')) + $([System.IO.Path]::GetFullPath ('$(LibUnwindGeneratedHeadersDirectory)')) $([System.IO.Path]::GetFullPath ('$(OpenTKSourceDirectory)')) net8.0 diff --git a/TheoriesToTest.txt b/TheoriesToTest.txt new file mode 100644 index 00000000000..7517af47f8a --- /dev/null +++ b/TheoriesToTest.txt @@ -0,0 +1,4 @@ +* See if it's a threading issue - add full locking to `get_function_pointer` instead of atomics that we use now +* See if it's an ALC issue - call `get_function_pointer` every time and check the returned pointer value +* Implement Jon's idea to always have the "old" dynamic registration stuff in JCW and only use it when some flag + is set and marshal methods are active. diff --git a/Xamarin.Android.sln b/Xamarin.Android.sln index c85d197a5aa..b46ca2898aa 100644 --- a/Xamarin.Android.sln +++ b/Xamarin.Android.sln @@ -47,6 +47,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Java.Interop.Tools.Diagnost EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Java.Interop.Tools.Cecil", "external\Java.Interop\src\Java.Interop.Tools.Cecil\Java.Interop.Tools.Cecil.csproj", "{D48EE8D0-0A0A-4493-AEF5-DAF5F8CF86AD}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "libunwind", "src\libunwind-xamarin\libunwind-xamarin.csproj", "{F8E4961B-C427-47F9-92D6-0BEB5B76B3D7}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "monodroid", "src\monodroid\monodroid.csproj", "{53EE4C57-1C03-405A-8243-8DA539546C88}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{CAB438D8-B0F5-4AF0-BEBD-9E2ADBD7B483}" diff --git a/build-tools/cmake/xa_common.cmake b/build-tools/cmake/xa_common.cmake index d732318eb05..250473e2633 100644 --- a/build-tools/cmake/xa_common.cmake +++ b/build-tools/cmake/xa_common.cmake @@ -1 +1,143 @@ -set(CMAKE_OSX_DEPLOYMENT_TARGET 10.12) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS OFF) + +include("${CMAKE_ANDROID_NDK}/build/cmake/abis.cmake") + +if(CMAKE_BUILD_TYPE STREQUAL Debug) + set(DEBUG_BUILD True) +else() + set(DEBUG_BUILD False) +endif() + +set(XA_NO_INLINE "$ENV{XA_NO_INLINE}") +if(XA_NO_INLINE) + set(DONT_INLINE_DEFAULT ON) +else() + set(DONT_INLINE_DEFAULT OFF) +endif() + +set(XA_NO_STRIP "$ENV{XA_NO_STRIP}") +if(XA_NO_STRIP OR DEBUG_BUILD) + set(STRIP_DEBUG_DEFAULT OFF) +endif() + +option(ENABLE_CLANG_ASAN "Enable the clang AddressSanitizer support" OFF) +option(ENABLE_CLANG_UBSAN "Enable the clang UndefinedBehaviorSanitizer support" OFF) + +if(ENABLE_CLANG_ASAN OR ENABLE_CLANG_UBSAN) + set(STRIP_DEBUG_DEFAULT OFF) + set(ANALYZERS_ENABLED ON) +else() + if(NOT XA_NO_STRIP) + set(STRIP_DEBUG_DEFAULT ON) + endif() + set(ANALYZERS_ENABLED OFF) +endif() + +option(COMPILER_DIAG_COLOR "Show compiler diagnostics/errors in color" ON) +option(STRIP_DEBUG "Strip debugging information when linking" ${STRIP_DEBUG_DEFAULT}) +option(DISABLE_DEBUG "Disable the built-in debugging code" OFF) +option(USE_CCACHE "Use ccache, if found, to speed up recompilation" ON) +option(DONT_INLINE "Do not inline any functions which are usually inlined, to get better stack traces" ${DONT_INLINE_DEFAULT}) + +if(USE_CCACHE) + if(CMAKE_CXX_COMPILER MATCHES "/ccache/") + message(STATUS "ccache: compiler already uses ccache") + else() + find_program(CCACHE ccache) + if(CCACHE) + set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE}") + set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE}") + message(STATUS "ccache: compiler will be lauched with ${CCACHE}") + endif() + endif() +endif() + +if(ANDROID_STL STREQUAL none) + set(USES_LIBSTDCPP False) +else() + set(USES_LIBSTDCPP True) +endif() + +# +# General config +# +if(CMAKE_HOST_SYSTEM_NAME STREQUAL Linux) + set(IS_LINUX True) +else() + set(IS_LINUX False) +endif() + +if(CMAKE_HOST_SYSTEM_NAME STREQUAL Darwin) + set(IS_MACOS True) +else() + set(IS_MACOS False) +endif() + +set(XA_BUILD_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../bin/Build${XA_BUILD_CONFIGURATION}") +include("${XA_BUILD_DIR}/xa_build_configuration.cmake") + +# +# Paths +# +if(ANDROID_ABI MATCHES "^arm64-v8a") + set(NET_RUNTIME_DIR "${NETCORE_APP_RUNTIME_DIR_ARM64}") + set(TOOLCHAIN_TRIPLE "${NDK_ABI_arm64-v8a_TRIPLE}") +elseif(ANDROID_ABI MATCHES "^armeabi-v7a") + set(NET_RUNTIME_DIR "${NETCORE_APP_RUNTIME_DIR_ARM}") + set(TOOLCHAIN_TRIPLE "${NDK_ABI_armeabi-v7a_TRIPLE}") +elseif(ANDROID_ABI MATCHES "^x86_64") + set(NET_RUNTIME_DIR "${NETCORE_APP_RUNTIME_DIR_X86_64}") + set(TOOLCHAIN_TRIPLE "${NDK_ABI_x86_64_TRIPLE}") +elseif(ANDROID_ABI MATCHES "^x86") + set(NET_RUNTIME_DIR "${NETCORE_APP_RUNTIME_DIR_X86}") + set(TOOLCHAIN_TRIPLE "${NDK_ABI_x86_TRIPLE}") +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(SHARED_SOURCES_DIR "${REPO_ROOT_DIR}/src/native/shared") +set(TRACING_SOURCES_DIR "${REPO_ROOT_DIR}/src/native/tracing") +# +# Include directories +# +include_directories(SYSTEM ${CMAKE_SYSROOT}/usr/include/c++/v1/) +include_directories(SYSTEM "${NET_RUNTIME_DIR}/native/include/mono-2.0") +include_directories("${JAVA_INTEROP_SRC_PATH}") +include_directories("${SHARED_SOURCES_DIR}") +include_directories("${TRACING_SOURCES_DIR}") + +# +# Compiler defines +# +add_compile_definitions(XA_VERSION="${XA_VERSION}") +add_compile_definitions(_REENTRANT) +add_compile_definitions(PLATFORM_ANDROID) + +if(DEBUG_BUILD AND NOT DISABLE_DEBUG) + add_compile_definitions(DEBUG) +endif() + +if(ANDROID_ABI MATCHES "^(arm64-v8a|x86_64)") + add_compile_definitions(ANDROID64) +endif() + +if (ANDROID_NDK_MAJOR LESS 20) + add_compile_definitions(__ANDROID_API_Q__=29) +endif() + +# +# Shared sources +# +set(XA_SHARED_SOURCES + ${SHARED_SOURCES_DIR}/helpers.cc + ${SHARED_SOURCES_DIR}/new_delete.cc +) diff --git a/build-tools/cmake/xa_macros.cmake b/build-tools/cmake/xa_macros.cmake index df23bdedcd0..69759acf363 100644 --- a/build-tools/cmake/xa_macros.cmake +++ b/build-tools/cmake/xa_macros.cmake @@ -200,7 +200,8 @@ function(xa_common_prepare) -fstack-protector-strong -fstrict-return -fno-strict-aliasing - -ffunction-sections + -fno-function-sections + -fno-data-sections -funswitch-loops -Wa,-noexecstack -fPIC diff --git a/build-tools/cmake/xa_preamble.cmake b/build-tools/cmake/xa_preamble.cmake new file mode 100644 index 00000000000..d28fc5e10c4 --- /dev/null +++ b/build-tools/cmake/xa_preamble.cmake @@ -0,0 +1,7 @@ +set(CMAKE_OSX_DEPLOYMENT_TARGET 10.12) + +# +# Read product version +# +file(STRINGS "../../Directory.Build.props" XA_PRODUCT_VERSION_XML REGEX "^[ \t]*(.*)") +string(REGEX REPLACE "^[ \t]*(.*)" "\\1" XA_VERSION "${XA_PRODUCT_VERSION_XML}") diff --git a/build-tools/create-packs/Microsoft.Android.Runtime.proj b/build-tools/create-packs/Microsoft.Android.Runtime.proj index 8982eb6f911..41fbd68991d 100644 --- a/build-tools/create-packs/Microsoft.Android.Runtime.proj +++ b/build-tools/create-packs/Microsoft.Android.Runtime.proj @@ -41,6 +41,8 @@ projects that use the Microsoft.Android framework in .NET 6+. <_AndroidRuntimePackAssets Include="$(MicrosoftAndroidSdkOutDir)lib\$(AndroidRID)\libmono-android.debug.so" /> <_AndroidRuntimePackAssets Include="$(MicrosoftAndroidSdkOutDir)lib\$(AndroidRID)\libmono-android.release.so" /> <_AndroidRuntimePackAssets Include="$(MicrosoftAndroidSdkOutDir)lib\$(AndroidRID)\libxamarin-debug-app-helper.so" /> + <_AndroidRuntimePackAssets Include="$(MicrosoftAndroidSdkOutDir)lib\$(AndroidRID)\libmarshal-methods-tracing.a" /> + <_AndroidRuntimePackAssets Include="$(MicrosoftAndroidSdkOutDir)lib\$(AndroidRID)\libxamarin-native-tracing.so" /> diff --git a/build-tools/installers/create-installers.targets b/build-tools/installers/create-installers.targets index daa577014a5..34551f5c26f 100644 --- a/build-tools/installers/create-installers.targets +++ b/build-tools/installers/create-installers.targets @@ -172,12 +172,20 @@ <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)ManifestOverlays\Timing.xml" /> <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-arm64\libc.so" /> <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-arm64\libm.so" /> + <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-arm64\libdl.so" /> + <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-arm64\liblog.so" /> <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-arm\libc.so" /> <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-arm\libm.so" /> + <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-arm\libdl.so" /> + <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-arm\liblog.so" /> <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x64\libc.so" /> <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x64\libm.so" /> + <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x64\libdl.so" /> + <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x64\liblog.so" /> <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x86\libc.so" /> <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x86\libm.so" /> + <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x86\libdl.so" /> + <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x86\liblog.so" /> <_MSBuildTargetsSrcFiles Include="$(MSBuildTargetsSrcDir)\Xamarin.Android.AvailableItems.targets" /> diff --git a/build-tools/scripts/cmake-android.props b/build-tools/scripts/cmake-android.props new file mode 100644 index 00000000000..6335db11af8 --- /dev/null +++ b/build-tools/scripts/cmake-android.props @@ -0,0 +1,6 @@ + + + + <_CmakeAndroidFlags>--debug-output -GNinja -DCMAKE_MAKE_PROGRAM="$(NinjaPath)" -DXA_BUILD_CONFIGURATION=$(Configuration) -DXA_LIB_TOP_DIR=$(MicrosoftAndroidSdkOutDir) -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DMONO_PATH="$(MonoSourceFullPath)" -DANDROID_STL="none" -DANDROID_CPP_FEATURES="no-rtti no-exceptions" -DANDROID_TOOLCHAIN=clang -DCMAKE_TOOLCHAIN_FILE="$(AndroidNdkDirectory)/build/cmake/android.toolchain.cmake" -DANDROID_NDK=$(AndroidNdkDirectory) + + diff --git a/build-tools/xaprepare/xaprepare/Application/KnownProperties.cs b/build-tools/xaprepare/xaprepare/Application/KnownProperties.cs index 2d9fd7efcfa..32f56aa57ff 100644 --- a/build-tools/xaprepare/xaprepare/Application/KnownProperties.cs +++ b/build-tools/xaprepare/xaprepare/Application/KnownProperties.cs @@ -38,6 +38,8 @@ static class KnownProperties public const string JavaInteropFullPath = "JavaInteropFullPath"; public const string JavaSdkDirectory = "JavaSdkDirectory"; public const string JdkIncludePath = "JdkIncludePath"; + public const string LibUnwindGeneratedHeadersFullPath = nameof (LibUnwindGeneratedHeadersFullPath); + public const string LibUnwindSourceFullPath = nameof (LibUnwindSourceFullPath); public const string LibZipSourceFullPath = "LibZipSourceFullPath"; public const string ManagedRuntime = "ManagedRuntime"; public const string MicrosoftAndroidSdkOutDir = "MicrosoftAndroidSdkOutDir"; diff --git a/build-tools/xaprepare/xaprepare/Application/Properties.Defaults.cs.in b/build-tools/xaprepare/xaprepare/Application/Properties.Defaults.cs.in index 6e59af3ab6e..39b44422eb7 100644 --- a/build-tools/xaprepare/xaprepare/Application/Properties.Defaults.cs.in +++ b/build-tools/xaprepare/xaprepare/Application/Properties.Defaults.cs.in @@ -42,6 +42,8 @@ namespace Xamarin.Android.Prepare properties.Add (KnownProperties.JavaInteropFullPath, StripQuotes (@"@JavaInteropFullPath@")); properties.Add (KnownProperties.JavaSdkDirectory, StripQuotes (@"@JavaSdkDirectory@")); properties.Add (KnownProperties.JdkIncludePath, StripQuotes (@"@JdkIncludePath@")); + properties.Add (KnownProperties.LibUnwindGeneratedHeadersFullPath, StripQuotes (@"@LibUnwindGeneratedHeadersFullPath@")); + properties.Add (KnownProperties.LibUnwindSourceFullPath, StripQuotes (@"@LibUnwindSourceFullPath")); properties.Add (KnownProperties.LibZipSourceFullPath, StripQuotes (@"@LibZipSourceFullPath@")); properties.Add (KnownProperties.ManagedRuntime, StripQuotes (@"@ManagedRuntime@")); properties.Add (KnownProperties.MicrosoftAndroidSdkOutDir, StripQuotes (@"@MicrosoftAndroidSdkOutDir@")); diff --git a/build-tools/xaprepare/xaprepare/xaprepare.targets b/build-tools/xaprepare/xaprepare/xaprepare.targets index 4c8e3be8906..dc8465d148b 100644 --- a/build-tools/xaprepare/xaprepare/xaprepare.targets +++ b/build-tools/xaprepare/xaprepare/xaprepare.targets @@ -75,6 +75,7 @@ + diff --git a/external/libunwind b/external/libunwind new file mode 160000 index 00000000000..9cc4d98b22a --- /dev/null +++ b/external/libunwind @@ -0,0 +1 @@ +Subproject commit 9cc4d98b22ae57bc1d8c253988feb85d4298a634 diff --git a/java.interop-tracing.diff b/java.interop-tracing.diff new file mode 100644 index 00000000000..bb00e359fa4 --- /dev/null +++ b/java.interop-tracing.diff @@ -0,0 +1,213 @@ +diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs +index 76f2bfc0..7a57f6b2 100644 +--- a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs ++++ b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs +@@ -133,6 +133,7 @@ namespace Java.Interop.Tools.JavaCallableWrappers { + + JavaCallableWrapperGenerator (TypeDefinition type, string? outerType, Action log, IMetadataResolver resolver, JavaCallableMethodClassifier? methodClassifier = null) + { ++ Console.WriteLine ($"JCWG: processing {type}"); + this.methodClassifier = methodClassifier; + this.type = type; + this.log = log; +@@ -224,6 +225,8 @@ namespace Java.Interop.Tools.JavaCallableWrappers { + curCtors = new List (); + AddConstructors (ctorTypes [i], outerType, baseCtors, curCtors, false); + } ++ ++ Console.WriteLine ($"JCWG: done processing {type}"); + } + + static void ExtractJavaNames (string jniName, out string package, out string type) +@@ -285,8 +288,10 @@ namespace Java.Interop.Tools.JavaCallableWrappers { + + void AddConstructor (MethodDefinition ctor, TypeDefinition type, string? outerType, List? baseCtors, List curCtors, bool onlyRegisteredOrExportedCtors, bool skipParameterCheck) + { ++ Console.WriteLine ($" JCWG: AddConstructor ({ctor}, {type}, ..., onlyRegisteredOrExportedCtors: {onlyRegisteredOrExportedCtors}, skipParameterCheck: {skipParameterCheck});"); + string managedParameters = GetManagedParameters (ctor, outerType); + if (!skipParameterCheck && (managedParameters == null || ctors.Any (c => c.ManagedParameters == managedParameters))) { ++ Console.WriteLine (" JCWG: skip #1"); + return; + } + +@@ -297,28 +302,38 @@ namespace Java.Interop.Tools.JavaCallableWrappers { + } + ctors.Add (new Signature (ctor, eattr, cache)); + curCtors.Add (ctor); ++ Console.WriteLine (" JCWG: add #1"); + return; + } + + RegisterAttribute rattr = GetMethodRegistrationAttributes (ctor).FirstOrDefault (); + if (rattr != null) { +- if (ctors.Any (c => c.JniSignature == rattr.Signature)) ++ if (ctors.Any (c => c.JniSignature == rattr.Signature)) { ++ Console.WriteLine (" JCWG: skip #2"); + return; ++ } + ctors.Add (new Signature (ctor, rattr, managedParameters, outerType, cache)); + curCtors.Add (ctor); ++ Console.WriteLine (" JCWG: add #2"); + return; + } + +- if (onlyRegisteredOrExportedCtors) ++ if (onlyRegisteredOrExportedCtors) { ++ Console.WriteLine (" JCWG: skip #1"); + return; ++ } + + string? jniSignature = GetJniSignature (ctor, cache); + +- if (jniSignature == null) ++ if (jniSignature == null) { ++ Console.WriteLine (" JCWG: skip #3"); + return; ++ } + +- if (ctors.Any (c => c.JniSignature == jniSignature)) ++ if (ctors.Any (c => c.JniSignature == jniSignature)) { ++ Console.WriteLine (" JCWG: skip #4"); + return; ++ } + + if (baseCtors == null) { + throw new InvalidOperationException ("`baseCtors` should not be null!"); +@@ -327,11 +342,13 @@ namespace Java.Interop.Tools.JavaCallableWrappers { + if (baseCtors.Any (m => m.Parameters.AreParametersCompatibleWith (ctor.Parameters, cache))) { + ctors.Add (new Signature (".ctor", jniSignature, "", managedParameters, outerType, null)); + curCtors.Add (ctor); ++ Console.WriteLine (" JCWG: add #3"); + return; + } + if (baseCtors.Any (m => !m.HasParameters)) { + ctors.Add (new Signature (".ctor", jniSignature, "", managedParameters, outerType, "")); + curCtors.Add (ctor); ++ Console.WriteLine (" JCWG: add #4"); + return; + } + } +@@ -549,17 +566,17 @@ namespace Java.Interop.Tools.JavaCallableWrappers { + GenerateHeader (writer); + + bool needCtor = false; +- if (HasDynamicallyRegisteredMethods) { ++// if (HasDynamicallyRegisteredMethods) { + needCtor = true; + writer.WriteLine ("/** @hide */"); + writer.WriteLine ("\tpublic static final String __md_methods;"); +- } ++// } + + if (children != null) { + for (int i = 0; i < children.Count; i++) { +- if (!children[i].HasDynamicallyRegisteredMethods) { +- continue; +- } ++ // if (!children[i].HasDynamicallyRegisteredMethods) { ++ // continue; ++ // } + needCtor = true; + writer.Write ("\tstatic final String __md_"); + writer.Write (i + 1); +@@ -570,9 +587,9 @@ namespace Java.Interop.Tools.JavaCallableWrappers { + if (needCtor) { + writer.WriteLine ("\tstatic {"); + +- if (HasDynamicallyRegisteredMethods) { ++// if (HasDynamicallyRegisteredMethods) { + GenerateRegisterType (writer, this, "__md_methods"); +- } ++// } + + if (children != null) { + for (int i = 0; i < children.Count; ++i) { +@@ -687,9 +704,12 @@ namespace Java.Interop.Tools.JavaCallableWrappers { + + void GenerateBody (TextWriter sw) + { ++ Console.WriteLine ($" JCWG: GenerateBody for {name} (ctors size: {ctors.Count})"); + foreach (Signature ctor in ctors) { +- if (string.IsNullOrEmpty (ctor.Params) && JavaNativeTypeManager.IsApplication (type, cache)) ++ if (string.IsNullOrEmpty (ctor.Params) && JavaNativeTypeManager.IsApplication (type, cache)) { ++ Console.WriteLine ($" JCWG: skipping constructor generation; params empty? {string.IsNullOrEmpty (ctor.Params)}; is application? {JavaNativeTypeManager.IsApplication (type, cache)}"); + continue; ++ } + GenerateConstructor (ctor, sw); + } + +@@ -745,9 +765,9 @@ namespace Java.Interop.Tools.JavaCallableWrappers { + + void GenerateRegisterType (TextWriter sw, JavaCallableWrapperGenerator self, string field) + { +- if (!self.HasDynamicallyRegisteredMethods) { +- return; +- } ++ // if (!self.HasDynamicallyRegisteredMethods) { ++ // return; ++ // } + + sw.Write ("\t\t"); + sw.Write (field); +@@ -818,9 +838,13 @@ namespace Java.Interop.Tools.JavaCallableWrappers { + return jniName.Replace ('/', '.').Replace ('$', '.'); + } + +- bool CannotRegisterInStaticConstructor (TypeDefinition type) ++ bool CannotRegisterInStaticConstructor (TypeDefinition type, bool dolog = false) + { +- return JavaNativeTypeManager.IsApplication (type, cache) || JavaNativeTypeManager.IsInstrumentation (type, cache); ++ Console.WriteLine ($" JCWG: CannotRegisterInStaticConstructor ({type})"); ++ bool ret = JavaNativeTypeManager.IsApplication (type, cache) || JavaNativeTypeManager.IsInstrumentation (type, cache); ++ Console.WriteLine ($" JCWG: CannotRegisterInStaticConstructor for type {type}; ret == {ret}; IsApplication: {JavaNativeTypeManager.IsApplication (type, cache)}; IsInstrumentation: {JavaNativeTypeManager.IsInstrumentation (type, cache)}"); ++ ++ return ret; + } + + class Signature { +@@ -941,6 +965,8 @@ namespace Java.Interop.Tools.JavaCallableWrappers { + + void GenerateConstructor (Signature ctor, TextWriter sw) + { ++ Console.WriteLine ($" JCWG: GenerateConstructor for {name}"); ++ + // TODO: we only generate constructors so that Android types w/ no + // default constructor can be subclasses by our generated code. + // +diff --git a/tools/java-source-utils/.classpath b/tools/java-source-utils/.classpath +index 30230c1c..cbded110 100644 +--- a/tools/java-source-utils/.classpath ++++ b/tools/java-source-utils/.classpath +@@ -20,6 +20,12 @@ + + + ++ ++ ++ ++ ++ ++ + + + +diff --git a/tools/java-source-utils/.project b/tools/java-source-utils/.project +index 8337a1c5..613733c8 100644 +--- a/tools/java-source-utils/.project ++++ b/tools/java-source-utils/.project +@@ -20,4 +20,15 @@ + org.eclipse.jdt.core.javanature + org.eclipse.buildship.core.gradleprojectnature + ++ ++ ++ 1682588570543 ++ ++ 30 ++ ++ org.eclipse.core.resources.regexFilterMatcher ++ node_modules|.metadata|archetype-resources|META-INF/maven|__CREATED_BY_JAVA_LANGUAGE_SERVER__ ++ ++ ++ + diff --git a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs index d7876273983..60b7b52eae0 100644 --- a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs +++ b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs @@ -491,8 +491,11 @@ public void RegisterNativeMembers ( { try { if (methods.IsEmpty) { - if (jniAddNativeMethodRegistrationAttributePresent) + RuntimeNativeMethods.monodroid_log (LogLevel.Info, LogCategories.Assembly, $"RegisterJniNatives: Type {type.FullName} registers no methods"); + if (jniAddNativeMethodRegistrationAttributePresent) { + RuntimeNativeMethods.monodroid_log (LogLevel.Info, LogCategories.Assembly, $"RegisterJniNatives: Type {type.FullName} has [AddNativeMethodRegistration] attribute"); base.RegisterNativeMembers (nativeClass, type, methods.ToString ()); + } return; } else if (FastRegisterNativeMembers (nativeClass, type, methods)) { return; @@ -539,6 +542,7 @@ public void RegisterNativeMembers ( throw new InvalidOperationException (FormattableString.Invariant ($"Specified managed method '{mname.ToString ()}' was not found. Signature: {signature.ToString ()}")); callback = CreateDynamicCallback (minfo); needToRegisterNatives = true; + RuntimeNativeMethods.monodroid_log (LogLevel.Info, LogCategories.Assembly, $"RegisterJniNativesMethod: [ ] {type.FullName}: __export__ found for method '{name.ToString ()}'"); } else { Type callbackDeclaringType = type; if (!callbackDeclaringTypeString.IsEmpty) { @@ -556,6 +560,7 @@ public void RegisterNativeMembers ( if (callback != null) { needToRegisterNatives = true; natives [nativesIndex++] = new JniNativeMethodRegistration (name.ToString (), signature.ToString (), callback); + RuntimeNativeMethods.monodroid_log (LogLevel.Info, LogCategories.Assembly, $"RegisterJniNativesMethod: [ ] {type.FullName}.{name.ToString ()}"); } } @@ -563,20 +568,12 @@ public void RegisterNativeMembers ( } if (needToRegisterNatives) { + RuntimeNativeMethods.monodroid_log (LogLevel.Info, LogCategories.Assembly, $"RegisterJniNatives: Type {type.FullName} calling JniEnvironment.Types.RegisterNatives"); JniEnvironment.Types.RegisterNatives (nativeClass.PeerReference, natives, nativesIndex); } } catch (Exception e) { JniEnvironment.Runtime.RaisePendingException (e); } - - bool ShouldRegisterDynamically (string callbackTypeName, string callbackString, string typeName, string callbackName) - { - if (String.Compare (typeName, callbackTypeName, StringComparison.Ordinal) != 0) { - return false; - } - - return String.Compare (callbackName, callbackString, StringComparison.Ordinal) == 0; - } } static int CountMethods (ReadOnlySpan methodsSpan) diff --git a/src/Mono.Android/Android.Runtime/InputStreamAdapter.cs b/src/Mono.Android/Android.Runtime/InputStreamAdapter.cs index 5eb3b04bd87..795ed57d217 100644 --- a/src/Mono.Android/Android.Runtime/InputStreamAdapter.cs +++ b/src/Mono.Android/Android.Runtime/InputStreamAdapter.cs @@ -8,11 +8,18 @@ public sealed class InputStreamAdapter : Java.IO.InputStream { public Stream BaseStream {get; private set;} + InputStreamAdapter () { + Console.WriteLine ("InputStreamAdapter () invoked"); + Console.WriteLine (new System.Diagnostics.StackTrace(true)); + } + public InputStreamAdapter (System.IO.Stream stream) : base ( JNIEnv.StartCreateInstance ("mono/android/runtime/InputStreamAdapter", "()V"), JniHandleOwnership.TransferLocalRef) { + Console.WriteLine ("InputStreamAdapter (System.IO.Stream) invoked"); + Console.WriteLine (new System.Diagnostics.StackTrace(true)); JNIEnv.FinishCreateInstance (Handle, "()V"); this.BaseStream = stream; @@ -54,4 +61,3 @@ public static IntPtr ToLocalJniHandle (Stream? value) } } } - diff --git a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs index 2f65464716b..22ba4dc13f3 100644 --- a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs +++ b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs @@ -76,6 +76,9 @@ static unsafe void RegisterJniNatives (IntPtr typeName_ptr, int typeName_len, In [UnmanagedCallersOnly] internal static unsafe void Initialize (JnienvInitializeArgs* args) { +#if NETCOREAPP + RuntimeNativeMethods.monodroid_log_traces (TraceKind.All, "In JNIEnvInit.Initialize"); +#endif IntPtr total_timing_sequence = IntPtr.Zero; IntPtr partial_timing_sequence = IntPtr.Zero; diff --git a/src/Mono.Android/Android.Runtime/RuntimeNativeMethods.cs b/src/Mono.Android/Android.Runtime/RuntimeNativeMethods.cs index a0c936c59ff..da635032800 100644 --- a/src/Mono.Android/Android.Runtime/RuntimeNativeMethods.cs +++ b/src/Mono.Android/Android.Runtime/RuntimeNativeMethods.cs @@ -6,8 +6,23 @@ namespace Android.Runtime { + // Values must be identical to those in src/monodroid/jni/monodroid-glue-internal.hh + [Flags] + enum TraceKind : uint + { + Java = 0x01, + Managed = 0x02, + Native = 0x04, + Signals = 0x08, + + All = Java | Managed | Native | Signals, + } + internal static class RuntimeNativeMethods { + [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)] + internal extern static void monodroid_log_traces (TraceKind kind, string first_line); + [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)] internal extern static void monodroid_log (LogLevel level, LogCategories category, string message); diff --git a/src/Mono.Android/Java.Interop/TypeManager.cs b/src/Mono.Android/Java.Interop/TypeManager.cs index fb4e87216a4..924a1d02ef3 100644 --- a/src/Mono.Android/Java.Interop/TypeManager.cs +++ b/src/Mono.Android/Java.Interop/TypeManager.cs @@ -224,8 +224,10 @@ static Exception CreateJavaLocationException () if (!JNIEnvInit.IsRunningOnDesktop) { // Miss message is logged in the native runtime - if (JNIEnvInit.LogAssemblyCategory) + if (JNIEnvInit.LogAssemblyCategory) { JNIEnv.LogTypemapTrace (new System.Diagnostics.StackTrace (true)); + RuntimeNativeMethods.monodroid_log (LogLevel.Warn, LogCategories.Assembly, CreateJavaLocationException ().ToString ()); + } return null; } diff --git a/src/Mono.Android/Java.Lang/Thread.cs b/src/Mono.Android/Java.Lang/Thread.cs index 8c6155f41d3..a17aeebc16f 100644 --- a/src/Mono.Android/Java.Lang/Thread.cs +++ b/src/Mono.Android/Java.Lang/Thread.cs @@ -41,7 +41,15 @@ public void Run () Dispose (); } - static Dictionary instances = new Dictionary (); + static Dictionary instances;// = new Dictionary (); + + static RunnableImplementor () + { + Console.WriteLine ("RunnableImplementor.cctor (): begin"); + Console.WriteLine (new System.Diagnostics.StackTrace(true)); + instances = new (); + Console.WriteLine ("RunnableImplementor.cctor (): end"); + } public static RunnableImplementor Remove (Action handler) { @@ -65,4 +73,3 @@ public Thread (Action runHandler, string threadName) : this (new RunnableImpleme public Thread (ThreadGroup group, Action runHandler, string threadName, long stackSize) : this (group, new RunnableImplementor (runHandler), threadName, stackSize) {} } } - diff --git a/src/Mono.Android/java/mono/android/TypeManager.java b/src/Mono.Android/java/mono/android/TypeManager.java index bf64780bf9e..ab3c1beeef9 100644 --- a/src/Mono.Android/java/mono/android/TypeManager.java +++ b/src/Mono.Android/java/mono/android/TypeManager.java @@ -1,20 +1,26 @@ package mono.android; +//#NOTE: make sure the `#FEATURE=*:START` and `#FEATURE=*:END` lines for different values of FEATURE don't overlap or interlace. +//#NOTE: Each FEATURE block should be self-contained, with no other FEATURE blocks nested. +//#NOTE: This is because the code that skips over those FEATURE blocks is VERY primitive (see the `CreateTypeManagerJava` task in XABT) public class TypeManager { public static void Activate (String typeName, String sig, Object instance, Object[] parameterList) { +//#FEATURE=CALL_TRACING:START - do not remove or modify this line, it is required during application build + android.util.Log.i ("monodroid-trace", String.format ("java: activating type: '%s' [%s]", typeName, sig)); +//#FEATURE=CALL_TRACING:END - do not remove or modify this line, it is required during application build n_activate (typeName, sig, instance, parameterList); } private static native void n_activate (String typeName, String sig, Object instance, Object[] parameterList); -//#MARSHAL_METHODS:START - do not remove or modify this line, it is required during application build static { - String methods = + String methods = +//#FEATURE=MARSHAL_METHODS:START - do not remove or modify this line, it is required during application build "n_activate:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;)V:GetActivateHandler\n" + +//#FEATURE=MARSHAL_METHODS:END - do not remove or modify this line, it is required during application build ""; mono.android.Runtime.register ("Java.Interop.TypeManager+JavaTypeManager, Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", TypeManager.class, methods); } -//#MARSHAL_METHODS:END - do not remove or modify this line, it is required during application build } diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets index 05c6c56639f..bebbef71778 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets @@ -230,7 +230,8 @@ _ResolveAssemblies MSBuild target. InputLibraries="@(_ResolvedNativeLibraries)" ExcludedLibraries="@(_MonoExcludedLibraries)" Components="@(_MonoComponent->Distinct())" - IncludeDebugSymbols="$(AndroidIncludeDebugSymbols)"> + IncludeDebugSymbols="$(AndroidIncludeDebugSymbols)" + IncludeNativeTracing="$(_UseNativeStackTraces)"> diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets index 5d4506284cd..4023ad15312 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets @@ -45,6 +45,18 @@ false + + + + <_AndroidMarshalMethodsTracingMode Condition=" '$(_AndroidMarshalMethodsTracingMode)' == '' ">none + <_AndroidUseNativeStackTraces Condition=" '$(_AndroidUseNativeStackTraces)' == '' And '$(_AndroidMarshalMethodsTracingMode)' == 'none' ">false + <_AndroidUseNativeStackTraces Condition=" '$(_AndroidUseNativeStackTraces)' == '' And '$(_AndroidMarshalMethodsTracingMode)' != 'none' ">true + <_UseNativeStackTraces Condition=" '$(_AndroidUseNativeStackTraces)' == 'true' Or '$(_AndroidMarshalMethodsTracingMode)' != 'none' ">true + diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CreateTypeManagerJava.cs b/src/Xamarin.Android.Build.Tasks/Tasks/CreateTypeManagerJava.cs index 37872f31927..87309b463d9 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/CreateTypeManagerJava.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/CreateTypeManagerJava.cs @@ -15,6 +15,9 @@ public class CreateTypeManagerJava : AndroidTask [Required] public string ResourceName { get; set; } + public bool CallTracingEnabled { get; set; } + public bool MarshalMethodsEnabled { get; set; } + [Required] public string OutputFilePath { get; set; } @@ -31,12 +34,18 @@ public override bool RunTask () var result = new StringBuilder (); bool ignoring = false; foreach (string line in content.Split ('\n')) { + if (SkipNoteLine (line)) { + continue; + } + if (!ignoring) { - if (ignoring = line.StartsWith ("//#MARSHAL_METHODS:START", StringComparison.Ordinal)) { + ignoring = StartIgnoring (line, out bool skipLine); + if (ignoring || skipLine) { continue; } + result.AppendLine (line); - } else if (line.StartsWith ("//#MARSHAL_METHODS:END", StringComparison.Ordinal)) { + } else if (EndIgnoring (line)) { ignoring = false; } } @@ -62,6 +71,38 @@ public override bool RunTask () return !Log.HasLoggedErrors; } + bool SkipNoteLine (string l) => l.Trim ().StartsWith ("//#NOTE:"); + + bool StartIgnoring (string l, out bool skipLine) + { + string line = l.Trim (); + skipLine = true; + if (MarshalMethodsEnabled && line.StartsWith ("//#FEATURE=MARSHAL_METHODS:START", StringComparison.Ordinal)) { + return true; + } + + if (!CallTracingEnabled && line.StartsWith ("//#FEATURE=CALL_TRACING:START", StringComparison.Ordinal)) { + return true; + } + + skipLine = line.StartsWith ("//#FEATURE", StringComparison.Ordinal); + return false; + } + + bool EndIgnoring (string l) + { + string line = l.Trim (); + if (MarshalMethodsEnabled && line.StartsWith ("//#FEATURE=MARSHAL_METHODS:END", StringComparison.Ordinal)) { + return true; + } + + if (!CallTracingEnabled && line.StartsWith ("//#FEATURE=CALL_TRACING:END", StringComparison.Ordinal)) { + return true; + } + + return false; + } + string? ReadResource (string resourceName) { using (var from = ExecutingAssembly.GetManifestResourceStream (resourceName)) { diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs index 8bd49bfcd99..ed9ba40cad6 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs @@ -355,7 +355,7 @@ IList MergeManifest (NativeCodeGenState codeGenState, Dictionary allJavaTypes, List javaTypesForJCW) = ScanForJavaTypes (resolver, tdCache, assemblies, userAssemblies, useMarshalMethods); var jcwContext = new JCWGeneratorContext (arch, resolver, assemblies.Values, javaTypesForJCW, tdCache, useMarshalMethods); - var jcwGenerator = new JCWGenerator (Log, jcwContext); + var jcwGenerator = new JCWGenerator (Log, jcwContext, IntermediateOutputDirectory); bool success; if (generateJavaCode) { diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs index cecfd42ada2..1c7f7dfa4d3 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs @@ -23,6 +23,7 @@ public class GeneratePackageManagerJava : AndroidTask public override string TaskPrefix => "GPM"; Guid buildId = Guid.NewGuid (); + MarshalMethodsTracingMode mmTracingMode; [Required] public ITaskItem[] ResolvedAssemblies { get; set; } @@ -66,6 +67,7 @@ public class GeneratePackageManagerJava : AndroidTask public bool InstantRunEnabled { get; set; } public bool EnableMarshalMethods { get; set; } + public string MarshalMethodsTracingMode { get; set; } public string RuntimeConfigBinFilePath { get; set; } public string BoundExceptionType { get; set; } @@ -91,6 +93,8 @@ bool _Debug { public override bool RunTask () { + mmTracingMode = MonoAndroidHelper.ParseMarshalMethodsTracingMode (MarshalMethodsTracingMode); + BuildId = buildId.ToString (); Log.LogDebugMessage (" [Output] BuildId: {0}", BuildId); @@ -384,7 +388,8 @@ void AddEnvironment () Log, assemblyCount, uniqueAssemblyNames, - EnsureCodeGenState (targetArch) + EnsureCodeGenState (targetArch), + mmTracingMode ); } else { marshalMethodsAsmGen = new MarshalMethodsNativeAssemblyGenerator ( diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/LinkApplicationSharedLibraries.cs b/src/Xamarin.Android.Build.Tasks/Tasks/LinkApplicationSharedLibraries.cs index 5f4b09ececa..1d8b17327ec 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/LinkApplicationSharedLibraries.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/LinkApplicationSharedLibraries.cs @@ -44,8 +44,16 @@ sealed class InputFiles [Required] public string AndroidBinUtilsDirectory { get; set; } + public string MarshalMethodsTracingMode { get; set; } + + MarshalMethodsTracingMode mmTracingMode; + bool mmTracingEnabled; + public override System.Threading.Tasks.Task RunTaskAsync () { + mmTracingMode = MonoAndroidHelper.ParseMarshalMethodsTracingMode (MarshalMethodsTracingMode); + mmTracingEnabled = mmTracingMode != Tasks.MarshalMethodsTracingMode.None; + return this.WhenAll (GetLinkerConfigs (), RunLinker); } @@ -122,6 +130,7 @@ IEnumerable GetLinkerConfigs () abis [abi] = GatherFilesForABI (item.ItemSpec, abi, ObjectFiles, runtimeNativeLibsDir, runtimeNativeLibStubsDir); } + // "--eh-frame-hdr " + const string commonLinkerArgs = "--shared " + "--allow-shlib-undefined " + @@ -134,7 +143,7 @@ IEnumerable GetLinkerConfigs () "--warn-shared-textrel " + "--fatal-warnings"; - string stripSymbolsArg = DebugBuild ? String.Empty : " -s"; + string stripSymbolsArg = DebugBuild || mmTracingEnabled ? String.Empty : " -s"; string ld = Path.Combine (AndroidBinUtilsDirectory, MonoAndroidHelper.GetExecutablePath (AndroidBinUtilsDirectory, "ld")); var targetLinkerArgs = new List (); @@ -208,6 +217,11 @@ InputFiles GatherFilesForABI (string runtimeSharedLibrary, string abi, ITaskItem "-lc", }; + if (mmTracingEnabled) { + extraLibraries.Add (Path.Combine (runtimeLibsDir, "libmarshal-methods-tracing.a")); + extraLibraries.Add ("-lxamarin-native-tracing"); + } + return new InputFiles { OutputSharedLibrary = runtimeSharedLibrary, ObjectFiles = GetItemsForABI (abi, objectFiles), diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/ProcessNativeLibraries.cs b/src/Xamarin.Android.Build.Tasks/Tasks/ProcessNativeLibraries.cs index 4970875d659..30a57d1398b 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/ProcessNativeLibraries.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/ProcessNativeLibraries.cs @@ -30,6 +30,7 @@ public class ProcessNativeLibraries : AndroidTask public string [] ExcludedLibraries { get; set; } public bool IncludeDebugSymbols { get; set; } + public bool IncludeNativeTracing { get; set; } [Output] public ITaskItem [] OutputLibraries { get; set; } @@ -82,6 +83,9 @@ public override bool RunTask () if (!wantedComponents.Contains (fileName)) { continue; } + } else if (!IncludeNativeTracing && String.Compare ("libxamarin-native-tracing", fileName, StringComparison.Ordinal) == 0) { + Log.LogDebugMessage ($"Excluding '{library.ItemSpec}' because native stack traces support is disabled"); + continue; } else if (ExcludedLibraries != null && ExcludedLibraries.Contains (fileName, StringComparer.OrdinalIgnoreCase)) { Log.LogDebugMessage ($"Excluding '{library.ItemSpec}'"); continue; diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs index 8f7e569dfa4..7ec1b6f4951 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs @@ -327,7 +327,6 @@ void HashAndSortDSOCache (LlvmIrVariable variable, LlvmIrModuleTarget target, ob if (entry == null) { throw new InvalidOperationException ($"Internal error: DSO cache entry has unexpected type {instance.Obj.GetType ()}"); } - entry.hash = MonoAndroidHelper.GetXxHash (entry.HashedName, is64Bit); entry.real_name_hash = MonoAndroidHelper.GetXxHash (entry.name, is64Bit); } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/JCWGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/JCWGenerator.cs index d1a400bab20..f8cfa34d562 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/JCWGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/JCWGenerator.cs @@ -42,13 +42,15 @@ class JCWGenerator { readonly TaskLoggingHelper log; readonly JCWGeneratorContext context; + readonly string intermediateOutputDirectory; public MarshalMethodsClassifier? Classifier { get; private set; } - public JCWGenerator (TaskLoggingHelper log, JCWGeneratorContext context) + public JCWGenerator (TaskLoggingHelper log, JCWGeneratorContext context, string intermediateOutputDirectory) { this.log = log; this.context = context; + this.intermediateOutputDirectory = intermediateOutputDirectory; } /// @@ -88,7 +90,7 @@ public bool GenerateAndClassify (string androidSdkPlatform, string outputPath, s ); } - MarshalMethodsClassifier MakeClassifier () => new MarshalMethodsClassifier (context.Arch, context.TypeDefinitionCache, context.Resolver, log); + MarshalMethodsClassifier MakeClassifier () => new MarshalMethodsClassifier (context.Arch, context.TypeDefinitionCache, context.Resolver, intermediateOutputDirectory, log); bool ProcessTypes (bool generateCode, string androidSdkPlatform, MarshalMethodsClassifier? classifier, string? outputPath, string? applicationJavaClass) { diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/JniRemappingAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/JniRemappingAssemblyGenerator.cs index dd6d0924fff..375025bfeb9 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/JniRemappingAssemblyGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/JniRemappingAssemblyGenerator.cs @@ -210,6 +210,8 @@ public JniRemappingAssemblyGenerator (TaskLoggingHelper log, List> (); + Console.WriteLine ($"Type replacement input count: {typeReplacementsInput.Count}"); + foreach (JniRemappingTypeReplacement mtr in typeReplacementsInput) { var entry = new JniRemappingTypeReplacementEntry { name = MakeJniRemappingString (mtr.From), diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrFunctionAttributeSet.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrFunctionAttributeSet.cs index 7c890e13417..5c8feabca5b 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrFunctionAttributeSet.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrFunctionAttributeSet.cs @@ -53,6 +53,19 @@ public void Add (IList attrList) } } + public void Add (AndroidTargetArch targetArch, LlvmIrFunctionAttribute attr) + { + if (privateTargetSpecificAttributes == null) { + privateTargetSpecificAttributes = new (); + } + + if (!privateTargetSpecificAttributes.TryGetValue (targetArch, out List attrList)) { + attrList = new (); + privateTargetSpecificAttributes.Add (targetArch, attrList); + } + attrList.Add (attr); + } + public string Render () { List list = attributes.ToList (); diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs index 9f1034bd6b8..0a03881be93 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs @@ -300,6 +300,12 @@ void GenerateNonBlittableConversion (TypeReference sourceType, TypeReference tar return; } + if (IsCharConversion (sourceType, targetType)) { + // No need to generate any code. Java's `char` is unsigned 16-bit type, the same as + // .NET's + return; + } + ThrowUnsupportedType (sourceType); } @@ -333,6 +339,19 @@ bool IsBooleanConversion (TypeReference sourceType, TypeReference targetType) return false; } + bool IsCharConversion (TypeReference sourceType, TypeReference targetType) + { + if (String.Compare ("System.Char", sourceType.FullName, StringComparison.Ordinal) == 0) { + if (String.Compare ("System.UInt16", targetType.FullName, StringComparison.Ordinal) != 0) { + throw new InvalidOperationException ($"Unexpected conversion from '{sourceType.FullName}' to '{targetType.FullName}'"); + } + + return true; + } + + return false; + } + void ThrowUnsupportedType (TypeReference type) { throw new InvalidOperationException ($"[{targetArch}] Unsupported non-blittable type '{type.FullName}'"); @@ -437,6 +456,12 @@ TypeReference MapToBlittableTypeIfNecessary (TypeReference type, out bool typeMa return ReturnValid (typeof(byte)); } + if (String.Compare ("System.Char", type.FullName, StringComparison.Ordinal) == 0) { + // Maps to Java JNI's jchar which is an unsigned 16-bit type + typeMapped = true; + return ReturnValid (typeof(ushort)); + } + throw new NotSupportedException ($"[{targetArch}] Cannot map unsupported blittable type '{type.FullName}'"); TypeReference ReturnValid (Type typeToLookUp) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs index 1507084dfc0..5936f6e7fb5 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using Java.Interop.Tools.Cecil; @@ -241,6 +242,7 @@ public bool Matches (MethodDefinition method) HashSet typesWithDynamicallyRegisteredMethods; ulong rejectedMethodCount = 0; ulong wrappedMethodCount = 0; + StreamWriter ignoredMethodsLog; readonly AndroidTargetArch targetArch; public IDictionary> MarshalMethods => marshalMethods; @@ -249,7 +251,7 @@ public bool Matches (MethodDefinition method) public ulong WrappedMethodCount => wrappedMethodCount; public TypeDefinitionCache TypeDefinitionCache => tdCache; - public MarshalMethodsClassifier (AndroidTargetArch targetArch, TypeDefinitionCache tdCache, IAssemblyResolver res, TaskLoggingHelper log) + public MarshalMethodsClassifier (AndroidTargetArch targetArch, TypeDefinitionCache tdCache, IAssemblyResolver res, string intermediateOutputDirectory, TaskLoggingHelper log) { this.targetArch = targetArch; this.log = log ?? throw new ArgumentNullException (nameof (log)); @@ -258,9 +260,12 @@ public MarshalMethodsClassifier (AndroidTargetArch targetArch, TypeDefinitionCac marshalMethods = new Dictionary> (StringComparer.Ordinal); assemblies = new HashSet (); typesWithDynamicallyRegisteredMethods = new HashSet (); + + var fs = File.Open (Path.Combine (intermediateOutputDirectory, $"{targetArch}-marshal-methods-ignored.txt"), FileMode.Create); + ignoredMethodsLog = new StreamWriter (fs, Files.UTF8withoutBOM); } - public MarshalMethodsClassifier (TypeDefinitionCache tdCache, IAssemblyResolver res, TaskLoggingHelper log) + public MarshalMethodsClassifier (TypeDefinitionCache tdCache, IAssemblyResolver res, string intermediateOutputDirectory, TaskLoggingHelper log) { this.log = log ?? throw new ArgumentNullException (nameof (log)); this.tdCache = tdCache ?? throw new ArgumentNullException (nameof (tdCache)); @@ -268,6 +273,17 @@ public MarshalMethodsClassifier (TypeDefinitionCache tdCache, IAssemblyResolver marshalMethods = new Dictionary> (StringComparer.Ordinal); assemblies = new HashSet (); typesWithDynamicallyRegisteredMethods = new HashSet (); + + var fs = File.Open (Path.Combine (intermediateOutputDirectory, "marshal-methods-ignored.txt"), FileMode.Create); + ignoredMethodsLog = new StreamWriter (fs, Files.UTF8withoutBOM); + } + + public void FlushAndCloseOutputs () + { + ignoredMethodsLog.WriteLine (); + ignoredMethodsLog.WriteLine ($"Marshal methods count: {MarshalMethods.Count}; Rejected methods count: {RejectedMethodCount}"); + ignoredMethodsLog.Flush (); + ignoredMethodsLog.Close (); } public override bool ShouldBeDynamicallyRegistered (TypeDefinition topType, MethodDefinition registeredMethod, MethodDefinition implementedMethod, CustomAttribute? registerAttribute) @@ -461,6 +477,15 @@ bool IsDynamicallyRegistered (TypeDefinition topType, MethodDefinition registere return true; } + void LogIgnored (TypeDefinition type, MethodDefinition method, string message, bool logWarning = true) + { + if (logWarning) { + log.LogWarning (message); + } + + ignoredMethodsLog.WriteLine ($"{type.FullName}\t{method.FullName}\t{message}"); + } + bool IsStandardHandler (TypeDefinition topType, ConnectorInfo connector, MethodDefinition registeredMethod, MethodDefinition implementedMethod, string jniName, string jniSignature) { const string HandlerNameStart = "Get"; @@ -485,23 +510,23 @@ bool IsStandardHandler (TypeDefinition topType, ConnectorInfo connector, MethodD MethodDefinition connectorMethod = FindMethod (connectorDeclaringType, connectorName); if (connectorMethod == null) { - log.LogWarning ($"Connector method '{connectorName}' not found in type '{connectorDeclaringType.FullName}' {GetAssemblyPathInfo (connectorDeclaringType)}"); + LogIgnored (topType, registeredMethod, $"Connector method '{connectorName}' not found in type '{connectorDeclaringType.FullName}' {GetAssemblyPathInfo (connectorDeclaringType)}"); return false; } if (String.Compare ("System.Delegate", connectorMethod.ReturnType.FullName, StringComparison.Ordinal) != 0) { - log.LogWarning ($"Connector '{connectorName}' in type '{connectorDeclaringType.FullName}' has invalid return type, expected 'System.Delegate', found '{connectorMethod.ReturnType.FullName}' {GetAssemblyPathInfo (connectorDeclaringType)}"); + LogIgnored (topType, registeredMethod, $"Connector '{connectorName}' in type '{connectorDeclaringType.FullName}' has invalid return type, expected 'System.Delegate', found '{connectorMethod.ReturnType.FullName}' {GetAssemblyPathInfo (connectorDeclaringType)}"); return false; } var ncbs = new NativeCallbackSignature (registeredMethod, log, tdCache); MethodDefinition nativeCallbackMethod = FindMethod (connectorDeclaringType, nativeCallbackName, ncbs); if (nativeCallbackMethod == null) { - log.LogWarning ($"Unable to find native callback method '{nativeCallbackName}' in type '{connectorDeclaringType.FullName}', matching the '{registeredMethod.FullName}' signature (jniName: '{jniName}') {GetAssemblyPathInfo (connectorDeclaringType)}"); + LogIgnored (topType, registeredMethod, $"Unable to find native callback method '{nativeCallbackName}' in type '{connectorDeclaringType.FullName}', matching the '{registeredMethod.FullName}' signature (jniName: '{jniName}') {GetAssemblyPathInfo (connectorDeclaringType)}"); return false; } - if (!EnsureIsValidUnmanagedCallersOnlyTarget (nativeCallbackMethod, out bool needsBlittableWorkaround)) { + if (!EnsureIsValidUnmanagedCallersOnlyTarget (topType, registeredMethod, nativeCallbackMethod, out bool needsBlittableWorkaround)) { return false; } @@ -510,7 +535,7 @@ bool IsStandardHandler (TypeDefinition topType, ConnectorInfo connector, MethodD FieldDefinition delegateField = FindField (nativeCallbackMethod.DeclaringType, delegateFieldName); if (delegateField != null) { if (String.Compare ("System.Delegate", delegateField.FieldType.FullName, StringComparison.Ordinal) != 0) { - log.LogWarning ($"delegate field '{delegateFieldName}' in type '{nativeCallbackMethod.DeclaringType.FullName}' has invalid type, expected 'System.Delegate', found '{delegateField.FieldType.FullName}' {GetAssemblyPathInfo (delegateField)}"); + LogIgnored (topType, registeredMethod, $"delegate field '{delegateFieldName}' in type '{nativeCallbackMethod.DeclaringType.FullName}' has invalid type, expected 'System.Delegate', found '{delegateField.FieldType.FullName}' {GetAssemblyPathInfo (delegateField)}"); return false; } } @@ -569,23 +594,23 @@ bool IsStandardHandler (TypeDefinition topType, ConnectorInfo connector, MethodD return true; } - bool EnsureIsValidUnmanagedCallersOnlyTarget (MethodDefinition method, out bool needsBlittableWorkaround) + bool EnsureIsValidUnmanagedCallersOnlyTarget (TypeDefinition topType, MethodDefinition registeredMethod, MethodDefinition nativeCallbackMethod, out bool needsBlittableWorkaround) { needsBlittableWorkaround = false; // Requirements: https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.unmanagedcallersonlyattribute?view=net-6.0#remarks - if (!method.IsStatic) { + if (!nativeCallbackMethod.IsStatic) { return LogReasonWhyAndReturnFailure ($"is not static"); } - if (method.HasGenericParameters) { + if (nativeCallbackMethod.HasGenericParameters) { return LogReasonWhyAndReturnFailure ($"has generic parameters"); } TypeReference type; bool needsWrapper = false; - if (String.Compare ("System.Void", method.ReturnType.FullName, StringComparison.Ordinal) != 0) { - type = GetRealType (method.ReturnType); + if (String.Compare ("System.Void", nativeCallbackMethod.ReturnType.FullName, StringComparison.Ordinal) != 0) { + type = GetRealType (nativeCallbackMethod.ReturnType); if (!IsAcceptable (type)) { needsBlittableWorkaround = true; WarnWhy ($"has a non-blittable return type '{type.FullName}'"); @@ -593,15 +618,15 @@ bool EnsureIsValidUnmanagedCallersOnlyTarget (MethodDefinition method, out bool } } - if (method.DeclaringType.HasGenericParameters) { + if (nativeCallbackMethod.DeclaringType.HasGenericParameters) { return LogReasonWhyAndReturnFailure ($"is declared in a type with generic parameters"); } - if (!method.HasParameters) { + if (!nativeCallbackMethod.HasParameters) { return UpdateWrappedCountAndReturn (true); } - foreach (ParameterDefinition pdef in method.Parameters) { + foreach (ParameterDefinition pdef in nativeCallbackMethod.Parameters) { type = GetRealType (pdef.ParameterType); if (!IsAcceptable (type)) { @@ -645,14 +670,14 @@ TypeReference GetRealType (TypeReference type) bool LogReasonWhyAndReturnFailure (string why) { - log.LogWarning ($"Method '{method.FullName}' {why}. It cannot be used with the `[UnmanagedCallersOnly]` attribute"); + LogIgnored (topType, registeredMethod, $"Method '{nativeCallbackMethod.FullName}' {why}. It cannot be used with the `[UnmanagedCallersOnly]` attribute"); return false; } void WarnWhy (string why) { // TODO: change to LogWarning once the generator can output code which requires no non-blittable wrappers - log.LogDebugMessage ($"Method '{method.FullName}' {why}. A workaround is required, this may make the application slower"); + log.LogDebugMessage ($"Method '{nativeCallbackMethod.FullName}' {why}. A workaround is required, this may make the application slower"); } } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.Tracing.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.Tracing.cs new file mode 100644 index 00000000000..7f4afc832c9 --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.Tracing.cs @@ -0,0 +1,603 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; + +using Xamarin.Android.Tools; +using Xamarin.Android.Tasks.LLVMIR; + +namespace Xamarin.Android.Tasks; + +using CecilMethodDefinition = global::Mono.Cecil.MethodDefinition; +using CecilParameterDefinition = global::Mono.Cecil.ParameterDefinition; + +partial class MarshalMethodsNativeAssemblyGenerator +{ + sealed class LlvmLifetimePointerSizeArgumentPlaceholder : LlvmIrInstructionPointerSizeArgumentPlaceholder + { + public override object? GetValue (LlvmIrModuleTarget target) + { + // the llvm.lifetime functions need a 64-bit integer, target.NativePointerSize is 32-bit + return (ulong)(uint)base.GetValue (target); + } + } + + enum TracingRenderArgumentFunction + { + None, + GetClassName, + GetObjectClassname, + GetCString, + GetBooleanString, + } + + sealed class TracingState + { + public List? trace_enter_leave_args = null; + public LlvmIrLocalVariable? asprintfAllocatedStringVar = null; + } + + readonly MarshalMethodsTracingMode tracingMode; + + LlvmIrFunction? asprintf; + LlvmIrFunction? free; + + LlvmIrFunction? llvm_lifetime_end; + LlvmIrFunction? llvm_lifetime_start; + + LlvmIrFunction? mm_trace_func_enter; + LlvmIrFunction? mm_trace_func_leave; + LlvmIrFunction? mm_trace_get_boolean_string; + LlvmIrFunction? mm_trace_get_c_string; + LlvmIrFunction? mm_trace_get_class_name; + LlvmIrFunction? mm_trace_get_object_class_name; + LlvmIrFunction? mm_trace_init; + + LlvmIrFunctionAttributeSet? freeCallAttributes; + LlvmIrFunctionAttributeSet? asprintfCallAttributes; + + void InitTracing (LlvmIrModule module) + { + var externalFunctionsAttributeSet = module.AddAttributeSet (MakeTraceFunctionsAttributeSet (module)); + + InitLlvmFunctions (module); + InitLibcFunctions (module, externalFunctionsAttributeSet); + InitTracingFunctions (module, externalFunctionsAttributeSet); + } + + void WriteTracingAtFunctionTop (LlvmIrModule module, MarshalMethodInfo method, LlvmIrFunctionBody body, LlvmIrFunction function, MarshalMethodsWriteState writeState) + { + if (tracingMode == MarshalMethodsTracingMode.None) { + return; + } + + module.RegisterString (method.NativeSymbolName); + + body.AddComment (" Tracing code start"); + + var tracingState = new TracingState { + asprintfAllocatedStringVar = function.CreateLocalVariable (typeof(string), "render_buf"), + }; + body.Alloca (tracingState.asprintfAllocatedStringVar); + + LlvmIrFunctionAttributeSet lifetimeAttrSet = MakeLlvmLifetimeAttributeSet (module); + LlvmIrInstructions.Call call = body.Call (llvm_lifetime_start, arguments: new List { new LlvmLifetimePointerSizeArgumentPlaceholder (), tracingState.asprintfAllocatedStringVar }); + call.AttributeSet = lifetimeAttrSet; + + body.Store (tracingState.asprintfAllocatedStringVar, module.TbaaAnyPointer); + + AddAsprintfCall (module, function, method, tracingState); + + CecilMethodDefinition nativeCallback = method.Method.NativeCallback; + var assemblyCacheIndexPlaceholder = new MarshalMethodAssemblyIndexValuePlaceholder (method, writeState.AssemblyCacheState); + tracingState.trace_enter_leave_args = new List { + function.Signature.Parameters[0], // JNIEnv *env + (int)tracingMode, + assemblyCacheIndexPlaceholder, + method.ClassCacheIndex, + nativeCallback.MetadataToken.ToUInt32 (), + method.NativeSymbolName, + tracingState.asprintfAllocatedStringVar, + }; + body.Call (mm_trace_func_enter, arguments: tracingState.trace_enter_leave_args); + AddFreeCall (function, tracingState.asprintfAllocatedStringVar); + + body.AddComment (" Tracing code end"); + } + + void AddAsprintfCall (LlvmIrModule module, LlvmIrFunction function, MarshalMethodInfo method, TracingState tracingState) + { + Mono.Collections.Generic.Collection? managedMethodParameters = method.Method.RegisteredMethod?.Parameters ?? method.Method.ImplementedMethod?.Parameters; + + int expectedRegisteredParamCount; + int managedParametersOffset; + if (managedMethodParameters == null) { + // There was no registered nor implemented methods, so we're looking at a native callback - it should have the same number of parameters as our wrapper + managedMethodParameters = method.Method.NativeCallback.Parameters; + expectedRegisteredParamCount = method.Parameters.Count; + managedParametersOffset = 0; + } else { + // Managed methods get two less parameters (no `env` and `klass`) + expectedRegisteredParamCount = method.Parameters.Count - 2; + managedParametersOffset = 2; + } + + if (managedMethodParameters.Count != expectedRegisteredParamCount) { + throw new InvalidOperationException ($"Internal error: unexpected number of registered method '{method.Method.NativeCallback.FullName}' parameters. Should be {expectedRegisteredParamCount}, but is {managedMethodParameters.Count}"); + } + + if (method.Parameters.Count != function.Signature.Parameters.Count) { + throw new InvalidOperationException ($"Internal error: number of native method parameter ({method.Parameters.Count}) doesn't match the number of marshal method parameters ({function.Signature.Parameters.Count})"); + } + + var varargs = new List (); + var variablesToFree = new List (); + var formatSb = new StringBuilder ('('); + bool first = true; + + for (int i = 0; i < method.Parameters.Count; i++) { + LlvmIrFunctionParameter parameter = method.Parameters[i]; + + if (!first) { + formatSb.Append (", "); + } else { + first = false; + } + + // Native method will have two more parameters than its managed counterpart - one for JNIEnv* and another for jclass. They always are the first two + // parameters, so we start looking at the managed parameters only once the first two are out of the way + CecilParameterDefinition? managedParameter = i >= managedParametersOffset ? managedMethodParameters[i - managedParametersOffset] : null; + Type actualType = VerifyAndGetActualParameterType (parameter, managedParameter); + PrepareAsprintfArgument (formatSb, function, parameter, actualType, varargs, variablesToFree); + } + + formatSb.Append (')'); + + string format = formatSb.ToString (); + var asprintfArgs = new List { + tracingState.asprintfAllocatedStringVar, + format, + }; + asprintfArgs.AddRange (varargs); + + DoAddAsprintfCall (asprintfArgs, module, function, format, tracingState); + + foreach (LlvmIrVariable vtf in variablesToFree) { + AddFreeCall (function, vtf); + } + + Type VerifyAndGetActualParameterType (LlvmIrFunctionParameter nativeParameter, CecilParameterDefinition? managedParameter) + { + if (managedParameter == null) { + return nativeParameter.Type; + } + + if (nativeParameter.Type == typeof(byte) && String.Compare ("System.Boolean", managedParameter.Name, StringComparison.Ordinal) == 0) { + // `bool`, as a non-blittable type, is mapped to `byte` by the marshal method rewriter + return typeof(bool); + } + + return nativeParameter.Type; + } + } + + void PrepareAsprintfArgument (StringBuilder format, LlvmIrFunction function, LlvmIrVariable parameter, Type actualType, List varargs, List variablesToFree) + { + LlvmIrVariable? result = null; + if (actualType == typeof(_jclass)) { + format.Append ("%s @%p"); + result = AddTransformFunctionCall (function, TracingRenderArgumentFunction.GetClassName, parameter); + varargs.Add (result); + variablesToFree.Add (result); + varargs.Add (parameter); + return; + } + + if (actualType == typeof(_jobject)) { + format.Append ("%s @%p"); + result = AddTransformFunctionCall (function, TracingRenderArgumentFunction.GetObjectClassname, parameter); + varargs.Add (result); + variablesToFree.Add (result); + varargs.Add (parameter); + return; + } + + if (actualType == typeof(_jstring)) { + format.Append ("\"%s\""); + result = AddTransformFunctionCall (function, TracingRenderArgumentFunction.GetCString, parameter); + varargs.Add (result); + variablesToFree.Add (result); + return; + } + + if (actualType == typeof(bool)) { + format.Append ("%s"); + + // No need to free(3) it, returns pointer to a constant + result = AddTransformFunctionCall (function, TracingRenderArgumentFunction.GetBooleanString, parameter); + varargs.Add (result); + return; + } + + if (actualType == typeof(byte) || actualType == typeof(ushort)) { + format.Append ("%u"); + AddUpcast (); + return; + } + + if (actualType == typeof(sbyte) || actualType == typeof(short)) { + format.Append ("%d"); + AddUpcast (); + return; + } + + if (actualType == typeof(char)) { + format.Append ("'\\%x'"); + AddUpcast (); + return; + } + + if (actualType == typeof(float)) { + format.Append ("%g"); + AddUpcast (); + return; + } + + if (actualType == typeof(string)) { + format.Append ("\"%s\""); + } else if (actualType == typeof(IntPtr) || typeof(_jobject).IsAssignableFrom (actualType) || actualType == typeof(_JNIEnv)) { + format.Append ("%p"); + } else if (actualType == typeof(int)) { + format.Append ("%d"); + } else if (actualType == typeof(uint)) { + format.Append ("%u"); + } else if (actualType == typeof(long)) { + format.Append ("%ld"); + } else if (actualType == typeof(ulong)) { + format.Append ("%lu"); + } else if (actualType == typeof(double)) { + format.Append ("%g"); + } else { + throw new InvalidOperationException ($"Unsupported type '{actualType}'"); + } + + varargs.Add (parameter); + + void AddUpcast () + { + LlvmIrVariable ret = function.CreateLocalVariable (typeof(T)); + function.Body.Ext (parameter, typeof(T), ret); + varargs.Add (ret); + } + } + + LlvmIrVariable? AddTransformFunctionCall (LlvmIrFunction function, TracingRenderArgumentFunction renderFunction, LlvmIrVariable paramVar) + { + if (renderFunction == TracingRenderArgumentFunction.None) { + return null; + } + + var transformerArgs = new List (); + LlvmIrFunction transformerFunc; + + switch (renderFunction) { + case TracingRenderArgumentFunction.GetClassName: + transformerFunc = mm_trace_get_class_name; + AddJNIEnvArgument (); + break; + + case TracingRenderArgumentFunction.GetObjectClassname: + transformerFunc = mm_trace_get_object_class_name; + AddJNIEnvArgument (); + break; + + case TracingRenderArgumentFunction.GetCString: + transformerFunc = mm_trace_get_c_string; + AddJNIEnvArgument (); + break; + + case TracingRenderArgumentFunction.GetBooleanString: + transformerFunc = mm_trace_get_boolean_string; + break; + + default: + throw new InvalidOperationException ($"Internal error: unsupported transformer function {renderFunction}"); + }; + transformerArgs.Add (paramVar); + + if (!transformerFunc.ReturnsValue) { + return null; + } + LlvmIrLocalVariable? result = function.CreateLocalVariable (transformerFunc.Signature.ReturnType); + function.Body.Call (transformerFunc, result, transformerArgs); + + return result; + + void AddJNIEnvArgument () + { + transformerArgs.Add (function.Signature.Parameters[0]); + } + } + + void DoAddAsprintfCall (List args, LlvmIrModule module, LlvmIrFunction function, string format, TracingState tracingState) + { + module.RegisterString (format); + + LlvmIrFunctionBody body = function.Body; + LlvmIrLocalVariable asprintf_ret = function.CreateLocalVariable (typeof(int), "asprintf_ret"); + LlvmIrInstructions.Call call = body.Call (asprintf, asprintf_ret, args); + call.AttributeSet = asprintfCallAttributes; + call.Comment = $" Format: {format}"; + + // Check whether asprintf returned a negative value (it returns -1 at failure, but we widen the check just in case) + LlvmIrLocalVariable asprintf_failed = function.CreateLocalVariable (typeof(bool), "asprintf_failed"); + body.Icmp (LlvmIrIcmpCond.SignedLessThan, asprintf_ret, (int)0, asprintf_failed); + + var asprintfIfThenLabel = new LlvmIrFunctionLabelItem (); + var asprintfIfDoneLabel = new LlvmIrFunctionLabelItem (); + + body.Br (asprintf_failed, asprintfIfThenLabel, asprintfIfDoneLabel); + + // Condition is true if asprintf **failed** + body.Add (asprintfIfThenLabel); + body.Store (tracingState.asprintfAllocatedStringVar); + body.Br (asprintfIfDoneLabel); + + body.Add (asprintfIfDoneLabel); + } + + void AddFreeCall (LlvmIrFunction function, object? toFree) + { + LlvmIrInstructions.Call call = function.Body.Call (free, arguments: new List { toFree }); + call.AttributeSet = freeCallAttributes; + } + + void InitLibcFunctions (LlvmIrModule module, LlvmIrFunctionAttributeSet? attrSet) + { + var asprintf_params = new List { + new (typeof(string), "strp") { + NoUndef = true, + }, + new (typeof(string), "fmt") { + NoUndef = true, + }, + new (typeof(void)) { + IsVarArgs = true, + }, + }; + + var asprintf_sig = new LlvmIrFunctionSignature ( + name: "asprintf", + returnType: typeof(int), + parameters: asprintf_params + ); + + asprintf = module.DeclareExternalFunction (new LlvmIrFunction (asprintf_sig, attrSet)); + + var free_params = new List { + new (typeof(IntPtr), "ptr") { + AllocPtr = true, + NoCapture = true, + NoUndef = true, + }, + }; + + var free_sig = new LlvmIrFunctionSignature ( + name: "free", + returnType: typeof(void), + parameters: free_params + ); + + free = module.DeclareExternalFunction (new LlvmIrFunction (free_sig, MakeFreeFunctionAttributeSet (module))); + freeCallAttributes = MakeFreeCallAttributeSet (module); + asprintfCallAttributes = MakeAsprintfCallAttributeSet (module); + } + + void InitLlvmFunctions (LlvmIrModule module) + { + var llvmFunctionsAttributeSet = module.AddAttributeSet (MakeLlvmIntrinsicFunctionsAttributeSet (module)); + var llvm_lifetime_params = new List { + new (typeof(ulong), "size"), + new (typeof(IntPtr), "pointer"), + }; + + var lifetime_sig = new LlvmIrFunctionSignature ( + name: "llvm.lifetime.start", + returnType: typeof(void), + parameters: llvm_lifetime_params + ); + + llvm_lifetime_start = module.DeclareExternalFunction ( + new LlvmIrFunction (lifetime_sig, llvmFunctionsAttributeSet) { + AddressSignificance = LlvmIrAddressSignificance.Default + } + ); + + llvm_lifetime_end = module.DeclareExternalFunction ( + new LlvmIrFunction ("llvm.lifetime.end", lifetime_sig, llvmFunctionsAttributeSet) { + AddressSignificance = LlvmIrAddressSignificance.Default + } + ); + } + + void InitTracingFunctions (LlvmIrModule module, LlvmIrFunctionAttributeSet? attrSet) + { + // Function names and declarations must match those in src/monodroid/jni/marshal-methods-tracing.hh + var mm_trace_init_params = new List { + new (typeof(IntPtr), "env"), + }; + + var mm_trace_init_sig = new LlvmIrFunctionSignature ( + name: "_mm_trace_init", + returnType: typeof(void), + parameters: mm_trace_init_params + ); + + mm_trace_init = module.DeclareExternalFunction (new LlvmIrFunction (mm_trace_init_sig, attrSet)); + + var mm_trace_func_enter_or_leave_params = new List { + new (typeof(IntPtr), "env"), // JNIEnv *env + new (typeof(int), "tracing_mode"), + new (typeof(uint), "mono_image_index"), + new (typeof(uint), "class_index"), + new (typeof(uint), "method_token"), + new (typeof(string), "native_method_name"), + new (typeof(string), "method_extra_info"), + }; + + var mm_trace_func_enter_leave_sig = new LlvmIrFunctionSignature ( + name: "_mm_trace_func_enter", + returnType: typeof(void), + parameters: mm_trace_func_enter_or_leave_params + ); + + mm_trace_func_enter = module.DeclareExternalFunction (new LlvmIrFunction (mm_trace_func_enter_leave_sig, attrSet)); + mm_trace_func_leave = module.DeclareExternalFunction (new LlvmIrFunction ("_mm_trace_func_leave", mm_trace_func_enter_leave_sig, attrSet)); + + var mm_trace_get_class_name_params = new List { + new (typeof(IntPtr), "env") { + NoUndef = true, + }, + new (typeof(_jclass), "v") { + NoUndef = true, + }, + }; + + var mm_trace_get_class_name_sig = new LlvmIrFunctionSignature ( + name: "_mm_trace_get_class_name", + returnType: typeof(string), + parameters: mm_trace_get_class_name_params + ); + + mm_trace_get_class_name = module.DeclareExternalFunction (new LlvmIrFunction (mm_trace_get_class_name_sig, attrSet)); + + var mm_trace_get_object_class_name_params = new List { + new (typeof(IntPtr), "env") { + NoUndef = true, + }, + new (typeof(_jobject), "v") { + NoUndef = true, + }, + }; + + var mm_trace_get_object_class_name_sig = new LlvmIrFunctionSignature ( + name: "_mm_trace_get_object_class_name", + returnType: typeof(string), + parameters: mm_trace_get_object_class_name_params + ); + + mm_trace_get_object_class_name = module.DeclareExternalFunction (new LlvmIrFunction (mm_trace_get_object_class_name_sig, attrSet)); + + var mm_trace_get_c_string_params = new List { + new (typeof(IntPtr), "env") { + NoUndef = true, + }, + new (typeof(_jstring), "v") { + NoUndef = true, + }, + }; + + var mm_trace_get_c_string_sig = new LlvmIrFunctionSignature ( + name: "_mm_trace_get_c_string", + returnType: typeof(string), + parameters: mm_trace_get_c_string_params + ); + + mm_trace_get_c_string = module.DeclareExternalFunction (new LlvmIrFunction (mm_trace_get_c_string_sig, attrSet)); + + var mm_trace_get_boolean_string_params = new List { + new (typeof(bool), "v") { + NoUndef = true, + }, + }; + + var mm_trace_get_boolean_string_sig = new LlvmIrFunctionSignature ( + name: "_mm_trace_get_boolean_string", + returnType: typeof(string), + parameters: mm_trace_get_boolean_string_params + ); + mm_trace_get_boolean_string_sig.ReturnAttributes.NonNull = true; + + mm_trace_get_boolean_string = module.DeclareExternalFunction (new LlvmIrFunction (mm_trace_get_boolean_string_sig, attrSet)); + } + + LlvmIrFunctionAttributeSet MakeLlvmIntrinsicFunctionsAttributeSet (LlvmIrModule module) + { + return new LlvmIrFunctionAttributeSet { + new ArgmemonlyFunctionAttribute (), + new MustprogressFunctionAttribute (), + new NocallbackFunctionAttribute (), + new NofreeFunctionAttribute (), + new NosyncFunctionAttribute (), + new NounwindFunctionAttribute (), + new WillreturnFunctionAttribute (), + }; + } + + LlvmIrFunctionAttributeSet MakeTraceFunctionsAttributeSet (LlvmIrModule module) + { + var ret = new LlvmIrFunctionAttributeSet { + new NounwindFunctionAttribute (), + new NoTrappingMathFunctionAttribute (true), + new StackProtectorBufferSizeFunctionAttribute (8), + }; + + ret.Add (AndroidTargetArch.Arm64, new FramePointerFunctionAttribute ("non-leaf")); + ret.Add (AndroidTargetArch.Arm, new FramePointerFunctionAttribute ("all")); + ret.Add (AndroidTargetArch.X86, new FramePointerFunctionAttribute ("none")); + ret.Add (AndroidTargetArch.X86_64, new FramePointerFunctionAttribute ("none")); + + return ret; + } + + LlvmIrFunctionAttributeSet MakeFreeCallAttributeSet (LlvmIrModule module) + { + var ret = new LlvmIrFunctionAttributeSet { + new NounwindFunctionAttribute (), + }; + ret.DoNotAddTargetSpecificAttributes = true; + + return module.AddAttributeSet (ret); + } + + LlvmIrFunctionAttributeSet MakeAsprintfCallAttributeSet (LlvmIrModule module) + { + var ret = new LlvmIrFunctionAttributeSet { + new NounwindFunctionAttribute (), + }; + ret.DoNotAddTargetSpecificAttributes = true; + + return module.AddAttributeSet (ret); + } + + LlvmIrFunctionAttributeSet MakeFreeFunctionAttributeSet (LlvmIrModule module) + { + var ret = new LlvmIrFunctionAttributeSet { + new MustprogressFunctionAttribute (), + new NounwindFunctionAttribute (), + new WillreturnFunctionAttribute (), + new AllockindFunctionAttribute ("free"), + // TODO: LLVM 16+ feature, enable when we switch to this version + // new MemoryFunctionAttribute { + // Default = MemoryAttributeAccessKind.Write, + // Argmem = MemoryAttributeAccessKind.None, + // InaccessibleMem = MemoryAttributeAccessKind.None, + // }, + new AllocFamilyFunctionAttribute ("malloc"), + new NoTrappingMathFunctionAttribute (true), + new StackProtectorBufferSizeFunctionAttribute (8), + }; + + return module.AddAttributeSet (ret); + } + + LlvmIrFunctionAttributeSet MakeLlvmLifetimeAttributeSet (LlvmIrModule module) + { + var ret = new LlvmIrFunctionAttributeSet { + new NounwindFunctionAttribute (), + }; + + ret.DoNotAddTargetSpecificAttributes = true; + return module.AddAttributeSet (ret); + } +} diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.cs index 46faa8d7479..51f02322ef6 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.cs @@ -256,7 +256,7 @@ public MarshalMethodsNativeAssemblyGenerator (TaskLoggingHelper log, AndroidTarg /// /// Constructor to be used ONLY when marshal methods are ENABLED /// - public MarshalMethodsNativeAssemblyGenerator (TaskLoggingHelper log, int numberOfAssembliesInApk, ICollection uniqueAssemblyNames, NativeCodeGenState codeGenState) + public MarshalMethodsNativeAssemblyGenerator (TaskLoggingHelper log, int numberOfAssembliesInApk, ICollection uniqueAssemblyNames, NativeCodeGenState codeGenState, MarshalMethodsTracingMode tracingMode) : base (log) { this.numberOfAssembliesInApk = numberOfAssembliesInApk; @@ -264,7 +264,8 @@ public MarshalMethodsNativeAssemblyGenerator (TaskLoggingHelper log, int numberO this.codeGenState = codeGenState ?? throw new ArgumentNullException (nameof (codeGenState)); generateEmptyCode = false; - defaultCallMarker = LlvmIrCallMarker.Tail; + this.tracingMode = tracingMode; + defaultCallMarker = tracingMode != MarshalMethodsTracingMode.None ? LlvmIrCallMarker.None : LlvmIrCallMarker.Tail; } void Init () @@ -595,6 +596,10 @@ protected override void Construct (LlvmIrModule module) MapStructures (module); Init (); + if (tracingMode != MarshalMethodsTracingMode.None) { + InitTracing (module); + } + AddAssemblyImageCache (module, out AssemblyCacheState acs); // class cache @@ -685,6 +690,8 @@ void AddMarshalMethod (LlvmIrModule module, MarshalMethodInfo method, ulong asmI void WriteBody (LlvmIrFunctionBody body) { + WriteTracingAtFunctionTop (module, method, body, func, writeState); + LlvmIrLocalVariable cb1 = func.CreateLocalVariable (typeof(IntPtr), "cb1"); body.Load (backingField, cb1, tbaa: module.TbaaAnyPointer); diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsTracingMode.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsTracingMode.cs new file mode 100644 index 00000000000..1aa9e82c5d0 --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsTracingMode.cs @@ -0,0 +1,10 @@ +namespace Xamarin.Android.Tasks +{ + // Enumeration member numeric values MUST match those in src/monodroid/jni/marshal-methods-tracing.hh + public enum MarshalMethodsTracingMode + { + None = 0x00, + Basic = 0x01, + Full = 0x02, + } +} diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs index 8fb845dd9df..ccc8b59b64b 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs @@ -522,7 +522,22 @@ public static string GetRelativePathForAndroidAsset (string assetsDirectory, ITa } #endif // MSBUILD + public static MarshalMethodsTracingMode ParseMarshalMethodsTracingMode (string input) + { + if (String.IsNullOrEmpty (input) || String.Compare ("none", input, StringComparison.InvariantCultureIgnoreCase) == 0) { + return MarshalMethodsTracingMode.None; + } + + if (String.Compare ("basic", input, StringComparison.InvariantCultureIgnoreCase) == 0) { + return MarshalMethodsTracingMode.Basic; + } + if (String.Compare ("full", input, StringComparison.InvariantCultureIgnoreCase) == 0) { + return MarshalMethodsTracingMode.Full; + } + + throw new InvalidOperationException ($"Unsupported marshal methods tracing mode '{input}'"); + } /// /// Converts $(SupportedOSPlatformVersion) to an API level, as it can be a version (21.0), or an int (21). diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets index 3e2d20ceb09..fcc712ba0d3 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.targets @@ -285,29 +285,29 @@ BeforeTargets="CopyFilesToOutputDirectory" Inputs="$(MSBuildAllProjects);@(IntermediateAssembly);@(InputAssemblies)" Outputs="$(IntermediateOutputPath)ILRepacker.stamp" > - - <_InputAssembliesThatExist Include="@(InputAssemblies)" Condition="Exists('%(Identity)')" /> - <_NetstandardPath Include="@(ReferencePath->'%(RootDir)%(Directory)')" Condition="'%(FileName)%(Extension)' == 'netstandard.dll'" /> - - - <_NetstandardDir>@(_NetstandardPath) - <_ILRepackArgs>/out:"$(MSBuildThisFileDirectory)$(IntermediateOutputPath)$(AssemblyName).dll" /internalize - <_ILRepackArgs>$(_ILRepackArgs) /keyfile:"$(XamarinAndroidSourcePath)product.snk" - <_ILRepackArgs>$(_ILRepackArgs) "$(MSBuildThisFileDirectory)$(IntermediateOutputPath)$(AssemblyName).dll" - <_ILRepackArgs>$(_ILRepackArgs) @(_InputAssembliesThatExist->'"%(Identity)"', ' ') - <_ILRepackArgs>$(_ILRepackArgs) /lib:"$(_NetstandardDir.TrimEnd('\'))" - - - - - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index 5d81e3f7545..ea3fc3b1b0d 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -330,6 +330,8 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. false true True + + False <_AndroidUseMarshalMethods Condition=" '$(AndroidIncludeDebugSymbols)' == 'True' ">False <_AndroidUseMarshalMethods Condition=" '$(AndroidIncludeDebugSymbols)' != 'True' ">$(AndroidEnableMarshalMethods) @@ -1358,6 +1360,8 @@ because xbuild doesn't support framework reference assemblies. /> @@ -1721,6 +1725,7 @@ because xbuild doesn't support framework reference assemblies. RuntimeConfigBinFilePath="$(_BinaryRuntimeConfigPath)" UseAssemblyStore="$(AndroidUseAssemblyStore)" EnableMarshalMethods="$(_AndroidUseMarshalMethods)" + MarshalMethodsTracingMode="$(_AndroidMarshalMethodsTracingMode)" > @@ -2007,6 +2012,7 @@ because xbuild doesn't support framework reference assemblies. ApplicationSharedLibraries="@(_ApplicationSharedLibrary)" DebugBuild="$(AndroidIncludeDebugSymbols)" AndroidBinUtilsDirectory="$(AndroidBinUtilsDirectory)" + MarshalMethodsTracingMode="$(_AndroidMarshalMethodsTracingMode)" /> diff --git a/src/libunwind-xamarin/CMakeLists.txt b/src/libunwind-xamarin/CMakeLists.txt new file mode 100644 index 00000000000..02ad008d774 --- /dev/null +++ b/src/libunwind-xamarin/CMakeLists.txt @@ -0,0 +1,395 @@ +cmake_minimum_required(VERSION 3.19) + +# +# MUST be included before project()! +# +include("../../build-tools/cmake/xa_preamble.cmake") + +if(NOT DEFINED LIBUNWIND_LIBRARY_NAME) + message(FATAL_ERROR "Please set the LIBUNWIND_LIBRARY_NAME variable on command line (-DLIBUNWIND_LIBRARY_NAME=base_library_name)") +endif() + +if(NOT DEFINED LIBUNWIND_SOURCE_DIR) + message(FATAL_ERROR "Please set the LIBUNWIND_SOURCE_DIR variable on command line (-DLIBUNWIND_SOURCE_DIR=source_dir_path)") +endif() + +if(NOT DEFINED LIBUNWIND_OUTPUT_DIR) + message(FATAL_ERROR "Please set the LIBUNWIND_OUTPUT_DIR variable on command line (-DLIBUNWIND_OUTPUT_DIR=output_dir_path)") +endif() + +if(NOT DEFINED LIBUNWIND_HEADERS_OUTPUT_DIR) + message(FATAL_ERROR "Please set the LIBUNWIND_HEADERS_OUTPUT_DIR variable on command line (-DLIBUNWIND_HEADERS_OUTPUT_DIR=output_dir_path)") +endif() + +# +# Read libunwind version +# +set(CONFIGURE_AC "${LIBUNWIND_SOURCE_DIR}/configure.ac") +file(STRINGS "${CONFIGURE_AC}" UNWIND_MAJOR_VERSION_AC REGEX "^[ \t]*define[ \t]*\\([ \t]*pkg_major,[ \t]*(.*)\\)") +file(STRINGS "${CONFIGURE_AC}" UNWIND_MINOR_VERSION_AC REGEX "^[ \t]*define[ \t]*\\([ \t]*pkg_minor,[ \t]*(.*)\\)") +file(STRINGS "${CONFIGURE_AC}" UNWIND_EXTRA_VERSION_AC REGEX "^[ \t]*define[ \t]*\\([ \t]*pkg_extra,[ \t]*(.*)\\)") +string(REGEX REPLACE "^[ \t]*define[ \t]*\\([ \t]*pkg_major,[ \t]*(.*)\\)" "\\1" UNWIND_MAJOR_VERSION "${UNWIND_MAJOR_VERSION_AC}") +string(REGEX REPLACE "^[ \t]*define[ \t]*\\([ \t]*pkg_minor,[ \t]*(.*)\\)" "\\1" UNWIND_MINOR_VERSION "${UNWIND_MINOR_VERSION_AC}") +string(REGEX REPLACE "^[ \t]*define[ \t]*\\([ \t]*pkg_extra,[ \t]*(.*)\\)" "\\1" UNWIND_EXTRA_VERSION "${UNWIND_EXTRA_VERSION_AC}") + +set(PKG_MAJOR "${UNWIND_MAJOR_VERSION}") +set(PKG_MINOR "${UNWIND_MINOR_VERSION}") +set(PKG_EXTRA "${UNWIND_EXTRA_VERSION}") +set(PACKAGE_STRING "libunwind-xamarin") +set(PACKAGE_BUGREPORT "") + +message(STATUS "Major: ${PKG_MAJOR}; Minor: ${PKG_MINOR}; Extra: ${PKG_EXTRA}") + +project( + libunwind-xamarin +# VERSION "${PKG_MAJOR}.${PKG_MINOR}.${PKG_EXTRA}" + LANGUAGES C ASM +) + +# +# MUST be included after project()! +# +include("../../build-tools/cmake/xa_common.cmake") + +include(CheckCSourceCompiles) +include(CheckIncludeFiles) +include(CheckSymbolExists) +include("../../build-tools/cmake/xa_macros.cmake") + +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${LIBUNWIND_OUTPUT_DIR}) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${LIBUNWIND_OUTPUT_DIR}) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${LIBUNWIND_OUTPUT_DIR}) + +if(CMAKE_ANDROID_ARCH_ABI STREQUAL arm64-v8a) + set(TARGET_AARCH64 TRUE) + set(arch aarch64) +elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL armeabi-v7a) + set(TARGET_ARM TRUE) + set(arch arm) +elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL x86_64) + set(TARGET_AMD64 TRUE) + set(arch x86_64) +elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL x86) + set(TARGET_X86 TRUE) + set(arch x86) +else() + message(FATAL_ERROR "Unsupported Android ABI ${CMAKE_ANDROID_ARCH_ABI}") +endif() + +set(DSO_SYMBOL_VISIBILITY "hidden") +xa_common_prepare() + +if(CMAKE_BUILD_TYPE STREQUAL Debug) + list(APPEND LOCAL_COMPILER_ARGS -g -fno-omit-frame-pointer) +else() + list(APPEND LOCAL_COMPILER_ARGS -s -fomit-frame-pointer) + add_compile_definitions(NDEBUG) +endif() +list(APPEND LOCAL_COMPILER_ARGS + ${XA_DEFAULT_SYMBOL_VISIBILITY} + -fno-asynchronous-unwind-tables + -fno-unwind-tables +) + +xa_check_c_flags(XA_C_FLAGS "${LOCAL_COMPILER_ARGS}") +xa_check_c_linker_flags(XA_C_LINKER_FLAGS "${LOCAL_COMPILER_ARGS}") + +add_compile_options(${XA_C_FLAGS}) +add_link_options(${XA_C_LINKER_FLAGS}) + +add_compile_definitions(HAVE_CONFIG_H) +add_compile_definitions(_GNU_SOURCE) +add_compile_definitions(UNW_LOCAL_ONLY) # we don't need remote unwinding + +# Detect include files +set(CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE) + +set(HAVE_ASM_VSYSCALL_H True) +#check_include_files(asm/vsyscall.h HAVE_ASM_VSYSCALL_H) +check_include_files(byteswap.h HAVE_BYTESWAP_H) +check_include_files(elf.h HAVE_ELF_H) +check_include_files(endian.h HAVE_ENDIAN_H) +check_include_files(link.h HAVE_LINK_H) +check_include_files(sys/endian.h HAVE_SYS_ENDIAN_H) +check_include_files(sys/link.h HAVE_SYS_LINK_H) +check_include_files(sys/param.h HAVE_SYS_PARAM_H) +check_include_files(sys/syscall.h HAVE_SYS_SYSCALL_H) + +# Detect functions +check_symbol_exists(mincore "sys/mman.h" HAVE_MINCORE) +check_symbol_exists(pipe2 "fcntl.h;unistd.h" HAVE_PIPE2) + +# TODO: consider enabling zlib + +configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/config.h) +configure_file(${LIBUNWIND_SOURCE_DIR}/include/libunwind-common.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/libunwind-common.h) +configure_file(${LIBUNWIND_SOURCE_DIR}/include/libunwind.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/libunwind.h) +configure_file(${LIBUNWIND_SOURCE_DIR}/include/tdep/libunwind_i.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/tdep/libunwind_i.h) + +# The files are nearly identical, the only difference is name of the header included when remote-only libunwind is used +# We don't use it, but we still copy files to separate directories in order to avoid locking issues during parallel builds +set(HEADERS_DIR ${LIBUNWIND_HEADERS_OUTPUT_DIR}/${CMAKE_ANDROID_ARCH_ABI}) + +file(COPY ${CMAKE_CURRENT_BINARY_DIR}/include/libunwind-common.h DESTINATION ${HEADERS_DIR}) +file(COPY ${CMAKE_CURRENT_BINARY_DIR}/include/libunwind.h DESTINATION ${HEADERS_DIR}) +file(COPY ${CMAKE_CURRENT_BINARY_DIR}/include/tdep/libunwind_i.h DESTINATION ${HEADERS_DIR}/tdep/) + +include_directories(${LIBUNWIND_SOURCE_DIR}/include/tdep) +include_directories(${LIBUNWIND_SOURCE_DIR}/include) +include_directories(${LIBUNWIND_SOURCE_DIR}/src) +include_directories(${CMAKE_CURRENT_BINARY_DIR}/include/tdep) +include_directories(${CMAKE_CURRENT_BINARY_DIR}/include) + +if(TARGET_ARM) + # Ensure that the remote and local unwind code can reside in the same binary without name clashing + add_definitions("-Darm_search_unwind_table=UNW_OBJ(arm_search_unwind_table)") + # We compile code with -std=c99 and the asm keyword is not recognized as it is a gnu extension + #TODO: possibly not needed? add_definitions(-Dasm=__asm__) + # The arm sources include ex_tables.h from include/tdep-arm without going through a redirection + # in include/tdep like it works for similar files on other architectures. So we need to add + # the include/tdep-arm to include directories + include_directories(${LIBUNWIND_SOURCE_DIR}/include/tdep-arm) +elseif(TARGET_AARCH64) + # We compile code with -std=c99 and the asm keyword is not recognized as it is a gnu extension + #TODO: possibly not needed? add_definitions(-Dasm=__asm__) +endif() + +set(SOURCES_DIR ${LIBUNWIND_SOURCE_DIR}/src) + +set(LIBUNWIND_XAMARIN_SOURCES + ${SOURCES_DIR}/dwarf/Gexpr.c + ${SOURCES_DIR}/dwarf/Gfde.c + ${SOURCES_DIR}/dwarf/Gfind_proc_info-lsb.c + ${SOURCES_DIR}/dwarf/Gfind_unwind_table.c + ${SOURCES_DIR}/dwarf/Gget_proc_info_in_range.c + ${SOURCES_DIR}/dwarf/Gparser.c + ${SOURCES_DIR}/dwarf/Gpe.c + ${SOURCES_DIR}/dwarf/Lexpr.c + ${SOURCES_DIR}/dwarf/Lfde.c + ${SOURCES_DIR}/dwarf/Lfind_proc_info-lsb.c + ${SOURCES_DIR}/dwarf/Lfind_unwind_table.c + ${SOURCES_DIR}/dwarf/Lget_proc_info_in_range.c + ${SOURCES_DIR}/dwarf/Lparser.c + ${SOURCES_DIR}/dwarf/Lpe.c + ${SOURCES_DIR}/dwarf/global.c + ${SOURCES_DIR}/elfxx.c + ${SOURCES_DIR}/mi/Gaddress_validator.c + ${SOURCES_DIR}/mi/Gdestroy_addr_space.c + ${SOURCES_DIR}/mi/Gdyn-extract.c +# ${SOURCES_DIR}/mi/Gdyn-remote.c + ${SOURCES_DIR}/mi/Gfind_dynamic_proc_info.c + ${SOURCES_DIR}/mi/Gget_elf_filename.c + ${SOURCES_DIR}/mi/Gget_fpreg.c + ${SOURCES_DIR}/mi/Gget_proc_info_by_ip.c + ${SOURCES_DIR}/mi/Gget_proc_name.c + ${SOURCES_DIR}/mi/Gget_reg.c + ${SOURCES_DIR}/mi/Gput_dynamic_unwind_info.c + ${SOURCES_DIR}/mi/Gset_cache_size.c + ${SOURCES_DIR}/mi/Gset_caching_policy.c + ${SOURCES_DIR}/mi/Gset_fpreg.c + ${SOURCES_DIR}/mi/Gset_iterate_phdr_function.c + ${SOURCES_DIR}/mi/Gset_reg.c + ${SOURCES_DIR}/mi/Ldestroy_addr_space.c + ${SOURCES_DIR}/mi/Ldyn-extract.c + ${SOURCES_DIR}/mi/Lfind_dynamic_proc_info.c + ${SOURCES_DIR}/mi/Lget_accessors.c + ${SOURCES_DIR}/mi/Lget_elf_filename.c + ${SOURCES_DIR}/mi/Lget_fpreg.c + ${SOURCES_DIR}/mi/Lget_proc_info_by_ip.c + ${SOURCES_DIR}/mi/Lget_proc_name.c + ${SOURCES_DIR}/mi/Lget_reg.c + ${SOURCES_DIR}/mi/Lput_dynamic_unwind_info.c + ${SOURCES_DIR}/mi/Lset_cache_size.c + ${SOURCES_DIR}/mi/Lset_caching_policy.c + ${SOURCES_DIR}/mi/Lset_fpreg.c + ${SOURCES_DIR}/mi/Lset_iterate_phdr_function.c + ${SOURCES_DIR}/mi/Lset_reg.c + ${SOURCES_DIR}/mi/backtrace.c + ${SOURCES_DIR}/mi/dyn-cancel.c + ${SOURCES_DIR}/mi/dyn-info-list.c + ${SOURCES_DIR}/mi/dyn-register.c + ${SOURCES_DIR}/mi/flush_cache.c + ${SOURCES_DIR}/mi/init.c + ${SOURCES_DIR}/mi/mempool.c + ${SOURCES_DIR}/mi/strerror.c + ${SOURCES_DIR}/os-linux.c +) + +if(TARGET_AMD64 OR TARGET_AARCH64) + list(APPEND LIBUNWIND_XAMARIN_SOURCES + ${SOURCES_DIR}/elf64.c + ) +endif() + +if(TARGET_X86 OR TARGET_ARM) + list(APPEND LIBUNWIND_XAMARIN_SOURCES + ${SOURCES_DIR}/elf32.c + ) +endif() + +if(TARGET_X86) + list(APPEND LIBUNWIND_XAMARIN_SOURCES + ${SOURCES_DIR}/x86/Gapply_reg_state.c + ${SOURCES_DIR}/x86/Gcreate_addr_space.c + ${SOURCES_DIR}/x86/Gget_proc_info.c + ${SOURCES_DIR}/x86/Gget_save_loc.c + ${SOURCES_DIR}/x86/Gglobal.c + ${SOURCES_DIR}/x86/Ginit.c + ${SOURCES_DIR}/x86/Ginit_local.c + ${SOURCES_DIR}/x86/Ginit_remote.c + ${SOURCES_DIR}/x86/Gos-linux.c + ${SOURCES_DIR}/x86/Greg_states_iterate.c + ${SOURCES_DIR}/x86/Gregs.c + ${SOURCES_DIR}/x86/Gresume.c + ${SOURCES_DIR}/x86/Gstep.c + ${SOURCES_DIR}/x86/Lapply_reg_state.c + ${SOURCES_DIR}/x86/Lcreate_addr_space.c + ${SOURCES_DIR}/x86/Lget_proc_info.c + ${SOURCES_DIR}/x86/Lget_save_loc.c + ${SOURCES_DIR}/x86/Lglobal.c + ${SOURCES_DIR}/x86/Linit.c + ${SOURCES_DIR}/x86/Linit_local.c + ${SOURCES_DIR}/x86/Linit_remote.c + ${SOURCES_DIR}/x86/Los-linux.c + ${SOURCES_DIR}/x86/Lreg_states_iterate.c + ${SOURCES_DIR}/x86/Lregs.c + ${SOURCES_DIR}/x86/Lresume.c + ${SOURCES_DIR}/x86/Lstep.c + ${SOURCES_DIR}/x86/getcontext-linux.S + ${SOURCES_DIR}/x86/is_fpreg.c + ${SOURCES_DIR}/x86/regname.c + ) +endif(TARGET_X86) + +if(TARGET_AMD64) + list(APPEND LIBUNWIND_XAMARIN_SOURCES + ${SOURCES_DIR}/x86_64/Gapply_reg_state.c + ${SOURCES_DIR}/x86_64/Gcreate_addr_space.c + ${SOURCES_DIR}/x86_64/Gget_proc_info.c + ${SOURCES_DIR}/x86_64/Gget_save_loc.c + ${SOURCES_DIR}/x86_64/Gglobal.c + ${SOURCES_DIR}/x86_64/Ginit.c + ${SOURCES_DIR}/x86_64/Ginit_local.c + ${SOURCES_DIR}/x86_64/Ginit_remote.c + ${SOURCES_DIR}/x86_64/Gos-linux.c + ${SOURCES_DIR}/x86_64/Greg_states_iterate.c + ${SOURCES_DIR}/x86_64/Gregs.c + ${SOURCES_DIR}/x86_64/Gresume.c + ${SOURCES_DIR}/x86_64/Gstash_frame.c + ${SOURCES_DIR}/x86_64/Gstep.c + ${SOURCES_DIR}/x86_64/Gtrace.c + ${SOURCES_DIR}/x86_64/Lapply_reg_state.c + ${SOURCES_DIR}/x86_64/Lcreate_addr_space.c + ${SOURCES_DIR}/x86_64/Lget_proc_info.c + ${SOURCES_DIR}/x86_64/Lget_save_loc.c + ${SOURCES_DIR}/x86_64/Lglobal.c + ${SOURCES_DIR}/x86_64/Linit.c + ${SOURCES_DIR}/x86_64/Linit_local.c + ${SOURCES_DIR}/x86_64/Linit_remote.c + ${SOURCES_DIR}/x86_64/Los-linux.c + ${SOURCES_DIR}/x86_64/Lreg_states_iterate.c + ${SOURCES_DIR}/x86_64/Lregs.c + ${SOURCES_DIR}/x86_64/Lresume.c + ${SOURCES_DIR}/x86_64/Lstash_frame.c + ${SOURCES_DIR}/x86_64/Lstep.c + ${SOURCES_DIR}/x86_64/Ltrace.c + ${SOURCES_DIR}/x86_64/getcontext.S + ${SOURCES_DIR}/x86_64/is_fpreg.c + ${SOURCES_DIR}/x86_64/regname.c + ${SOURCES_DIR}/x86_64/setcontext.S + ) +endif() + +if(TARGET_ARM) + list(APPEND LIBUNWIND_XAMARIN_SOURCES + ${SOURCES_DIR}/arm/Gapply_reg_state.c + ${SOURCES_DIR}/arm/Gcreate_addr_space.c + ${SOURCES_DIR}/arm/Gex_tables.c + ${SOURCES_DIR}/arm/Gget_proc_info.c + ${SOURCES_DIR}/arm/Gget_save_loc.c + ${SOURCES_DIR}/arm/Gglobal.c + ${SOURCES_DIR}/arm/Ginit.c + ${SOURCES_DIR}/arm/Ginit_local.c + ${SOURCES_DIR}/arm/Ginit_remote.c + ${SOURCES_DIR}/arm/Gos-linux.c + ${SOURCES_DIR}/arm/Greg_states_iterate.c + ${SOURCES_DIR}/arm/Gregs.c + ${SOURCES_DIR}/arm/Gresume.c + ${SOURCES_DIR}/arm/Gstash_frame.c + ${SOURCES_DIR}/arm/Gstep.c + ${SOURCES_DIR}/arm/Gtrace.c + ${SOURCES_DIR}/arm/Lapply_reg_state.c + ${SOURCES_DIR}/arm/Lcreate_addr_space.c + ${SOURCES_DIR}/arm/Lex_tables.c + ${SOURCES_DIR}/arm/Lget_proc_info.c + ${SOURCES_DIR}/arm/Lget_save_loc.c + ${SOURCES_DIR}/arm/Lglobal.c + ${SOURCES_DIR}/arm/Linit.c + ${SOURCES_DIR}/arm/Linit_local.c + ${SOURCES_DIR}/arm/Linit_remote.c + ${SOURCES_DIR}/arm/Los-linux.c + ${SOURCES_DIR}/arm/Lreg_states_iterate.c + ${SOURCES_DIR}/arm/Lregs.c + ${SOURCES_DIR}/arm/Lresume.c + ${SOURCES_DIR}/arm/Lstash_frame.c + ${SOURCES_DIR}/arm/Lstep.c + ${SOURCES_DIR}/arm/Ltrace.c + ${SOURCES_DIR}/arm/getcontext.S + ${SOURCES_DIR}/arm/is_fpreg.c + ${SOURCES_DIR}/arm/regname.c + ) +endif() + +if(TARGET_AARCH64) + list(APPEND LIBUNWIND_XAMARIN_SOURCES + ${SOURCES_DIR}/aarch64/Gapply_reg_state.c + ${SOURCES_DIR}/aarch64/Gcreate_addr_space.c + ${SOURCES_DIR}/aarch64/Gget_proc_info.c + ${SOURCES_DIR}/aarch64/Gget_save_loc.c + ${SOURCES_DIR}/aarch64/Gglobal.c + ${SOURCES_DIR}/aarch64/Ginit.c + ${SOURCES_DIR}/aarch64/Ginit_local.c + ${SOURCES_DIR}/aarch64/Ginit_remote.c + ${SOURCES_DIR}/aarch64/Gis_signal_frame.c + + # Use local version with partial revert of https://github.com/libunwind/libunwind/pull/503 + # until https://github.com/libunwind/libunwind/issues/702 is fixed + # ${SOURCES_DIR}/aarch64/Gos-linux.c + fixes/aarch64/Gos-linux.c + + ${SOURCES_DIR}/aarch64/Greg_states_iterate.c + ${SOURCES_DIR}/aarch64/Gregs.c + ${SOURCES_DIR}/aarch64/Gresume.c + ${SOURCES_DIR}/aarch64/Gstash_frame.c + ${SOURCES_DIR}/aarch64/Gstep.c + ${SOURCES_DIR}/aarch64/Gtrace.c + ${SOURCES_DIR}/aarch64/Lapply_reg_state.c + ${SOURCES_DIR}/aarch64/Lcreate_addr_space.c + ${SOURCES_DIR}/aarch64/Lget_proc_info.c + ${SOURCES_DIR}/aarch64/Lget_save_loc.c + ${SOURCES_DIR}/aarch64/Lglobal.c + ${SOURCES_DIR}/aarch64/Linit.c + ${SOURCES_DIR}/aarch64/Linit_local.c + ${SOURCES_DIR}/aarch64/Linit_remote.c + ${SOURCES_DIR}/aarch64/Lis_signal_frame.c + ${SOURCES_DIR}/aarch64/Lreg_states_iterate.c + ${SOURCES_DIR}/aarch64/Lregs.c + ${SOURCES_DIR}/aarch64/Lresume.c + ${SOURCES_DIR}/aarch64/Lstash_frame.c + ${SOURCES_DIR}/aarch64/Lstep.c + ${SOURCES_DIR}/aarch64/Ltrace.c + ${SOURCES_DIR}/aarch64/getcontext.S + ${SOURCES_DIR}/aarch64/is_fpreg.c + ${SOURCES_DIR}/aarch64/regname.c + ) +endif() + +add_library(${LIBUNWIND_LIBRARY_NAME} + STATIC + ${LIBUNWIND_XAMARIN_SOURCES} +) + +target_link_options( + ${LIBUNWIND_LIBRARY_NAME} + PRIVATE ${XA_DEFAULT_SYMBOL_VISIBILITY} +) diff --git a/src/libunwind-xamarin/config.h.in b/src/libunwind-xamarin/config.h.in new file mode 100644 index 00000000000..52656bb9740 --- /dev/null +++ b/src/libunwind-xamarin/config.h.in @@ -0,0 +1,21 @@ +#if !defined (__LIBUNWIND_CONFIG_H) +#define __LIBUNWIND_CONFIG_H + +#cmakedefine01 HAVE_ELF_H +#cmakedefine01 HAVE_ENDIAN_H +#cmakedefine01 HAVE_ASM_VSYSCALL_H +#cmakedefine01 HAVE_BYTESWAP_H +#cmakedefine01 HAVE_ELF_H +#cmakedefine01 HAVE_ENDIAN_H +#cmakedefine01 HAVE_LINK_H +#cmakedefine01 HAVE_SYS_ENDIAN_H +#cmakedefine01 HAVE_SYS_LINK_H +#cmakedefine01 HAVE_SYS_PARAM_H +#cmakedefine01 HAVE_SYS_SYSCALL_H +#cmakedefine01 HAVE_MINCORE +#cmakedefine01 HAVE_PIPE2 + +#define PACKAGE_STRING "@PACKAGE_STRING@" +#define PACKAGE_BUGREPORT "@PACKAGE_BUGREPORT@" + +#endif // ndef __LIBUNWIND_CONFIG_H diff --git a/src/libunwind-xamarin/fixes/aarch64/Gos-linux.c b/src/libunwind-xamarin/fixes/aarch64/Gos-linux.c new file mode 100644 index 00000000000..fd9f266427c --- /dev/null +++ b/src/libunwind-xamarin/fixes/aarch64/Gos-linux.c @@ -0,0 +1,145 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + Copyright (C) 2011-2013 Linaro Limited + Copyright (C) 2012 Tommi Rantala + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +//#include "unwind_i.h" +#include "../../../../external/libunwind/src/aarch64/unwind_i.h" +#ifndef UNW_REMOTE_ONLY + +HIDDEN int +aarch64_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg) +{ + struct cursor *c = (struct cursor *) cursor; + unw_tdep_context_t *uc = c->uc; + + if (c->sigcontext_format == AARCH64_SCF_NONE) + { + /* Since there are no signals involved here we restore EH and non scratch + registers only. */ + unsigned long regs[24]; + regs[0] = uc->uc_mcontext.regs[0]; + regs[1] = uc->uc_mcontext.regs[1]; + regs[2] = uc->uc_mcontext.regs[2]; + regs[3] = uc->uc_mcontext.regs[3]; + regs[4] = uc->uc_mcontext.regs[19]; + regs[5] = uc->uc_mcontext.regs[20]; + regs[6] = uc->uc_mcontext.regs[21]; + regs[7] = uc->uc_mcontext.regs[22]; + regs[8] = uc->uc_mcontext.regs[23]; + regs[9] = uc->uc_mcontext.regs[24]; + regs[10] = uc->uc_mcontext.regs[25]; + regs[11] = uc->uc_mcontext.regs[26]; + regs[12] = uc->uc_mcontext.regs[27]; + regs[13] = uc->uc_mcontext.regs[28]; + regs[14] = uc->uc_mcontext.regs[29]; /* FP */ + regs[15] = uc->uc_mcontext.regs[30]; /* LR */ + regs[16] = GET_FPCTX(uc)->vregs[8]; + regs[17] = GET_FPCTX(uc)->vregs[9]; + regs[18] = GET_FPCTX(uc)->vregs[10]; + regs[19] = GET_FPCTX(uc)->vregs[11]; + regs[20] = GET_FPCTX(uc)->vregs[12]; + regs[21] = GET_FPCTX(uc)->vregs[13]; + regs[22] = GET_FPCTX(uc)->vregs[14]; + regs[23] = GET_FPCTX(uc)->vregs[15]; + unsigned long sp = uc->uc_mcontext.sp; + + struct regs_overlay { + char x[sizeof(regs)]; + }; + + __asm__ __volatile__ ( + "mov x4, %0\n" + "mov x5, %1\n" + "ldp x0, x1, [x4]\n" + "ldp x2, x3, [x4,16]\n" + "ldp x19, x20, [x4,32]\n" + "ldp x21, x22, [x4,48]\n" + "ldp x23, x24, [x4,64]\n" + "ldp x25, x26, [x4,80]\n" + "ldp x27, x28, [x4,96]\n" + "ldp x29, x30, [x4,112]\n" + "ldp d8, d9, [x4,128]\n" + "ldp d10, d11, [x4,144]\n" + "ldp d12, d13, [x4,160]\n" + "ldp d14, d15, [x4,176]\n" + "mov sp, x5\n" + "ret \n" + : + : "r" (regs), + "r" (sp), + "m" (*(struct regs_overlay *)regs) + ); + } + else + { + struct sigcontext *sc = (struct sigcontext *) c->sigcontext_addr; + + if (c->dwarf.eh_valid_mask & 0x1) sc->regs[0] = c->dwarf.eh_args[0]; + if (c->dwarf.eh_valid_mask & 0x2) sc->regs[1] = c->dwarf.eh_args[1]; + if (c->dwarf.eh_valid_mask & 0x4) sc->regs[2] = c->dwarf.eh_args[2]; + if (c->dwarf.eh_valid_mask & 0x8) sc->regs[3] = c->dwarf.eh_args[3]; + + sc->regs[4] = uc->uc_mcontext.regs[4]; + sc->regs[5] = uc->uc_mcontext.regs[5]; + sc->regs[6] = uc->uc_mcontext.regs[6]; + sc->regs[7] = uc->uc_mcontext.regs[7]; + sc->regs[8] = uc->uc_mcontext.regs[8]; + sc->regs[9] = uc->uc_mcontext.regs[9]; + sc->regs[10] = uc->uc_mcontext.regs[10]; + sc->regs[11] = uc->uc_mcontext.regs[11]; + sc->regs[12] = uc->uc_mcontext.regs[12]; + sc->regs[13] = uc->uc_mcontext.regs[13]; + sc->regs[14] = uc->uc_mcontext.regs[14]; + sc->regs[15] = uc->uc_mcontext.regs[15]; + sc->regs[16] = uc->uc_mcontext.regs[16]; + sc->regs[17] = uc->uc_mcontext.regs[17]; + sc->regs[18] = uc->uc_mcontext.regs[18]; + sc->regs[19] = uc->uc_mcontext.regs[19]; + sc->regs[20] = uc->uc_mcontext.regs[20]; + sc->regs[21] = uc->uc_mcontext.regs[21]; + sc->regs[22] = uc->uc_mcontext.regs[22]; + sc->regs[23] = uc->uc_mcontext.regs[23]; + sc->regs[24] = uc->uc_mcontext.regs[24]; + sc->regs[25] = uc->uc_mcontext.regs[25]; + sc->regs[26] = uc->uc_mcontext.regs[26]; + sc->regs[27] = uc->uc_mcontext.regs[27]; + sc->regs[28] = uc->uc_mcontext.regs[28]; + sc->regs[29] = uc->uc_mcontext.regs[29]; + sc->regs[30] = uc->uc_mcontext.regs[30]; + sc->sp = uc->uc_mcontext.sp; + sc->pc = uc->uc_mcontext.pc; + sc->pstate = uc->uc_mcontext.pstate; + + __asm__ __volatile__ ( + "mov sp, %0\n" + "ret %1\n" + : : "r" (c->sigcontext_sp), "r" (c->sigcontext_pc) + ); + } + unreachable(); + return -UNW_EINVAL; +} + +#endif /* !UNW_REMOTE_ONLY */ diff --git a/src/libunwind-xamarin/libunwind-xamarin.csproj b/src/libunwind-xamarin/libunwind-xamarin.csproj new file mode 100644 index 00000000000..b7aba4a3cfb --- /dev/null +++ b/src/libunwind-xamarin/libunwind-xamarin.csproj @@ -0,0 +1,15 @@ + + + Exe + netstandard2.0 + False + + + + + + $(MicrosoftAndroidSdkOutDir)lib + + + + diff --git a/src/libunwind-xamarin/libunwind-xamarin.targets b/src/libunwind-xamarin/libunwind-xamarin.targets new file mode 100644 index 00000000000..ff3cfd58a9d --- /dev/null +++ b/src/libunwind-xamarin/libunwind-xamarin.targets @@ -0,0 +1,54 @@ + + + + + + + <_LibUnwindBaseLibName>unwind_xamarin + <_LibUnwindLibName>lib$(_LibUnwindBaseLibName).a + <_LibUnwindHeadersOutputDir>$(LibUnwindGeneratedHeadersFullPath) + + + + + + + + <_ConfigureLibUnwindCommands + Include="@(AndroidSupportedTargetJitAbi)"> + $(CmakePath) + $(_CmakeAndroidFlags) -DANDROID_NATIVE_API_LEVEL=%(AndroidSupportedTargetJitAbi.ApiLevelNET) -DANDROID_PLATFORM=android-%(AndroidSupportedTargetJitAbi.ApiLevelNET) -DANDROID_ABI=%(AndroidSupportedTargetJitAbi.Identity) -DANDROID_RID=%(AndroidSupportedTargetJitAbi.AndroidRID) -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY="$(OutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)" -DCMAKE_LIBRARY_OUTPUT_DIRECTORY="$(OutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)" -DCMAKE_BUILD_TYPE=$(Configuration) -DLIBUNWIND_SOURCE_DIR="$(LibUnwindSourceFullPath)" -DLIBUNWIND_LIBRARY_NAME="$(_LibUnwindBaseLibName)" -DLIBUNWIND_OUTPUT_DIR="@(AndroidSupportedTargetJitAbi->'$(OutputPath)/%(AndroidRID)')" -DLIBUNWIND_HEADERS_OUTPUT_DIR="$(_LibUnwindHeadersOutputDir)" $(MSBuildThisFileDirectory) + $(IntermediateOutputPath)%(AndroidSupportedTargetJitAbi.Identity)-$(Configuration) + + + + + + + + + + + <_BuildLibUnwindCommands + Include="@(AndroidSupportedTargetJitAbi)"> + $(NinjaPath) + -v + $(IntermediateOutputPath)%(AndroidSupportedTargetJitAbi.Identity)-$(Configuration) + + + + + + + + + + + diff --git a/src/monodroid/CMakeLists.txt b/src/monodroid/CMakeLists.txt index 469a4ef5fca..53821b3e899 100644 --- a/src/monodroid/CMakeLists.txt +++ b/src/monodroid/CMakeLists.txt @@ -1,15 +1,9 @@ -cmake_minimum_required(VERSION 3.18.1) +cmake_minimum_required(VERSION 3.19) # # MUST be included before project()! # -include("../../build-tools/cmake/xa_common.cmake") - -# -# Read product version -# -file(STRINGS "../../Directory.Build.props" XA_PRODUCT_VERSION_XML REGEX "^[ \t]*(.*)") -string(REGEX REPLACE "^[ \t]*(.*)" "\\1" XA_VERSION "${XA_PRODUCT_VERSION_XML}") +include("../../build-tools/cmake/xa_preamble.cmake") project( monodroid @@ -17,76 +11,14 @@ project( DESCRIPTION "Xamarin.Android native runtime" HOMEPAGE_URL "https://github.com/xamarin/xamarin-android" LANGUAGES CXX C - ) - -option(COMPILER_DIAG_COLOR "Show compiler diagnostics/errors in color" ON) - -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -set(CMAKE_C_STANDARD 11) -set(CMAKE_C_STANDARD_REQUIRED ON) -set(CMAKE_C_EXTENSIONS OFF) - -if(CMAKE_BUILD_TYPE STREQUAL Debug) - set(DEBUG_BUILD True) -else() - set(DEBUG_BUILD False) -endif() - -set(XA_NO_INLINE "$ENV{XA_NO_INLINE}") -if(XA_NO_INLINE) - set(DONT_INLINE_DEFAULT ON) -else() - set(DONT_INLINE_DEFAULT OFF) -endif() - -set(XA_NO_STRIP "$ENV{XA_NO_STRIP}") -if(XA_NO_STRIP OR DEBUG_BUILD) - set(STRIP_DEBUG_DEFAULT OFF) -endif() - -option(ENABLE_CLANG_ASAN "Enable the clang AddressSanitizer support" OFF) -option(ENABLE_CLANG_UBSAN "Enable the clang UndefinedBehaviorSanitizer support" OFF) +) -if(ENABLE_CLANG_ASAN OR ENABLE_CLANG_UBSAN) - # ASAN and UBSAN always require the debug symbols to be left in the binary - set(STRIP_DEBUG_DEFAULT OFF) - set(ANALYZERS_ENABLED ON) -else() - if(NOT XA_NO_STRIP) - set(STRIP_DEBUG_DEFAULT ON) - endif() - set(ANALYZERS_ENABLED OFF) -endif() +# +# MUST be included after project()! +# +include("../../build-tools/cmake/xa_common.cmake") option(ENABLE_TIMING "Build with timing support" OFF) -option(STRIP_DEBUG "Strip debugging information when linking" ${STRIP_DEBUG_DEFAULT}) -option(DISABLE_DEBUG "Disable the built-in debugging code" OFF) -option(USE_CCACHE "Use ccache, if found, to speed up recompilation" ON) -option(DONT_INLINE "Do not inline any functions which are usually inlined, to get better stack traces" ${DONT_INLINE_DEFAULT}) - -if(USE_CCACHE) - if(CMAKE_CXX_COMPILER MATCHES "/ccache/") - message(STATUS "ccache: compiler already uses ccache") - else() - find_program(CCACHE ccache) - if(CCACHE) - set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE}") - set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE}") - message(STATUS "ccache: compiler will be lauched with ${CCACHE}") - endif() - endif() -endif() - - - -if(ANDROID_STL STREQUAL none) - set(USES_LIBSTDCPP False) -else() - set(USES_LIBSTDCPP True) -endif() # Environment checks @@ -112,53 +44,30 @@ if(NOT DEFINED XA_LIB_TOP_DIR) message(FATAL_ERROR "Please set the XA_LIB_TOP_DIR variable on command line (-DXA_LIB_TOP_DIR=path)") endif() +if(NOT DEFINED LIBUNWIND_SOURCE_DIR) + message(FATAL_ERROR "Please set the LIBUNWIND_SOURCE_DIR on command line (-DLIBUNWIND_SOURCE_DIR=path)") +endif() + +if(NOT DEFINED LIBUNWIND_HEADERS_DIR) + message(FATAL_ERROR "Please set the LIBUNWIND_HEADERS_DIR on command line (-DLIBUNWIND_HEADERS_DIR=path)") +endif() + # Needed modules include(CheckIncludeFile) include(CheckCXXSymbolExists) -# General config - -if(CMAKE_HOST_SYSTEM_NAME STREQUAL Linux) - set(IS_LINUX True) -else() - set(IS_LINUX False) -endif() - -if(CMAKE_HOST_SYSTEM_NAME STREQUAL Darwin) - set(IS_MACOS True) -else() - set(IS_MACOS False) -endif() - # Paths -set(EXTERNAL_DIR "../../external") -set(JAVA_INTEROP_SRC_PATH "${EXTERNAL_DIR}/Java.Interop/src/java-interop") set(SOURCES_DIR ${CMAKE_SOURCE_DIR}/jni) -set(BIONIC_SOURCES_DIR "../../src-ThirdParty/bionic") +set(BIONIC_SOURCES_DIR "${REPO_ROOT_DIR}/src-ThirdParty/bionic") set(LZ4_SRC_DIR "${EXTERNAL_DIR}/lz4/lib") set(LZ4_INCLUDE_DIR ${LZ4_SRC_DIR}) -set(XA_BIN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../bin/${XA_BUILD_CONFIGURATION}") -set(XA_BUILD_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../bin/Build${XA_BUILD_CONFIGURATION}") +set(XA_BIN_DIR "${REPO_ROOT_DIR}/bin/${XA_BUILD_CONFIGURATION}") set(ROBIN_MAP_DIR "${EXTERNAL_DIR}/robin-map") set(XXHASH_DIR "${EXTERNAL_DIR}/xxHash") set(CONSTEXPR_XXH3_DIR "${EXTERNAL_DIR}/constexpr-xxh3") -include("${XA_BUILD_DIR}/xa_build_configuration.cmake") - -if(ANDROID_ABI MATCHES "^arm64-v8a") - set(NET_RUNTIME_DIR "${NETCORE_APP_RUNTIME_DIR_ARM64}") -elseif(ANDROID_ABI MATCHES "^armeabi-v7a") - set(NET_RUNTIME_DIR "${NETCORE_APP_RUNTIME_DIR_ARM}") -elseif(ANDROID_ABI MATCHES "^x86_64") - set(NET_RUNTIME_DIR "${NETCORE_APP_RUNTIME_DIR_X86_64}") -elseif(ANDROID_ABI MATCHES "^x86") - set(NET_RUNTIME_DIR "${NETCORE_APP_RUNTIME_DIR_X86}") -else() - message(FATAL "${ANDROID_ABI} is not supported for .NET 6+ builds") -endif() - set(LZ4_SOURCES "${LZ4_SRC_DIR}/lz4.c" ) @@ -171,9 +80,8 @@ include_directories(${EXTERNAL_DIR}) # much about them, we can just as well avoid cluttered build output. include_directories(SYSTEM ${CONSTEXPR_XXH3_DIR}) include_directories(SYSTEM ${ROBIN_MAP_DIR}/include) -include_directories(SYSTEM ${CMAKE_SYSROOT}/usr/include/c++/v1/) include_directories(SYSTEM ${LZ4_INCLUDE_DIR}) -include_directories(SYSTEM "${NET_RUNTIME_DIR}/native/include/mono-2.0") + include_directories("jni") include_directories("${XA_BIN_DIR}/include") include_directories("${XA_BIN_DIR}/include/${ANDROID_ABI}/eglib") @@ -182,7 +90,6 @@ include_directories("${XA_BIN_DIR}/include/${ANDROID_ABI}/eglib") include_directories("../../bin/${CONFIGURATION}/include") include_directories("../../bin/${CONFIGURATION}/include/${ANDROID_ABI}/eglib") include_directories("${MONO_PATH}/mono/eglib") -include_directories("${JAVA_INTEROP_SRC_PATH}") # Common preparation code include("../../build-tools/cmake/xa_macros.cmake") @@ -192,10 +99,8 @@ xa_macos_prepare_arm64() # Compiler defines -add_compile_definitions(XA_VERSION="${XA_VERSION}") add_compile_definitions(TSL_NO_EXCEPTIONS) add_compile_definitions(HAVE_CONFIG_H) -add_compile_definitions(_REENTRANT) add_compile_definitions(JI_DLL_EXPORT) add_compile_definitions(MONO_DLL_EXPORT) add_compile_definitions(NET) @@ -214,15 +119,6 @@ if (ENABLE_TIMING) endif() add_compile_definitions(HAVE_LZ4) -add_compile_definitions(PLATFORM_ANDROID) - -if(ANDROID_ABI MATCHES "^(arm64-v8a|x86_64)") - add_compile_definitions(ANDROID64) -endif() - -if (ANDROID_NDK_MAJOR LESS 20) - add_compile_definitions(__ANDROID_API_Q__=29) -endif() # Compiler and linker flags set(LINK_LIBS @@ -376,6 +272,8 @@ endif() set(XAMARIN_INTERNAL_API_LIB xa-internal-api${CHECKED_BUILD_INFIX}) set(XAMARIN_DEBUG_APP_HELPER_LIB xamarin-debug-app-helper${CHECKED_BUILD_INFIX}) set(XAMARIN_APP_STUB_LIB xamarin-app) +set(XAMARIN_NATIVE_TRACING_LIB xamarin-native-tracing) +set(XAMARIN_MARSHAL_METHODS_TRACING_LIB marshal-methods-tracing) string(TOLOWER ${CMAKE_BUILD_TYPE} XAMARIN_MONO_ANDROID_SUFFIX) set(XAMARIN_MONO_ANDROID_LIB "mono-android${CHECKED_BUILD_INFIX}.${XAMARIN_MONO_ANDROID_SUFFIX}") @@ -387,6 +285,7 @@ set(XAMARIN_MONODROID_SOURCES ${JAVA_INTEROP_SRC_PATH}/java-interop-util.cc ${JAVA_INTEROP_SRC_PATH}/java-interop.cc ${LZ4_SOURCES} + ${XA_SHARED_SOURCES} ${SOURCES_DIR}/android-system.cc ${SOURCES_DIR}/basic-android-system.cc ${SOURCES_DIR}/basic-utilities.cc @@ -396,14 +295,13 @@ set(XAMARIN_MONODROID_SOURCES ${SOURCES_DIR}/embedded-assemblies-zip.cc ${SOURCES_DIR}/embedded-assemblies.cc ${SOURCES_DIR}/globals.cc - ${SOURCES_DIR}/helpers.cc ${SOURCES_DIR}/jni-remapping.cc ${SOURCES_DIR}/logger.cc ${SOURCES_DIR}/mono-log-adapter.cc ${SOURCES_DIR}/monodroid-glue.cc ${SOURCES_DIR}/monodroid-networkinfo.cc + ${SOURCES_DIR}/monodroid-tracing.cc ${SOURCES_DIR}/monovm-properties.cc - ${SOURCES_DIR}/new_delete.cc ${SOURCES_DIR}/osbridge.cc ${SOURCES_DIR}/pinvoke-override-api.cc ${SOURCES_DIR}/shared-constants.cc @@ -422,11 +320,25 @@ endif() if(NOT USES_LIBSTDCPP) list(APPEND XAMARIN_MONODROID_SOURCES ${BIONIC_SOURCES_DIR}/cxa_guard.cc - ${SOURCES_DIR}/cxx-abi/string.cc - ${SOURCES_DIR}/cxx-abi/terminate.cc + ${SHARED_SOURCES_DIR}/cxx-abi/string.cc + ${SHARED_SOURCES_DIR}/cxx-abi/terminate.cc ) endif() +set(NATIVE_TRACING_SOURCES + ${SHARED_SOURCES_DIR}/cxx-abi/string.cc +# ${SHARED_SOURCES_DIR}/cxx-abi/terminate.cc + ${TRACING_SOURCES_DIR}/native-tracing.cc + ${XA_SHARED_SOURCES} +) + +set(MARSHAL_METHODS_TRACING_SOURCES + ${SHARED_SOURCES_DIR}/cxx-abi/string.cc + ${SHARED_SOURCES_DIR}/cxx-abi/terminate.cc + ${SOURCES_DIR}/marshal-methods-tracing.cc + ${XA_SHARED_SOURCES} +) + set(XAMARIN_APP_STUB_SOURCES ${SOURCES_DIR}/application_dso_stub.cc ) @@ -436,9 +348,8 @@ set(XAMARIN_DEBUG_APP_HELPER_SOURCES ${SOURCES_DIR}/basic-utilities.cc ${SOURCES_DIR}/cpu-arch-detect.cc ${SOURCES_DIR}/debug-app-helper.cc - ${SOURCES_DIR}/helpers.cc - ${SOURCES_DIR}/new_delete.cc ${SOURCES_DIR}/shared-constants.cc + ${XA_SHARED_SOURCES} ) set(XAMARIN_STUB_LIB_SOURCES @@ -503,6 +414,77 @@ add_library( SHARED ${XAMARIN_MONODROID_SOURCES} ) +if(NOT DEBUG_BUILD) + set(CPP_ABI_PATH ${CMAKE_SYSROOT}/usr/lib/${SYSROOT_ABI_LIB_DIR}/${TOOLCHAIN_TRIPLE}/libc++abi.a) + + add_library( + ${XAMARIN_NATIVE_TRACING_LIB} + SHARED ${NATIVE_TRACING_SOURCES} + ) + + target_include_directories( + ${XAMARIN_NATIVE_TRACING_LIB} BEFORE + PRIVATE + ${LIBUNWIND_SOURCE_DIR}/include + ${LIBUNWIND_HEADERS_DIR}/${CMAKE_ANDROID_ARCH_ABI} + ${NDK_CXX_LIBCPPABI_SOURCE_PATH}/include + ) + + target_compile_options( + ${XAMARIN_NATIVE_TRACING_LIB} + PRIVATE + # Avoid the 'warning: dynamic exception specifications are deprecated' warning from libc++ headers + -Wno-deprecated-dynamic-exception-spec + ${XA_DEFAULT_SYMBOL_VISIBILITY} + # Prevent genration of the .eh_frame section (we don't use exceptions and don't need it) + -fno-asynchronous-unwind-tables + ) + + target_link_options( + ${XAMARIN_NATIVE_TRACING_LIB} + PRIVATE ${XA_DEFAULT_SYMBOL_VISIBILITY} + ) + + target_link_libraries( + ${XAMARIN_NATIVE_TRACING_LIB} + PRIVATE + -llog + ${CPP_ABI_PATH} + ${XA_LIBRARY_OUTPUT_DIRECTORY}/libunwind_xamarin.a + ) + + target_compile_definitions( + ${XAMARIN_NATIVE_TRACING_LIB} + PRIVATE + XAMARIN_TRACING + ) + + add_library( + ${XAMARIN_MARSHAL_METHODS_TRACING_LIB} + STATIC ${MARSHAL_METHODS_TRACING_SOURCES} + ) + + target_include_directories( + ${XAMARIN_MARSHAL_METHODS_TRACING_LIB} BEFORE + PRIVATE + ${LIBUNWIND_SOURCE_DIR}/include + ${LIBUNWIND_HEADERS_DIR}/${CMAKE_ANDROID_ARCH_ABI} + ) + + target_compile_definitions( + ${XAMARIN_MARSHAL_METHODS_TRACING_LIB} + PRIVATE + XAMARIN_TRACING + ) + + target_compile_options( + ${XAMARIN_MARSHAL_METHODS_TRACING_LIB} + PRIVATE + # Prevent genration of the .eh_frame section (we don't use exceptions and don't need it) + -fno-asynchronous-unwind-tables + ) +endif() + # Ugly, but this is the only way to change LZ4 symbols visibility without modifying lz4.h set(LZ4_VISIBILITY_OPTS "-DLZ4LIB_VISIBILITY=__attribute__ ((visibility (\"hidden\")))") @@ -511,6 +493,13 @@ target_compile_options( PRIVATE ${XA_DEFAULT_SYMBOL_VISIBILITY} "${LZ4_VISIBILITY_OPTS}" ) +target_include_directories( + ${XAMARIN_MONO_ANDROID_LIB} BEFORE + PRIVATE + ${LIBUNWIND_SOURCE_DIR}/include + ${LIBUNWIND_HEADERS_DIR}/${CMAKE_ANDROID_ARCH_ABI} +) + set(DEBUG_HELPER_LINK_LIBS "-ldl") target_link_options( diff --git a/src/monodroid/jni/application_dso_stub.cc b/src/monodroid/jni/application_dso_stub.cc index 54eedc4a04c..65a5b0444be 100644 --- a/src/monodroid/jni/application_dso_stub.cc +++ b/src/monodroid/jni/application_dso_stub.cc @@ -1,3 +1,4 @@ +#include #include #include @@ -22,13 +23,75 @@ const TypeMap type_map = { managed_to_java }; #else -const uint32_t map_module_count = 0; +const uint32_t map_module_count = 2; const uint32_t java_type_count = 0; -const char* const java_type_names[] = {}; +const char* const java_type_names[] = { + "java/lang/String", + "java/lang/Exception", +}; + +static TypeMapModuleEntry module1[] = { + { + .type_token_id = 1111, + .java_map_index = 0 + } +}; -TypeMapModule map_modules[] = {}; -const TypeMapJava map_java[] = {}; -const xamarin::android::hash_t map_java_hashes[] = {}; +static uint8_t module1_java_map[] = { 1, 2 }; + +static TypeMapModuleEntry module2[] = { + { + .type_token_id = 2222, + .java_map_index = 0 + } +}; + +static uint8_t module2_java_map[] = { 3, 4 }; + +TypeMapModule map_modules[] = { + { + .module_uuid = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }, + .entry_count = 1, + .duplicate_count = 0, + .map = module1, + .duplicate_map = nullptr, + .assembly_name = "Mono.Android", + .image = nullptr, + .java_name_width = 111, + .java_map = module1_java_map, + }, + + { + .module_uuid = {17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 }, + .entry_count = 1, + .duplicate_count = 0, + .map = module2, + .duplicate_map = nullptr, + .assembly_name = "System", + .image = nullptr, + .java_name_width = 222, + .java_map = module2_java_map, + }, +}; + +const TypeMapJava map_java[] = { + { + .module_index = 0, + .type_token_id = 1, + .java_name_index = 1, + }, + + { + .module_index = 1, + .type_token_id = 2, + .java_name_index = 2, + }, +}; + +const xamarin::android::hash_t map_java_hashes[] = { + 0x1, + 0x2, +}; #endif CompressedAssemblies compressed_assemblies = { @@ -68,7 +131,9 @@ const ApplicationConfig application_config = { }; const char* const mono_aot_mode_name = "normal"; -const char* const app_environment_variables[] = {}; +const char* const app_environment_variables[] = { + "name", "value", +}; const char* const app_system_properties[] = {}; static constexpr size_t AssemblyNameWidth = 128; diff --git a/src/monodroid/jni/cpp-util.hh b/src/monodroid/jni/cpp-util.hh index 8f50149eb33..66dddc1d325 100644 --- a/src/monodroid/jni/cpp-util.hh +++ b/src/monodroid/jni/cpp-util.hh @@ -43,9 +43,19 @@ namespace xamarin::android template struct CDeleter final { + using UnderlyingType = std::remove_cv_t; + void operator() (T* p) { - std::free (p); + UnderlyingType *ptr; + + if constexpr (std::is_const_v) { + ptr = const_cast*> (p); + } else { + ptr = p; + } + + std::free (reinterpret_cast(ptr)); } }; diff --git a/src/monodroid/jni/generate-pinvoke-tables.cc b/src/monodroid/jni/generate-pinvoke-tables.cc index bcc9bce2acf..e40caee9f21 100644 --- a/src/monodroid/jni/generate-pinvoke-tables.cc +++ b/src/monodroid/jni/generate-pinvoke-tables.cc @@ -61,6 +61,7 @@ const std::vector internal_pinvoke_names = { "_monodroid_gref_log_delete", "_monodroid_gref_log_new", "monodroid_log", + "monodroid_log_traces", "_monodroid_lookup_replacement_type", "_monodroid_lookup_replacement_method_info", "_monodroid_lref_log_delete", @@ -122,6 +123,8 @@ const std::vector dotnet_pinvoke_names = { "CompressionNative_InflateEnd", "CompressionNative_InflateInit2_", "CompressionNative_InflateReset", + "_kBrotliContextLookupTable", + "_kBrotliPrefixCodeRanges", // libSystem.Native.so "SystemNative_Abort", @@ -249,6 +252,7 @@ const std::vector dotnet_pinvoke_names = { "SystemNative_GetSystemTimeAsTicks", "SystemNative_GetTcpGlobalStatistics", "SystemNative_GetTimestamp", + "SystemNative_GetTimeZoneData", "SystemNative_GetUdpGlobalStatistics", "SystemNative_GetUnixRelease", "SystemNative_GetUnixVersion", @@ -517,6 +521,7 @@ const std::vector dotnet_pinvoke_names = { "CryptoNative_HmacOneShot", "CryptoNative_HmacReset", "CryptoNative_HmacUpdate", + "Java_net_dot_android_crypto_DotnetProxyTrustManager_verifyRemoteCertificate", }; template diff --git a/src/monodroid/jni/marshal-methods-tracing.cc b/src/monodroid/jni/marshal-methods-tracing.cc new file mode 100644 index 00000000000..49a319b7bf7 --- /dev/null +++ b/src/monodroid/jni/marshal-methods-tracing.cc @@ -0,0 +1,183 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "marshal-methods-tracing.hh" +#include "marshal-methods-utilities.hh" +#include "cpp-util.hh" +#include "helpers.hh" +#include "native-tracing.hh" + +using namespace xamarin::android; +using namespace xamarin::android::internal; + +constexpr int PRIORITY = ANDROID_LOG_INFO; +constexpr char LEAD[] = "MM: "; +constexpr char BOOL_TRUE[] = "true"; +constexpr char BOOL_FALSE[] = "false"; +constexpr char MISSING_ENV[] = ""; +constexpr char NULL_PARAM[] = ""; +constexpr char INTERNAL_ERROR[] = ""; +constexpr char UNKNOWN[] = ""; + +static jclass java_lang_Class; +static jmethodID java_lang_Class_getName; + +void _mm_trace_init (JNIEnv *env) noexcept +{ + if (env == nullptr || java_lang_Class != nullptr) { + return; + } + + java_lang_Class = to_gref (env, env->FindClass ("java/lang/Class")); + java_lang_Class_getName = env->GetMethodID (java_lang_Class, "getName", "()Ljava/lang/String"); + + if (env->ExceptionOccurred ()) { + env->ExceptionDescribe (); + env->ExceptionClear (); + xamarin::android::Helpers::abort_application (); + } + + bool all_found = assert_valid_jni_pointer (java_lang_Class, "class", "java.lang.Class"); + all_found &= assert_valid_jni_pointer (java_lang_Class_getName, "method", "java.lang.Class.getName ()"); + + if (!all_found) { + xamarin::android::Helpers::abort_application (); + } +} + +static void _mm_trace_func_leave_enter (JNIEnv *env, int32_t tracing_mode, uint32_t mono_image_index, uint32_t class_index, uint32_t method_token, + const char* which, const char* native_method_name, bool need_trace, const char* method_extra) noexcept +{ + uint64_t method_id = MarshalMethodsUtilities::get_method_id (mono_image_index, method_token); + const char *managed_method_name = MarshalMethodsUtilities::get_method_name (method_id); + const char *class_name = MarshalMethodsUtilities::get_class_name (class_index); + + if (need_trace && tracing_mode == TracingModeFull) { + std::string trace { LEAD }; + trace.append (which); + trace.append (": "); + trace.append (native_method_name); + if (method_extra != nullptr) { + trace.append (" "); + trace.append (method_extra); + } + trace.append (" {"); + trace.append (managed_method_name); + trace.append ("} in class "); + trace.append (class_name); + + trace.append ("\n Native stack trace:\n"); + c_unique_ptr native_trace { xa_get_native_backtrace () }; + trace.append (native_trace.get ()); + trace.append ("\n"); + + trace.append ("\n Java stack trace:\n"); + c_unique_ptr java_trace { xa_get_java_backtrace (env) }; + trace.append (java_trace.get ()); + trace.append ("\n"); + + trace.append ("\n Installed signal handlers:\n"); + c_unique_ptr signal_handlers { xa_get_interesting_signal_handlers () }; + trace.append (signal_handlers.get ()); + + __android_log_write (PRIORITY, SharedConstants::LOG_CATEGORY_NAME_MONODROID_ASSEMBLY, trace.c_str ()); + } else { + __android_log_print ( + PRIORITY, + SharedConstants::LOG_CATEGORY_NAME_MONODROID_ASSEMBLY, + "%s%s: %s%s%s {%s} in class %s", + LEAD, + which, + native_method_name, + method_extra == nullptr ? "" : " ", + method_extra == nullptr ? "" : method_extra, + managed_method_name, + class_name + ); + } +} + +void _mm_trace_func_enter (JNIEnv *env, int32_t tracing_mode, uint32_t mono_image_index, uint32_t class_index, uint32_t method_token, const char* native_method_name, const char* method_params) noexcept +{ + constexpr char ENTER[] = "ENTER"; + _mm_trace_func_leave_enter (env, tracing_mode, mono_image_index, class_index, method_token, ENTER, native_method_name, true /* need_trace */, method_params); +} + +void _mm_trace_func_leave (JNIEnv *env, int32_t tracing_mode, uint32_t mono_image_index, uint32_t class_index, uint32_t method_token, const char* native_method_name, const char* method_return_value) noexcept +{ + constexpr char LEAVE[] = "LEAVE"; + _mm_trace_func_leave_enter (env, tracing_mode, mono_image_index, class_index, method_token, LEAVE, native_method_name, false /* need_trace */, method_return_value); +} + +const char* _mm_trace_get_boolean_string (uint8_t v) noexcept +{ + return v ? BOOL_TRUE : BOOL_FALSE; +} + +char* _mm_trace_get_c_string (JNIEnv *env, jstring v) noexcept +{ + if (env == nullptr) { + return strdup (MISSING_ENV); + } + + if (v == nullptr) { + return strdup (NULL_PARAM); + } + + const char *s = env->GetStringUTFChars (v, nullptr); + char *ret = strdup (s); + env->ReleaseStringUTFChars (v, s); + + return ret; +} + +[[gnu::always_inline]] +static char* get_class_name (JNIEnv *env, jclass klass) noexcept +{ + if (java_lang_Class == nullptr || java_lang_Class_getName == nullptr) { + return strdup (INTERNAL_ERROR); + } + + auto className = static_cast(env->CallObjectMethod (klass, java_lang_Class_getName)); + if (className == nullptr) { + return strdup (UNKNOWN); + } + + return _mm_trace_get_c_string (env, className); +} + +char* _mm_trace_get_class_name (JNIEnv *env, jclass v) noexcept +{ + if (env == nullptr) { + return strdup (MISSING_ENV); + } + + if (v == nullptr) { + return strdup (NULL_PARAM); + } + + return get_class_name (env, v); +} + +char* _mm_trace_get_object_class_name (JNIEnv *env, jobject v) noexcept +{ + if (env == nullptr) { + return strdup (MISSING_ENV); + } + + if (v == nullptr) { + return strdup (NULL_PARAM); + } + + return get_class_name (env, env->GetObjectClass (v)); +} diff --git a/src/monodroid/jni/marshal-methods-tracing.hh b/src/monodroid/jni/marshal-methods-tracing.hh new file mode 100644 index 00000000000..aa725eae783 --- /dev/null +++ b/src/monodroid/jni/marshal-methods-tracing.hh @@ -0,0 +1,42 @@ +#include +#if !defined (__MARSHAL_METHODS_TRACING_HH) +#define __MARSHAL_METHODS_TRACING_HH + +#include + +#include "monodroid-glue-internal.hh" + +// These values MUST match those in the MarshalMethodsTracingMode managed enum (src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsTracingMode.cs) + +inline constexpr int32_t TracingModeNone = 0x00; +inline constexpr int32_t TracingModeBasic = 0x01; +inline constexpr int32_t TracingModeFull = 0x02; + +extern "C" { + [[gnu::visibility("hidden")]] + void _mm_trace_init (JNIEnv *env) noexcept; + + [[gnu::visibility("hidden")]] + void _mm_trace_func_enter (JNIEnv *env, int32_t tracing_mode, uint32_t mono_image_index, uint32_t class_index, uint32_t method_token, const char* native_method_name, const char* method_params) noexcept; + + [[gnu::visibility("hidden")]] + void _mm_trace_func_leave (JNIEnv *env, int32_t tracing_mode, uint32_t mono_image_index, uint32_t class_index, uint32_t method_token, const char* native_method_name, const char* method_params) noexcept; + + // Returns pointer to a dynamically allocated string, must be freed + [[gnu::visibility("hidden")]] + char* _mm_trace_get_class_name (JNIEnv *env, jclass v) noexcept; + + // Returns pointer to a dynamically allocated string, must be freed + [[gnu::visibility("hidden")]] + char* _mm_trace_get_object_class_name (JNIEnv *env, jobject v) noexcept; + + // Returns pointer to a dynamically allocated string, must be freed + [[gnu::visibility("hidden")]] + char* _mm_trace_get_c_string (JNIEnv *env, jstring v) noexcept; + + // Returns pointer to a constant string, must not be freed + [[gnu::visibility("hidden")]] + const char* _mm_trace_get_boolean_string (uint8_t v) noexcept; +} + +#endif // ndef __MARSHAL_METHODS_TRACING_HH diff --git a/src/monodroid/jni/marshal-methods-utilities.hh b/src/monodroid/jni/marshal-methods-utilities.hh new file mode 100644 index 00000000000..e141178c75b --- /dev/null +++ b/src/monodroid/jni/marshal-methods-utilities.hh @@ -0,0 +1,45 @@ +#if !defined (__MARSHAL_METHODS_UTILITIES_HH) +#define __MARSHAL_METHODS_UTILITIES_HH + +#include + +#include "xamarin-app.hh" + +#if defined (ANDROID) && defined (RELEASE) +namespace xamarin::android::internal +{ + class MarshalMethodsUtilities + { + static constexpr char Unknown[] = "Unknown"; + + public: + static const char* get_method_name (uint64_t id) noexcept + { + size_t i = 0; + while (mm_method_names[i].id != 0) { + if (mm_method_names[i].id == id) { + return mm_method_names[i].name; + } + i++; + } + + return Unknown; + } + + static const char* get_class_name (uint32_t class_index) noexcept + { + if (class_index >= marshal_methods_number_of_classes) { + return Unknown; + } + + return mm_class_names[class_index]; + } + + static uint64_t get_method_id (uint32_t mono_image_index, uint32_t method_token) noexcept + { + return (static_cast(mono_image_index) << 32) | method_token; + } + }; +} +#endif // def ANDROID && def RELEASE +#endif // ndef __MARSHAL_METHODS_UTILITIES_HH diff --git a/src/monodroid/jni/monodroid-glue-internal.hh b/src/monodroid/jni/monodroid-glue-internal.hh index abd3401671c..8f4f3fdd4ce 100644 --- a/src/monodroid/jni/monodroid-glue-internal.hh +++ b/src/monodroid/jni/monodroid-glue-internal.hh @@ -2,6 +2,7 @@ #ifndef __MONODROID_GLUE_INTERNAL_H #define __MONODROID_GLUE_INTERNAL_H +#include #include #include @@ -64,6 +65,15 @@ namespace xamarin::android::internal } }; + // Values must be identical to those in src/Mono.Android/Android.Runtime/RuntimeNativeMethods.cs + enum class TraceKind : uint32_t + { + Java = 0x01, + Managed = 0x02, + Native = 0x04, + Signals = 0x08, + }; + class MonodroidRuntime { using pinvoke_api_map = tsl::robin_map< @@ -146,6 +156,8 @@ namespace xamarin::android::internal static constexpr std::string_view mono_component_diagnostics_tracing_name { "libmono-component-diagnostics_tracing.so" }; static constexpr hash_t mono_component_diagnostics_tracing_hash = xxhash::hash (mono_component_diagnostics_tracing_name); + static constexpr char xamarin_native_tracing_name[] = "libxamarin-native-tracing.so"; + public: static constexpr int XA_LOG_COUNTERS = MONO_COUNTER_JIT | MONO_COUNTER_METADATA | MONO_COUNTER_GC | MONO_COUNTER_GENERICS | MONO_COUNTER_INTERP; @@ -190,6 +202,7 @@ namespace xamarin::android::internal void propagate_uncaught_exception (JNIEnv *env, jobject javaThread, jthrowable javaException); char* get_java_class_name_for_TypeManager (jclass klass); + void log_traces (JNIEnv *env, TraceKind kind, const char *first_line) noexcept; private: static void mono_log_handler (const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data); @@ -209,6 +222,22 @@ namespace xamarin::android::internal static PinvokeEntry* find_pinvoke_address (hash_t hash, const PinvokeEntry *entries, size_t entry_count) noexcept; static void* handle_other_pinvoke_request (const char *library_name, hash_t library_name_hash, const char *entrypoint_name, hash_t entrypoint_name_hash) noexcept; static void* monodroid_pinvoke_override (const char *library_name, const char *entrypoint_name); + + template + static void load_symbol (void *handle, const char *name, TFunc*& fnptr) noexcept + { + char *err = nullptr; + void *symptr = monodroid_dlsym (handle, name, &err, nullptr); + + if (symptr == nullptr) { + log_warn (LOG_DEFAULT, "Failed to load symbol '%s' library with handle %p. %s", name, handle, err == nullptr ? "Unknown error" : err); + fnptr = nullptr; + return; + } + + fnptr = reinterpret_cast(symptr); + } + static void* monodroid_dlopen_ignore_component_or_load (hash_t hash, const char *name, int flags, char **err) noexcept; static void* monodroid_dlopen (const char *name, int flags, char **err) noexcept; static void* monodroid_dlopen (const char *name, int flags, char **err, void *user_data) noexcept; @@ -228,6 +257,7 @@ namespace xamarin::android::internal void set_debug_options (); void parse_gdb_options (); void mono_runtime_init (JNIEnv *env, dynamic_local_string& runtime_args); + void init_android_runtime (JNIEnv *env, jclass runtimeClass, jobject loader); void set_environment_variable_for_directory (const char *name, jstring_wrapper &value, bool createDirectory, mode_t mode); @@ -261,6 +291,7 @@ namespace xamarin::android::internal static void jit_done (MonoProfiler *prof, MonoMethod *method, MonoJitInfo* jinfo); static void thread_start (MonoProfiler *prof, uintptr_t tid); static void thread_end (MonoProfiler *prof, uintptr_t tid); + #if !defined (RELEASE) static MonoReflectionType* typemap_java_to_managed (MonoString *java_type_name) noexcept; static const char* typemap_managed_to_java (MonoReflectionType *type, const uint8_t *mvid) noexcept; @@ -270,7 +301,6 @@ namespace xamarin::android::internal #if defined (RELEASE) static const char* get_method_name (uint32_t mono_image_index, uint32_t method_token) noexcept; - static const char* get_class_name (uint32_t class_index) noexcept; template static void get_function_pointer (uint32_t mono_image_index, uint32_t class_index, uint32_t method_token, void*& target_ptr) noexcept; diff --git a/src/monodroid/jni/monodroid-glue.cc b/src/monodroid/jni/monodroid-glue.cc index 1de65c5c46d..91c0d4caa14 100644 --- a/src/monodroid/jni/monodroid-glue.cc +++ b/src/monodroid/jni/monodroid-glue.cc @@ -701,13 +701,11 @@ MonodroidRuntime::mono_runtime_init ([[maybe_unused]] JNIEnv *env, [[maybe_unuse embeddedAssemblies.install_preload_hooks_for_appdomains (); #ifndef RELEASE mono_install_assembly_preload_hook (open_from_update_dir, nullptr); -#endif - -#if defined (RELEASE) +#else // ndef RELEASE if (application_config.marshal_methods_enabled) { xamarin_app_init (env, get_function_pointer_at_startup); } -#endif // def RELEASE && def ANDROID && def NET +#endif // def RELEASE } void @@ -1806,6 +1804,14 @@ MonodroidRuntime::Java_mono_android_Runtime_register (JNIEnv *env, jstring manag env->ReleaseStringChars (methods, methods_ptr); env->ReleaseStringChars (managedType, managedType_ptr); + const char *tmp = env->GetStringUTFChars (managedType, nullptr); + log_warn (LOG_ASSEMBLY, "Blazor: registering type %s", tmp); + env->ReleaseStringUTFChars (managedType, tmp); + + tmp = env->GetStringUTFChars (methods, nullptr); + log_warn (LOG_ASSEMBLY, "Blazor: methods: %s", tmp); + env->ReleaseStringUTFChars (methods, tmp); + if (FastTiming::enabled ()) [[unlikely]] { internal_timing->end_event (total_time_index, true /* uses_more_info */); diff --git a/src/monodroid/jni/monodroid-tracing.cc b/src/monodroid/jni/monodroid-tracing.cc new file mode 100644 index 00000000000..53731ff28fa --- /dev/null +++ b/src/monodroid/jni/monodroid-tracing.cc @@ -0,0 +1,89 @@ +#include +#include + +#include "java-interop-logger.h" +#include "mono/utils/details/mono-dl-fallback-types.h" +#include "monodroid-glue-internal.hh" +#include "native-tracing.hh" +#include "cppcompat.hh" +#include + +using namespace xamarin::android::internal; + +static decltype(xa_get_native_backtrace)* _xa_get_native_backtrace; +static decltype(xa_get_managed_backtrace)* _xa_get_managed_backtrace; +static decltype(xa_get_java_backtrace)* _xa_get_java_backtrace; +static decltype(xa_get_interesting_signal_handlers)* _xa_get_interesting_signal_handlers; +static bool tracing_init_done; + +static std::mutex tracing_init_lock {}; + +void +MonodroidRuntime::log_traces (JNIEnv *env, TraceKind kind, const char *first_line) noexcept +{ + if (!tracing_init_done) { + std::lock_guard lock (tracing_init_lock); + + char *err = nullptr; + void *handle = monodroid_dlopen (xamarin_native_tracing_name, MONO_DL_EAGER, &err, nullptr); + if (handle == nullptr) { + log_warn (LOG_DEFAULT, "Failed to load native tracing library '%s'. %s", xamarin_native_tracing_name, err == nullptr ? "Unknown error" : err); + } else { + load_symbol (handle, "xa_get_native_backtrace", _xa_get_native_backtrace); + load_symbol (handle, "xa_get_managed_backtrace", _xa_get_managed_backtrace); + load_symbol (handle, "xa_get_java_backtrace", _xa_get_java_backtrace); + load_symbol (handle, "xa_get_interesting_signal_handlers", _xa_get_interesting_signal_handlers); + } + + tracing_init_done = true; + } + + std::string trace; + if (first_line != nullptr) { + trace.append (first_line); + trace.append ("\n"); + } + + bool need_newline = false; + auto add_trace = [&] (c_unique_ptr const& data, const char *banner) -> void { + if (need_newline) { + trace.append ("\n "); + } else { + trace.append (" "); + } + + trace.append (banner); + if (!data) { + trace.append (": unavailable"); + } else { + trace.append (":\n"); + trace.append (data.get ()); + trace.append ("\n"); + } + need_newline = true; + }; + + if ((kind & TraceKind::Native) == TraceKind::Native) { + c_unique_ptr native { _xa_get_native_backtrace != nullptr ? _xa_get_native_backtrace () : nullptr }; + add_trace (native, "Native stacktrace"); + } + + if ((kind & TraceKind::Java) == TraceKind::Java && env != nullptr) { + c_unique_ptr java { _xa_get_java_backtrace != nullptr ?_xa_get_java_backtrace (env) : nullptr }; + add_trace (java, "Java stacktrace"); + } + + if ((kind & TraceKind::Managed) == TraceKind::Managed) { + c_unique_ptr managed { _xa_get_managed_backtrace != nullptr ? _xa_get_managed_backtrace () : nullptr }; + add_trace (managed, "Managed stacktrace"); + } + + if ((kind & TraceKind::Signals) == TraceKind::Signals) { + c_unique_ptr signals { _xa_get_interesting_signal_handlers != nullptr ? _xa_get_interesting_signal_handlers () : nullptr }; + add_trace (signals, "Signal handlers"); + } + + // Use this call because it is slightly faster (doesn't need to parse the format) and it doesn't truncate longer + // strings (like the stack traces we've just produced), unlike __android_log_vprint used by our `log_*` functions + __android_log_write (ANDROID_LOG_INFO, SharedConstants::LOG_CATEGORY_NAME_MONODROID, trace.c_str ()); +} diff --git a/src/monodroid/jni/pinvoke-override-api.cc b/src/monodroid/jni/pinvoke-override-api.cc index 94232f60045..09710484807 100644 --- a/src/monodroid/jni/pinvoke-override-api.cc +++ b/src/monodroid/jni/pinvoke-override-api.cc @@ -363,6 +363,15 @@ _monodroid_lookup_replacement_method_info (const char *jniSourceType, const char return JniRemapping::lookup_replacement_method_info (jniSourceType, jniMethodName, jniMethodSignature); } +static void +monodroid_log_traces (uint32_t kind, const char *first_line) +{ + JNIEnv *env = osBridge.ensure_jnienv (); + auto tk = static_cast(kind); + + monodroidRuntime.log_traces (env, tk, first_line); +} + #include "pinvoke-tables.include" MonodroidRuntime::pinvoke_library_map MonodroidRuntime::other_pinvoke_map (MonodroidRuntime::LIBRARY_MAP_INITIAL_BUCKET_COUNT); diff --git a/src/monodroid/jni/pinvoke-tables.include b/src/monodroid/jni/pinvoke-tables.include index 1f3c479095c..371a81d18d4 100644 --- a/src/monodroid/jni/pinvoke-tables.include +++ b/src/monodroid/jni/pinvoke-tables.include @@ -18,6 +18,7 @@ static PinvokeEntry internal_pinvokes[] = { {0x2fbe68718cf2510d, "_monodroid_get_identity_hash_code", reinterpret_cast(&_monodroid_get_identity_hash_code)}, {0x3ade4348ac8ce0fa, "_monodroid_freeifaddrs", reinterpret_cast(&_monodroid_freeifaddrs)}, {0x3b2467e7eadd4a6a, "_monodroid_lref_log_new", reinterpret_cast(&_monodroid_lref_log_new)}, + {0x3b8097af56b5361f, "monodroid_log_traces", reinterpret_cast(&monodroid_log_traces)}, {0x3c5532ecdab53f89, "set_world_accessable", reinterpret_cast(&set_world_accessable)}, {0x423c8f539a2c56d2, "_monodroid_lookup_replacement_type", reinterpret_cast(&_monodroid_lookup_replacement_type)}, {0x4b1956138764939a, "_monodroid_gref_log_new", reinterpret_cast(&_monodroid_gref_log_new)}, @@ -205,6 +206,7 @@ static PinvokeEntry dotnet_pinvokes[] = { {0x52794f1118d32f08, "SystemNative_GetUnixVersion", nullptr}, {0x52fc107ebdb6fcc7, "AndroidCryptoNative_X509StoreRemoveCertificate", nullptr}, {0x5381564d2c06c0a3, "SystemNative_SysConf", nullptr}, + {0x54ec3421ab70a40a, "Java_net_dot_android_crypto_DotnetProxyTrustManager_verifyRemoteCertificate", nullptr}, {0x556bc89d2d4dfc85, "SystemNative_GetDeviceIdentifiers", nullptr}, {0x558250d199e906bb, "CryptoNative_ErrReasonErrorString", nullptr}, {0x5592a052ceb4caf6, "SystemNative_GetProcessPath", nullptr}, @@ -234,6 +236,7 @@ static PinvokeEntry dotnet_pinvokes[] = { {0x639e6e938ec8d9f2, "SystemNative_GetPeerUserName", nullptr}, {0x6448f0806bd3a338, "SystemNative_FreeEnviron", nullptr}, {0x648a9b317bc64fe0, "AndroidCryptoNative_RsaGenerateKeyEx", nullptr}, + {0x64a5286c2f8cba88, "_kBrotliContextLookupTable", nullptr}, {0x650eddee76c6b8da, "SystemNative_GetHostName", nullptr}, {0x652badfba5d61929, "SystemNative_FcntlSetFD", nullptr}, {0x66e049fe27bf91ea, "AndroidCryptoNative_SSLSupportsApplicationProtocolsConfiguration", nullptr}, @@ -262,6 +265,7 @@ static PinvokeEntry dotnet_pinvokes[] = { {0x7356b141407d261e, "AndroidCryptoNative_EcdhDeriveKey", nullptr}, {0x742da00b2dbf435d, "SystemNative_LoadLibrary", nullptr}, {0x74ec4a8d869776ad, "AndroidCryptoNative_Aes128Ccm", nullptr}, + {0x7559feb379d38da5, "SystemNative_GetTimeZoneData", nullptr}, {0x758dfbf057da0da0, "AndroidCryptoNative_DsaSignatureFieldSize", nullptr}, {0x7975d1d7029cf1a3, "AndroidCryptoNative_Aes128Gcm", nullptr}, {0x79f5c24afbd04af1, "AndroidCryptoNative_Aes256Cbc", nullptr}, @@ -326,6 +330,7 @@ static PinvokeEntry dotnet_pinvokes[] = { {0xa56954e28eb9a9c9, "AndroidCryptoNative_Des3Cfb8", nullptr}, {0xa57e18f82abd5958, "BrotliDecoderDestroyInstance", nullptr}, {0xa5eda72b95fe78c3, "AndroidCryptoNative_X509ChainGetErrors", nullptr}, + {0xa689498245ccfb72, "_kBrotliPrefixCodeRanges", nullptr}, {0xa89b70c38d3ba079, "CryptoNative_HmacCreate", nullptr}, {0xa89ec9958d999483, "SystemNative_GetCwd", nullptr}, {0xa8bdc3e7ee898dfc, "SystemNative_Shutdown", nullptr}, @@ -540,6 +545,7 @@ static PinvokeEntry internal_pinvokes[] = { {0xd78c749d, "monodroid_get_log_categories", reinterpret_cast(&monodroid_get_log_categories)}, {0xd91f3619, "create_public_directory", reinterpret_cast(&create_public_directory)}, {0xe215a17c, "_monodroid_weak_gref_delete", reinterpret_cast(&_monodroid_weak_gref_delete)}, + {0xe4c3ee19, "monodroid_log_traces", reinterpret_cast(&monodroid_log_traces)}, {0xe7e77ca5, "_monodroid_gref_log", reinterpret_cast(&_monodroid_gref_log)}, {0xea2184e3, "_monodroid_gc_wait_for_bridge_processing", reinterpret_cast(&_monodroid_gc_wait_for_bridge_processing)}, {0xf4079b4a, "monodroid_dylib_mono_new", reinterpret_cast(&monodroid_dylib_mono_new)}, @@ -596,6 +602,7 @@ static PinvokeEntry dotnet_pinvokes[] = { {0x1bf277c4, "SystemNative_WaitForSocketEvents", nullptr}, {0x1c4778bf, "SystemNative_AlignedFree", nullptr}, {0x1cb466df, "AndroidCryptoNative_RsaGenerateKeyEx", nullptr}, + {0x1cc522bb, "_kBrotliContextLookupTable", nullptr}, {0x1cf7b52c, "SystemNative_MAdvise", nullptr}, {0x1eb6eaaa, "CryptoNative_GetRandomBytes", nullptr}, {0x1ebc63c1, "AndroidCryptoNative_X509ChainDestroyContext", nullptr}, @@ -747,12 +754,15 @@ static PinvokeEntry dotnet_pinvokes[] = { {0x70e91ddd, "SystemNative_FChMod", nullptr}, {0x71698a7f, "SystemNative_GetDomainSocketSizes", nullptr}, {0x7243c4b4, "AndroidCryptoNative_Des3Cfb8", nullptr}, + {0x74ebdcdf, "_kBrotliPrefixCodeRanges", nullptr}, + {0x758dd6aa, "SystemNative_GetTimeZoneData", nullptr}, {0x759f5b1e, "AndroidCryptoNative_Aes256Cfb8", nullptr}, {0x75b11f61, "BrotliDecoderGetErrorCode", nullptr}, {0x76e97b2e, "SystemNative_Rename", nullptr}, {0x77cb373b, "SystemNative_GetIPSocketAddressSizes", nullptr}, {0x78c1eb52, "AndroidCryptoNative_Des3Ecb", nullptr}, {0x7a0529c1, "SystemNative_InitializeTerminalAndSignalHandling", nullptr}, + {0x7aa30494, "Java_net_dot_android_crypto_DotnetProxyTrustManager_verifyRemoteCertificate", nullptr}, {0x7ad3b820, "AndroidCryptoNative_Aes192Cfb128", nullptr}, {0x7cb19137, "SystemNative_GetIcmpv6GlobalStatistics", nullptr}, {0x7d0c477d, "CryptoNative_ErrPeekLastError", nullptr}, @@ -989,5 +999,5 @@ constexpr hash_t system_io_compression_native_library_hash = 0xafe3142c; constexpr hash_t system_security_cryptography_native_android_library_hash = 0x93625cd; #endif -constexpr size_t internal_pinvokes_count = 49; -constexpr size_t dotnet_pinvokes_count = 428; +constexpr size_t internal_pinvokes_count = 50; +constexpr size_t dotnet_pinvokes_count = 432; diff --git a/src/monodroid/jni/shared-constants.hh b/src/monodroid/jni/shared-constants.hh index 2c706ae2df7..6fe136e1fcf 100644 --- a/src/monodroid/jni/shared-constants.hh +++ b/src/monodroid/jni/shared-constants.hh @@ -83,6 +83,43 @@ namespace xamarin::android::internal // Documented in NDK's comments static constexpr size_t MAX_LOGCAT_MESSAGE_LENGTH = 1023; + + static constexpr char LOG_CATEGORY_NAME_NONE[] = "*none*"; + static constexpr char LOG_CATEGORY_NAME_MONODROID[] = "monodroid"; + static constexpr char LOG_CATEGORY_NAME_MONODROID_ASSEMBLY[] ="monodroid-assembly"; + static constexpr char LOG_CATEGORY_NAME_MONODROID_DEBUG[] = "monodroid-debug"; + static constexpr char LOG_CATEGORY_NAME_MONODROID_GC[] = "monodroid-gc"; + static constexpr char LOG_CATEGORY_NAME_MONODROID_GREF[] = "monodroid-gref"; + static constexpr char LOG_CATEGORY_NAME_MONODROID_LREF[] = "monodroid-lref"; + static constexpr char LOG_CATEGORY_NAME_MONODROID_TIMING[] = "monodroid-timing"; + static constexpr char LOG_CATEGORY_NAME_MONODROID_BUNDLE[] = "monodroid-bundle"; + static constexpr char LOG_CATEGORY_NAME_MONODROID_NETWORK[] = "monodroid-network"; + static constexpr char LOG_CATEGORY_NAME_MONODROID_NETLINK[] = "monodroid-netlink"; + static constexpr char LOG_CATEGORY_NAME_ERROR[] = "*error*"; + +#if defined (__aarch64__) + static constexpr bool IsARM64 = true; +#else + static constexpr bool IsARM64 = false; +#endif + +#if defined (__arm__) + static constexpr bool IsARM32 = true; +#else + static constexpr bool IsARM32 = false; +#endif + +#if defined (__i386__) + static constexpr bool IsX86 = true; +#else + static constexpr bool IsX86 = false; +#endif + +#if defined (__x86_64__) + static constexpr bool IsX64 = true; +#else + static constexpr bool IsX64 = false; +#endif }; } #endif // __SHARED_CONSTANTS_HH diff --git a/src/monodroid/jni/xamarin-android-app-context.cc b/src/monodroid/jni/xamarin-android-app-context.cc index b4598519eb0..583bfed2141 100644 --- a/src/monodroid/jni/xamarin-android-app-context.cc +++ b/src/monodroid/jni/xamarin-android-app-context.cc @@ -5,36 +5,17 @@ #include "monodroid-glue-internal.hh" #include "mono-image-loader.hh" +#include "marshal-methods-utilities.hh" using namespace xamarin::android::internal; -static constexpr char Unknown[] = "Unknown"; - const char* MonodroidRuntime::get_method_name (uint32_t mono_image_index, uint32_t method_token) noexcept { - uint64_t id = (static_cast(mono_image_index) << 32) | method_token; + uint64_t id = MarshalMethodsUtilities::get_method_id (mono_image_index, method_token); log_debug (LOG_ASSEMBLY, "MM: looking for name of method with id 0x%llx, in mono image at index %u", id, mono_image_index); - size_t i = 0; - while (mm_method_names[i].id != 0) { - if (mm_method_names[i].id == id) { - return mm_method_names[i].name; - } - i++; - } - - return Unknown; -} - -const char* -MonodroidRuntime::get_class_name (uint32_t class_index) noexcept -{ - if (class_index >= marshal_methods_number_of_classes) { - return Unknown; - } - - return mm_class_names[class_index]; + return MarshalMethodsUtilities::get_method_name (id); } template @@ -45,7 +26,7 @@ MonodroidRuntime::get_function_pointer (uint32_t mono_image_index, uint32_t clas LOG_ASSEMBLY, "MM: Trying to look up pointer to method '%s' (token 0x%x) in class '%s' (index %u)", get_method_name (mono_image_index, method_token), method_token, - get_class_name (class_index), class_index + MarshalMethodsUtilities::get_class_name (class_index), class_index ); if (class_index >= marshal_methods_number_of_classes) [[unlikely]] { @@ -85,7 +66,7 @@ MonodroidRuntime::get_function_pointer (uint32_t mono_image_index, uint32_t clas LOG_DEFAULT, "Failed to obtain function pointer to method '%s' in class '%s'", get_method_name (mono_image_index, method_token), - get_class_name (class_index) + MarshalMethodsUtilities::get_class_name (class_index) ); log_fatal ( diff --git a/src/monodroid/monodroid.csproj b/src/monodroid/monodroid.csproj index c4dbc2d7b5c..100496fe8fc 100644 --- a/src/monodroid/monodroid.csproj +++ b/src/monodroid/monodroid.csproj @@ -6,7 +6,7 @@ Exe false - + @@ -14,8 +14,9 @@ - + + diff --git a/src/monodroid/monodroid.targets b/src/monodroid/monodroid.targets index ed667e7fc44..7d0c3010bbf 100644 --- a/src/monodroid/monodroid.targets +++ b/src/monodroid/monodroid.targets @@ -1,6 +1,7 @@ + @@ -74,43 +75,48 @@ DependsOnTargets="_ConfigureRuntimesInputs" Inputs="@(_ConfigureRuntimesInputs)" Outputs="@(_ConfigureRuntimesOutputs)"> + + + + <_CmakeLibUnwind>-DLIBUNWIND_SOURCE_DIR="$(LibUnwindSourceDirectory)" -DLIBUNWIND_HEADERS_DIR="$(LibUnwindGeneratedHeadersDirectory)" <_NoInline Condition=" '$(DoNotInlineMonodroid)' == 'true' ">-DDONT_INLINE=ON <_NoStrip Condition=" '$(DoNotStripMonodroid)' == 'true' ">-DSTRIP_DEBUG=OFF <_CmakeAndroidFlags>$(_NoInline) $(_NoStrip) --debug-output -GNinja -DCMAKE_MAKE_PROGRAM="$(NinjaPath)" -DXA_BUILD_CONFIGURATION=$(Configuration) -DXA_LIB_TOP_DIR=$(MicrosoftAndroidSdkOutDir) -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DMONO_PATH="$(MonoSourceFullPath)" -DANDROID_STL="none" -DANDROID_CPP_FEATURES="no-rtti no-exceptions" -DANDROID_TOOLCHAIN=clang -DCMAKE_TOOLCHAIN_FILE="$(AndroidNdkDirectory)/build/cmake/android.toolchain.cmake" -DANDROID_NDK=$(AndroidNdkDirectory) + <_ConfigureRuntimeCommands Include="@(AndroidSupportedTargetJitAbi)"> $(CmakePath) - $(_CmakeAndroidFlags) -DCONFIGURATION=Release -DCMAKE_BUILD_TYPE=Debug -DANDROID_NATIVE_API_LEVEL=%(AndroidSupportedTargetJitAbi.ApiLevelNET) -DANDROID_PLATFORM=android-%(AndroidSupportedTargetJitAbi.ApiLevelNET) -DANDROID_ABI=%(AndroidSupportedTargetJitAbi.Identity) -DANDROID_RID=%(AndroidSupportedTargetJitAbi.AndroidRID) -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY="$(OutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)" -DCMAKE_LIBRARY_OUTPUT_DIRECTORY="$(OutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)" "$(MSBuildThisFileDirectory)" + $(_CmakeAndroidFlags) $(_CmakeLibUnwind) -DCONFIGURATION=Release -DCMAKE_BUILD_TYPE=Debug -DANDROID_NATIVE_API_LEVEL=%(AndroidSupportedTargetJitAbi.ApiLevelNET) -DANDROID_PLATFORM=android-%(AndroidSupportedTargetJitAbi.ApiLevelNET) -DANDROID_ABI=%(AndroidSupportedTargetJitAbi.Identity) -DANDROID_RID=%(AndroidSupportedTargetJitAbi.AndroidRID) -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY="$(OutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)" -DCMAKE_LIBRARY_OUTPUT_DIRECTORY="$(OutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)" "$(MSBuildThisFileDirectory)" $(IntermediateOutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)-Debug <_ConfigureRuntimeCommands Include="@(AndroidSupportedTargetJitAbi)"> $(CmakePath) - $(_CmakeAndroidFlags) -DCONFIGURATION=Debug -DCMAKE_BUILD_TYPE=Release -DANDROID_NATIVE_API_LEVEL=%(AndroidSupportedTargetJitAbi.ApiLevelNET) -DANDROID_PLATFORM=android-%(AndroidSupportedTargetJitAbi.ApiLevelNET) -DANDROID_ABI=%(AndroidSupportedTargetJitAbi.Identity) -DANDROID_RID=%(AndroidSupportedTargetJitAbi.AndroidRID) -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY="$(OutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)" -DCMAKE_LIBRARY_OUTPUT_DIRECTORY="$(OutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)" "$(MSBuildThisFileDirectory)" + $(_CmakeAndroidFlags) $(_CmakeLibUnwind) -DCONFIGURATION=Debug -DCMAKE_BUILD_TYPE=Release -DANDROID_NATIVE_API_LEVEL=%(AndroidSupportedTargetJitAbi.ApiLevelNET) -DANDROID_PLATFORM=android-%(AndroidSupportedTargetJitAbi.ApiLevelNET) -DANDROID_ABI=%(AndroidSupportedTargetJitAbi.Identity) -DANDROID_RID=%(AndroidSupportedTargetJitAbi.AndroidRID) -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY="$(OutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)" -DCMAKE_LIBRARY_OUTPUT_DIRECTORY="$(OutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)" "$(MSBuildThisFileDirectory)" $(IntermediateOutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)-Release <_ConfigureRuntimeCommands Include="@(AndroidSupportedTargetJitAbi)"> $(CmakePath) - $(_CmakeAndroidFlags) -DCONFIGURATION=Release -DCMAKE_BUILD_TYPE=Debug -DENABLE_CLANG_ASAN=ON -DANDROID_STL="c++_static" -DANDROID_NATIVE_API_LEVEL=%(AndroidSupportedTargetJitAbi.ApiLevelNET) -DANDROID_PLATFORM=android-%(AndroidSupportedTargetJitAbi.ApiLevelNET) -DANDROID_ABI=%(AndroidSupportedTargetJitAbi.Identity) -DANDROID_RID=%(AndroidSupportedTargetJitAbi.AndroidRID) -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY="$(OutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)" -DCMAKE_LIBRARY_OUTPUT_DIRECTORY="$(OutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)" "$(MSBuildThisFileDirectory)" + $(_CmakeAndroidFlags) $(_CmakeLibUnwind) -DCONFIGURATION=Release -DCMAKE_BUILD_TYPE=Debug -DENABLE_CLANG_ASAN=ON -DANDROID_STL="c++_static" -DANDROID_NATIVE_API_LEVEL=%(AndroidSupportedTargetJitAbi.ApiLevelNET) -DANDROID_PLATFORM=android-%(AndroidSupportedTargetJitAbi.ApiLevelNET) -DANDROID_ABI=%(AndroidSupportedTargetJitAbi.Identity) -DANDROID_RID=%(AndroidSupportedTargetJitAbi.AndroidRID) -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY="$(OutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)" -DCMAKE_LIBRARY_OUTPUT_DIRECTORY="$(OutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)" "$(MSBuildThisFileDirectory)" $(IntermediateOutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)-asan-Debug <_ConfigureRuntimeCommands Include="@(AndroidSupportedTargetJitAbi)"> $(CmakePath) - $(_CmakeAndroidFlags) -DCONFIGURATION=Release -DCMAKE_BUILD_TYPE=Debug -DENABLE_CLANG_UBSAN=ON -DANDROID_STL="c++_static" -DANDROID_CPP_FEATURES="rtti exceptions" -DANDROID_NATIVE_API_LEVEL=%(AndroidSupportedTargetJitAbi.ApiLevelNET) -DANDROID_PLATFORM=android-%(AndroidSupportedTargetJitAbi.ApiLevelNET) -DANDROID_ABI=%(AndroidSupportedTargetJitAbi.Identity) -DANDROID_RID=%(AndroidSupportedTargetJitAbi.AndroidRID) -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY="$(OutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)" -DCMAKE_LIBRARY_OUTPUT_DIRECTORY="$(OutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)" "$(MSBuildThisFileDirectory)" + $(_CmakeAndroidFlags) $(_CmakeLibUnwind) -DCONFIGURATION=Release -DCMAKE_BUILD_TYPE=Debug -DENABLE_CLANG_UBSAN=ON -DANDROID_STL="c++_static" -DANDROID_CPP_FEATURES="rtti exceptions" -DANDROID_NATIVE_API_LEVEL=%(AndroidSupportedTargetJitAbi.ApiLevelNET) -DANDROID_PLATFORM=android-%(AndroidSupportedTargetJitAbi.ApiLevelNET) -DANDROID_ABI=%(AndroidSupportedTargetJitAbi.Identity) -DANDROID_RID=%(AndroidSupportedTargetJitAbi.AndroidRID) -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY="$(OutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)" -DCMAKE_LIBRARY_OUTPUT_DIRECTORY="$(OutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)" "$(MSBuildThisFileDirectory)" $(IntermediateOutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)-ubsan-Debug <_ConfigureRuntimeCommands Include="@(AndroidSupportedTargetJitAbi)"> $(CmakePath) - $(_CmakeAndroidFlags) -DCONFIGURATION=Debug -DCMAKE_BUILD_TYPE=Release -DENABLE_CLANG_ASAN=ON -DANDROID_STL="c++_static" -DANDROID_NATIVE_API_LEVEL=%(AndroidSupportedTargetJitAbi.ApiLevelNET) -DANDROID_PLATFORM=android-%(AndroidSupportedTargetJitAbi.ApiLevelNET) -DANDROID_ABI=%(AndroidSupportedTargetJitAbi.Identity) -DANDROID_RID=%(AndroidSupportedTargetJitAbi.AndroidRID) -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY="$(OutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)" -DCMAKE_LIBRARY_OUTPUT_DIRECTORY="$(OutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)" "$(MSBuildThisFileDirectory)" + $(_CmakeAndroidFlags) $(_CmakeLibUnwind) -DCONFIGURATION=Debug -DCMAKE_BUILD_TYPE=Release -DENABLE_CLANG_ASAN=ON -DANDROID_STL="c++_static" -DANDROID_NATIVE_API_LEVEL=%(AndroidSupportedTargetJitAbi.ApiLevelNET) -DANDROID_PLATFORM=android-%(AndroidSupportedTargetJitAbi.ApiLevelNET) -DANDROID_ABI=%(AndroidSupportedTargetJitAbi.Identity) -DANDROID_RID=%(AndroidSupportedTargetJitAbi.AndroidRID) -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY="$(OutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)" -DCMAKE_LIBRARY_OUTPUT_DIRECTORY="$(OutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)" "$(MSBuildThisFileDirectory)" $(IntermediateOutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)-asan-Release <_ConfigureRuntimeCommands Include="@(AndroidSupportedTargetJitAbi)"> $(CmakePath) - $(_CmakeAndroidFlags) -DCONFIGURATION=Debug -DCMAKE_BUILD_TYPE=Release -DENABLE_CLANG_UBSAN=ON -DANDROID_STL="c++_static" -DANDROID_CPP_FEATURES="rtti exceptions" -DANDROID_NATIVE_API_LEVEL=%(AndroidSupportedTargetJitAbi.ApiLevelNET) -DANDROID_PLATFORM=android-%(AndroidSupportedTargetJitAbi.ApiLevelNET) -DANDROID_ABI=%(AndroidSupportedTargetJitAbi.Identity) -DANDROID_RID=%(AndroidSupportedTargetJitAbi.AndroidRID) -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY="$(OutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)" -DCMAKE_LIBRARY_OUTPUT_DIRECTORY="$(OutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)" "$(MSBuildThisFileDirectory)" + $(_CmakeAndroidFlags) $(_CmakeLibUnwind) -DCONFIGURATION=Debug -DCMAKE_BUILD_TYPE=Release -DENABLE_CLANG_UBSAN=ON -DANDROID_STL="c++_static" -DANDROID_CPP_FEATURES="rtti exceptions" -DANDROID_NATIVE_API_LEVEL=%(AndroidSupportedTargetJitAbi.ApiLevelNET) -DANDROID_PLATFORM=android-%(AndroidSupportedTargetJitAbi.ApiLevelNET) -DANDROID_ABI=%(AndroidSupportedTargetJitAbi.Identity) -DANDROID_RID=%(AndroidSupportedTargetJitAbi.AndroidRID) -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY="$(OutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)" -DCMAKE_LIBRARY_OUTPUT_DIRECTORY="$(OutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)" "$(MSBuildThisFileDirectory)" $(IntermediateOutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)-ubsan-Release diff --git a/src/native/CMakeLists.txt b/src/native/CMakeLists.txt new file mode 100644 index 00000000000..b7a0a0daee3 --- /dev/null +++ b/src/native/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.19) + +# +# MUST be included before project()! +# +include("../../build-tools/cmake/xa_preamble.cmake") + +project( + native-libs + VERSION ${XA_VERSION} + LANGUAGES CXX +) + +# +# MUST be included after project()! +# +include("../../build-tools/cmake/xa_common.cmake") diff --git a/src/monodroid/jni/cxx-abi/string.cc b/src/native/shared/cxx-abi/string.cc similarity index 100% rename from src/monodroid/jni/cxx-abi/string.cc rename to src/native/shared/cxx-abi/string.cc diff --git a/src/monodroid/jni/cxx-abi/terminate.cc b/src/native/shared/cxx-abi/terminate.cc similarity index 100% rename from src/monodroid/jni/cxx-abi/terminate.cc rename to src/native/shared/cxx-abi/terminate.cc diff --git a/src/monodroid/jni/helpers.cc b/src/native/shared/helpers.cc similarity index 100% rename from src/monodroid/jni/helpers.cc rename to src/native/shared/helpers.cc diff --git a/src/monodroid/jni/helpers.hh b/src/native/shared/helpers.hh similarity index 97% rename from src/monodroid/jni/helpers.hh rename to src/native/shared/helpers.hh index 88d4904e92d..fe5b03ecda1 100644 --- a/src/monodroid/jni/helpers.hh +++ b/src/native/shared/helpers.hh @@ -12,7 +12,7 @@ namespace xamarin::android #define ADD_WITH_OVERFLOW_CHECK(__ret_type__, __a__, __b__) xamarin::android::Helpers::add_with_overflow_check<__ret_type__>(__FILE__, __LINE__, (__a__), (__b__)) #define MULTIPLY_WITH_OVERFLOW_CHECK(__ret_type__, __a__, __b__) xamarin::android::Helpers::multiply_with_overflow_check<__ret_type__>(__FILE__, __LINE__, (__a__), (__b__)) - class Helpers + class [[gnu::visibility("hidden")]] Helpers { public: template diff --git a/src/monodroid/jni/new_delete.cc b/src/native/shared/new_delete.cc similarity index 97% rename from src/monodroid/jni/new_delete.cc rename to src/native/shared/new_delete.cc index 074dfe35610..dcd3eb1d20d 100644 --- a/src/monodroid/jni/new_delete.cc +++ b/src/native/shared/new_delete.cc @@ -22,7 +22,9 @@ operator new (size_t size) { void* p = do_alloc (size); if (p == nullptr) { +#if !defined (XAMARIN_TRACING) log_fatal (LOG_DEFAULT, "Out of memory in the `new` operator"); +#endif xamarin::android::Helpers::abort_application (); } diff --git a/src/native/shared/platform-compat.hh b/src/native/shared/platform-compat.hh new file mode 100644 index 00000000000..c632b32480f --- /dev/null +++ b/src/native/shared/platform-compat.hh @@ -0,0 +1,16 @@ +#ifndef __PLATFORM_COMPAT_HH +#define __PLATFORM_COMPAT_HH + +#include + +static inline constexpr int DEFAULT_DIRECTORY_MODE = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + +#if defined(NO_INLINE) +#define force_inline [[gnu::noinline]] +#define inline_calls [[gnu::flatten]] +#else +#define force_inline [[gnu::always_inline]] +#define inline_calls +#endif + +#endif // __PLATFORM_COMPAT_HH diff --git a/src/native/tracing/native-tracing.cc b/src/native/tracing/native-tracing.cc new file mode 100644 index 00000000000..1111c1adeca --- /dev/null +++ b/src/native/tracing/native-tracing.cc @@ -0,0 +1,336 @@ +#include +#include +#include + +#include +#include + +#include + +#include "native-tracing.hh" +#include "shared-constants.hh" +#include "cppcompat.hh" + +constexpr int PRIORITY = ANDROID_LOG_INFO; + +static void append_frame_number (std::string &trace, size_t count) noexcept; +static unw_word_t adjust_address (unw_word_t addr) noexcept; +static void init_jni (JNIEnv *env) noexcept; + +// java.lang.Thread +static jclass java_lang_Thread; +static jmethodID java_lang_Thread_currentThread; +static jmethodID java_lang_Thread_getStackTrace; + +// java.lang.StackTraceElement +static jclass java_lang_StackTraceElement; +static jmethodID java_lang_StackTraceElement_toString; + +static std::mutex java_init_lock; + +const char* xa_get_managed_backtrace () noexcept +{ + std::string trace { "TODO: implement" }; + + return strdup (trace.c_str ()); +} + +const char* xa_get_native_backtrace () noexcept +{ + constexpr int FRAME_OFFSET_WIDTH = sizeof(uintptr_t) * 2; + + unw_cursor_t cursor; + unw_context_t uc; + unw_word_t ip; + unw_word_t offp; + std::array name_buf; + std::array num_buf; // Enough for text representation of a decimal 64-bit integer + some possible + // additions (sign, padding, punctuation etc) + const char *symbol_name; + Dl_info info; + + unw_getcontext (&uc); + unw_init_local (&cursor, &uc); + + size_t frame_counter = 0; + + std::string trace; + while (unw_step (&cursor) > 0) { + if (!trace.empty ()) { + trace.append ("\n"); + } + + unw_get_reg (&cursor, UNW_REG_IP, &ip); + ip = adjust_address (ip); + + auto ptr = reinterpret_cast(ip); + const char *fname = nullptr; + const void *symptr = nullptr; + unw_word_t frame_offset = 0; + bool info_valid = false; + + if (dladdr (ptr, &info) != 0) { + if (info.dli_fname != nullptr) { + fname = info.dli_fname; + } + symptr = info.dli_sname; + frame_offset = ip - reinterpret_cast(info.dli_fbase); + info_valid = true; + } else { + frame_offset = ip; + } + + append_frame_number (trace, frame_counter++); + + std::snprintf (num_buf.data (), num_buf.size (), "%0*zx (", FRAME_OFFSET_WIDTH, frame_offset); + trace.append (num_buf.data ()); + std::snprintf (num_buf.data (), num_buf.size (), "%p) ", ptr); + trace.append (num_buf.data ()); + + // TODO: consider searching /proc/self/maps for the beginning of the corresponding region to calculate the + // correct offset (like done in bionic stack trace) + trace.append (fname != nullptr ? fname : "[anonymous]"); + + bool symbol_name_allocated = false; + offp = 0; + if (unw_get_proc_name (&cursor, name_buf.data (), name_buf.size (), &offp) == 0) { + symbol_name = name_buf.data (); + } else if (info_valid && info.dli_sname != nullptr) { + symbol_name = info.dli_sname; + } else { + symbol_name = nullptr; + } + offp = adjust_address (offp); + + if (symbol_name != nullptr) { + char *demangled_symbol_name; + int demangle_status; + + // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#demangler + demangled_symbol_name = abi::__cxa_demangle (symbol_name, nullptr, nullptr, &demangle_status); + symbol_name_allocated = demangle_status == 0 && demangled_symbol_name != nullptr; + if (symbol_name_allocated) { + symbol_name = demangled_symbol_name; + } + } + + if (symbol_name != nullptr) { + trace.append (" "); + trace.append (symbol_name); + if (offp != 0) { + trace.append (" + "); + std::snprintf (num_buf.data (), num_buf.size (), "%zu", offp); + trace.append (num_buf.data ()); + } + } + + if (symptr != nullptr) { + trace.append (" (symaddr: "); + std::snprintf (num_buf.data (), num_buf.size (), "%p", symptr); + trace.append (num_buf.data ()); + trace.append (")"); + } + + if (symbol_name_allocated && symbol_name != nullptr) { + std::free (reinterpret_cast(const_cast(symbol_name))); + } + } + + return strdup (trace.c_str ()); +} + +const char* xa_get_java_backtrace (JNIEnv *env) noexcept +{ + init_jni (env); + + // TODO: error handling + jobject current_thread = env->CallStaticObjectMethod (java_lang_Thread, java_lang_Thread_currentThread); + auto stack_trace_array = static_cast(env->CallNonvirtualObjectMethod (current_thread, java_lang_Thread, java_lang_Thread_getStackTrace)); + jsize nframes = env->GetArrayLength (stack_trace_array); + std::string trace; + + for (jsize i = 0; i < nframes; i++) { + jobject frame = env->GetObjectArrayElement (stack_trace_array, i); + auto frame_desc_java = static_cast(env->CallObjectMethod (frame, java_lang_StackTraceElement_toString)); + const char *frame_desc = env->GetStringUTFChars (frame_desc_java, nullptr); + + if (!trace.empty ()) { + trace.append ("\n"); + } + + append_frame_number (trace, i); + trace.append (frame_desc); + env->ReleaseStringUTFChars (frame_desc_java, frame_desc); + } + + return strdup (trace.c_str ()); +} + +[[gnu::always_inline]] +unw_word_t adjust_address (unw_word_t addr) noexcept +{ + // This is what bionic does, let's do the same so that our backtrace addresses match bionic output + // Code copied verbatim from + // https://android.googlesource.com/platform/bionic/+/refs/tags/android-13.0.0_r37/libc/bionic/execinfo.cpp#50 + if (addr != 0) { +#if defined (__arm__) + // If the address is suspiciously low, do nothing to avoid a segfault trying + // to access this memory. + if (addr >= 4096) { + // Check bits [15:11] of the first halfword assuming the instruction + // is 32 bits long. If the bits are any of these values, then our + // assumption was correct: + // b11101 + // b11110 + // b11111 + // Otherwise, this is a 16 bit instruction. + uint16_t value = (*reinterpret_cast(addr - 2)) >> 11; + if (value == 0x1f || value == 0x1e || value == 0x1d) { + return addr - 4; + } + + return addr - 2; + } +#elif defined (__aarch64__) + // All instructions are 4 bytes long, skip back one instruction. + return addr - 4; +#elif defined (__i386__) || defined (__x86_64__) + // It's difficult to decode exactly where the previous instruction is, + // so subtract 1 to estimate where the instruction lives. + return addr - 1; +#endif + } + + return addr; +} + +const char* xa_get_interesting_signal_handlers () noexcept +{ + constexpr char SA_SIGNAL[] = "signal"; + constexpr char SA_SIGACTION[] = "sigaction"; + constexpr char SIG_IGNORED[] = "[ignored]"; + + std::array num_buf; + Dl_info info; + struct sigaction cur_sa; + std::string trace; + + for (int i = 0; i < _NSIG; i++) { + if (sigaction (i, nullptr, &cur_sa) != 0) { + continue; // ignore + } + + void *handler; + const char *installed_with; + if (cur_sa.sa_flags & SA_SIGINFO) { + handler = reinterpret_cast(cur_sa.sa_sigaction); + installed_with = SA_SIGACTION; + } else { + handler = reinterpret_cast(cur_sa.sa_handler); + installed_with = SA_SIGNAL; + } + + if (handler == SIG_DFL) { + continue; + } + + if (!trace.empty ()) { + trace.append ("\n"); + } + + const char *symbol_name = nullptr; + const char *file_name = nullptr; + if (handler == SIG_IGN) { + symbol_name = SIG_IGNORED; + } else { + if (dladdr (handler, &info) != 0) { + symbol_name = info.dli_sname; + file_name = info.dli_fname; + } + } + + trace.append (" "); + trace.append (strsignal (i)); + trace.append (" ("); + std::snprintf (num_buf.data (), num_buf.size (), "%d", i); + trace.append (num_buf.data ()); + trace.append ("), with "); + trace.append (installed_with); + trace.append (": "); + + if (file_name != nullptr) { + trace.append (file_name); + trace.append (" "); + } + + if (symbol_name == nullptr) { + std::snprintf (num_buf.data (), num_buf.size (), "%p", handler); + trace.append (num_buf.data ()); + } else { + trace.append (symbol_name); + } + } + + return strdup (trace.c_str ()); +} + +[[gnu::always_inline]] +void append_frame_number (std::string &trace, size_t count) noexcept +{ + std::array num_buf; // Enough for text representation of a decimal 64-bit integer + some possible + // additions (sign, padding, punctuation etc) + trace.append (" #"); + std::snprintf (num_buf.data (), num_buf.size (), "%-3zu: ", count); + trace.append (num_buf.data ()); +} + +void init_jni (JNIEnv *env) noexcept +{ + // We might be called more than once, ignore all but the first call + if (java_lang_Thread != nullptr) { + return; + } + + std::lock_guard lock (java_init_lock); + + java_lang_Thread = to_gref (env, env->FindClass ("java/lang/Thread")); + java_lang_Thread_currentThread = env->GetStaticMethodID (java_lang_Thread, "currentThread", "()Ljava/lang/Thread;"); + java_lang_Thread_getStackTrace = env->GetMethodID (java_lang_Thread, "getStackTrace", "()[Ljava/lang/StackTraceElement;"); + java_lang_StackTraceElement = to_gref (env, env->FindClass ("java/lang/StackTraceElement")); + java_lang_StackTraceElement_toString = env->GetMethodID (java_lang_StackTraceElement, "toString", "()Ljava/lang/String;"); + + // We check for the Java exception and possible null pointers only here, since all the calls JNI before the last one + // would do the exception check for us. + if (env->ExceptionOccurred ()) { + env->ExceptionDescribe (); + env->ExceptionClear (); + xamarin::android::Helpers::abort_application (); + } + + bool all_found = assert_valid_jni_pointer (java_lang_Thread, "class", "java.lang.Thread"); + all_found &= assert_valid_jni_pointer (java_lang_Thread_currentThread, "method", "java.lang.Thread.currentThread ()"); + all_found &= assert_valid_jni_pointer (java_lang_Thread_getStackTrace, "method", "java.lang.Thread.getStackTrace ()"); + all_found &= assert_valid_jni_pointer (java_lang_Thread, "class", "java.lang.StackTraceElement"); + all_found &= assert_valid_jni_pointer (java_lang_Thread_currentThread, "method", "java.lang.StackTraceElement.toString ()"); + + if (!all_found) { + xamarin::android::Helpers::abort_application (); + } +} + +bool assert_valid_jni_pointer (void *o, const char *missing_kind, const char *missing_name) noexcept +{ + if (o != nullptr) { + return true; + } + + __android_log_print ( + PRIORITY, + xamarin::android::internal::SharedConstants::LOG_CATEGORY_NAME_MONODROID_ASSEMBLY, + "missing Java %s: %s", + missing_kind, + missing_name + ); + + return false; +} diff --git a/src/native/tracing/native-tracing.hh b/src/native/tracing/native-tracing.hh new file mode 100644 index 00000000000..6f08e7273b3 --- /dev/null +++ b/src/native/tracing/native-tracing.hh @@ -0,0 +1,46 @@ +#if !defined (__NATIVE_TRACING_HH) +#define __NATIVE_TRACING_HH + +#include +#include +#include + +#define UNW_LOCAL_ONLY +#include + +// Public API must not expose any types that are part of libc++ - we don't know what version of the +// library (if any) is used by the application we're embedded in. +// +// For the same reason, we cannot return memory allocated with the `new` operator - the implementation +// used by the application's C++ code might be incompatible. For this reason, any dynamically allocated +// memory we return to the caller is allocated with the libc's `malloc` +// +extern "C" { + [[gnu::visibility("default")]] + const char* xa_get_native_backtrace () noexcept; + + [[gnu::visibility("default")]] + const char* xa_get_java_backtrace (JNIEnv *env) noexcept; + + [[gnu::visibility("default")]] + const char* xa_get_managed_backtrace () noexcept; + + [[gnu::visibility("default")]] + const char* xa_get_interesting_signal_handlers () noexcept; +} + +template +[[gnu::always_inline]] +inline TJavaPointer to_gref (JNIEnv *env, TJavaPointer lref) noexcept +{ + if (lref == nullptr) { + return nullptr; + } + + auto ret = static_cast (env->NewGlobalRef (lref)); + env->DeleteLocalRef (lref); + return ret; +} + +bool assert_valid_jni_pointer (void *o, const char *missing_kind, const char *missing_name) noexcept; +#endif // ndef __NATIVE_TRACING_HH