From 660fb3397a157b2ac2c19841dc0a3be53d5b5869 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 20 Oct 2022 00:35:23 +0200 Subject: [PATCH 01/17] [WIP] Build lib{c,m} stubs Needed for proper linking of AOT libraries --- .../installers/unix-binutils.projitems | 9 +++ src/monodroid/CMakeLists.txt | 59 +++++++++++++++++++ src/monodroid/libstub/stub.cc | 8 +++ 3 files changed, 76 insertions(+) create mode 100644 src/monodroid/libstub/stub.cc diff --git a/build-tools/installers/unix-binutils.projitems b/build-tools/installers/unix-binutils.projitems index 2c29381728a..64c23f18a4c 100644 --- a/build-tools/installers/unix-binutils.projitems +++ b/build-tools/installers/unix-binutils.projitems @@ -24,6 +24,15 @@ <_BinUtilsFilesUnixSignAndHarden Include="$(MicrosoftAndroidSdkOutDir)$(HostOS)\binutils\bin\x86_64-linux-android-ld" /> <_BinUtilsFilesUnixSignAndHarden Include="$(MicrosoftAndroidSdkOutDir)$(HostOS)\binutils\bin\x86_64-linux-android-strip" /> + <_BinUtilsFilesUnixSign Include="$(MicrosoftAndroidSdkOutDir)\libstubs\android-arm64\libc.so" /> + <_BinUtilsFilesUnixSign Include="$(MicrosoftAndroidSdkOutDir)\libstubs\android-arm64\libm.so" /> + <_BinUtilsFilesUnixSign Include="$(MicrosoftAndroidSdkOutDir)\libstubs\android-arm\libc.so" /> + <_BinUtilsFilesUnixSign Include="$(MicrosoftAndroidSdkOutDir)\libstubs\android-arm\libm.so" /> + <_BinUtilsFilesUnixSign Include="$(MicrosoftAndroidSdkOutDir)\libstubs\android-x64\libc.so" /> + <_BinUtilsFilesUnixSign Include="$(MicrosoftAndroidSdkOutDir)\libstubs\android-x64\libm.so" /> + <_BinUtilsFilesUnixSign Include="$(MicrosoftAndroidSdkOutDir)\libstubs\android-x86\libc.so" /> + <_BinUtilsFilesUnixSign Include="$(MicrosoftAndroidSdkOutDir)\libstubs\android-x86\libm.so" /> + <_BinUtilsFilesUnixSign Include="$(MicrosoftAndroidSdkOutDir)$(HostOS)\binutils\lib\liblldCOFF.$(_LlvmLibExtension)" /> <_BinUtilsFilesUnixSign Include="$(MicrosoftAndroidSdkOutDir)$(HostOS)\binutils\lib\liblldCommon.$(_LlvmLibExtension)" /> <_BinUtilsFilesUnixSign Include="$(MicrosoftAndroidSdkOutDir)$(HostOS)\binutils\lib\liblldELF.$(_LlvmLibExtension)" /> diff --git a/src/monodroid/CMakeLists.txt b/src/monodroid/CMakeLists.txt index 7f45848792b..dbbf20796c1 100644 --- a/src/monodroid/CMakeLists.txt +++ b/src/monodroid/CMakeLists.txt @@ -427,6 +427,7 @@ endif() if(ANDROID) if(ENABLE_NET) set(XA_LIBRARY_OUTPUT_DIRECTORY "${XA_LIB_TOP_DIR}/lib/${ANDROID_RID}") + set(XA_LIBRARY_STUBS_OUTPUT_DIRECTORY "${XA_LIB_TOP_DIR}/libstubs/${ANDROID_RID}") link_directories("${NET_RUNTIME_DIR}/native") else() set(XA_LIBRARY_OUTPUT_DIRECTORY "${XA_LIB_TOP_DIR}/lib/${ANDROID_ABI}") @@ -568,6 +569,10 @@ set(XAMARIN_DEBUG_APP_HELPER_SOURCES ${SOURCES_DIR}/shared-constants.cc ) +set(XAMARIN_STUB_LIB_SOURCES + libstub/stub.cc +) + # Build configure_file(jni/host-config.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/host-config.h) @@ -714,3 +719,57 @@ target_link_libraries( ${XAMARIN_MONO_ANDROID_LIB} ${LINK_LIBS} xamarin-app ) + +if(ANDROID AND ENABLE_NET) + add_library( + c + SHARED ${XAMARIN_STUB_LIB_SOURCES} + ) + + target_compile_definitions( + c + PRIVATE STUB_LIB_NAME=libc + ) + + target_compile_options( + c + PRIVATE -nostdlib + ) + + target_link_options( + c + PRIVATE -nostdlib + ) + + set_target_properties( + c + PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${XA_LIBRARY_STUBS_OUTPUT_DIRECTORY}" + ) + + add_library( + m + SHARED ${XAMARIN_STUB_LIB_SOURCES} + ) + + target_compile_definitions( + m + PRIVATE STUB_LIB_NAME=libm + ) + + target_compile_options( + m + PRIVATE -nostdlib + ) + + target_link_options( + m + PRIVATE -nostdlib + ) + + set_target_properties( + m + PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${XA_LIBRARY_STUBS_OUTPUT_DIRECTORY}" + ) +endif() diff --git a/src/monodroid/libstub/stub.cc b/src/monodroid/libstub/stub.cc new file mode 100644 index 00000000000..be0ec6b7ec7 --- /dev/null +++ b/src/monodroid/libstub/stub.cc @@ -0,0 +1,8 @@ +#if !defined (STUB_LIB_NAME) +#error STUB_LIB_NAME must be defined on command line +#endif + +void STUB_LIB_NAME () +{ + // no-op +} From 86c9dcf9f5b9c02ab00f8095ac0ade97df2ef23d Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 20 Oct 2022 15:38:33 +0200 Subject: [PATCH 02/17] Works locally, let's see --- .../Tasks/GetAotArguments.cs | 55 +++++++++++++++---- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs index 29850857e0c..68c5f4e5c53 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs @@ -255,6 +255,7 @@ string GetLdFlags (NdkTools ndk, AndroidTargetArch arch, int level, string toolP { var toolchainPath = toolPrefix.Substring (0, toolPrefix.LastIndexOf (Path.DirectorySeparatorChar)); var ldFlags = new StringBuilder (); + var libs = new List (); if (UseAndroidNdk && EnableLLVM) { string androidLibPath = string.Empty; try { @@ -273,7 +274,6 @@ string GetLdFlags (NdkTools ndk, AndroidTargetArch arch, int level, string toolP } else toolchainLibDir = GetNdkToolchainLibraryDir (ndk, toolchainPath); - var libs = new List (); if (ndk.UsesClang) { if (!String.IsNullOrEmpty (toolchainLibDir)) { libs.Add ($"-L{toolchainLibDir.TrimEnd ('\\')}"); @@ -292,26 +292,39 @@ string GetLdFlags (NdkTools ndk, AndroidTargetArch arch, int level, string toolP } libs.Add (Path.Combine (androidLibPath, "libc.so")); libs.Add (Path.Combine (androidLibPath, "libm.so")); - - ldFlags.Append ($"\\\"{string.Join ("\\\";\\\"", libs)}\\\""); } else { if (ldFlags.Length > 0) { ldFlags.Append (' '); } - // - // This flag is needed for Mono AOT to work correctly with the LLVM 14 `lld` linker due to the following change: - // - // The AArch64 port now supports adrp+ldr and adrp+add optimizations. --no-relax can suppress the optimization. - // - // Without the flag, `lld` will modify AOT-generated code in a way that the Mono runtime doesn't support. Until - // the runtime issue is fixed, we need to pass this flag then. - // if (!UseAndroidNdk) { + // + // This flag is needed for Mono AOT to work correctly with the LLVM 14 `lld` linker due to the following change: + // + // The AArch64 port now supports adrp+ldr and adrp+add optimizations. --no-relax can suppress the optimization. + // + // Without the flag, `lld` will modify AOT-generated code in a way that the Mono runtime doesn't support. Until + // the runtime issue is fixed, we need to pass this flag then. + // ldFlags.Append ("--no-relax"); + + // We need to link against libc and libm, but since NDK is not in use, the linker won't be able to find the actual Android libraries. + // Therefore, we will use their stubs to satisfy the linker. At runtime they will, of course, use the actual Android libraries. + string libstubsPath = Path.Combine (AndroidBinUtilsDirectory, "..", "..", "..", "libstubs", ArchToRid (arch)); + + libs.Add (Path.Combine (libstubsPath, "libc.so")); + libs.Add (Path.Combine (libstubsPath, "libm.so")); } } + if (ldFlags.Length > 0) { + ldFlags.Append (' '); + } + + if (libs.Count > 0) { + ldFlags.Append ($"\\\"{string.Join ("\\\";\\\"", libs)}\\\""); + } + if (StripLibraries) { if (ldFlags.Length > 0) { ldFlags.Append (' '); @@ -320,6 +333,26 @@ string GetLdFlags (NdkTools ndk, AndroidTargetArch arch, int level, string toolP } return ldFlags.ToString (); + + string ArchToRid (AndroidTargetArch arch) + { + switch (arch) { + case AndroidTargetArch.Arm64: + return "android-arm64"; + + case AndroidTargetArch.Arm: + return "android-arm"; + + case AndroidTargetArch.X86: + return "android-x86"; + + case AndroidTargetArch.X86_64: + return "android-x64"; + + default: + throw new InvalidOperationException ($"Internal error: unsupported ABI '{arch}'"); + } + } } static string GetNdkToolchainLibraryDir (NdkTools ndk, string binDir, string archDir = null) From 8a3b808430a072ee3cca8f0498e790419140c1af Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 20 Oct 2022 22:17:33 +0200 Subject: [PATCH 03/17] Quote spaces in `lib{c,m}.so` paths and add a test --- .../Tasks/GetAotArguments.cs | 2 +- .../Xamarin.Android.Build.Tests/BuildTest.cs | 35 ++++++++++++++++ .../Utilities/ELFHelper.cs | 42 +++++++++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs index 68c5f4e5c53..3400c458cd3 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs @@ -310,7 +310,7 @@ string GetLdFlags (NdkTools ndk, AndroidTargetArch arch, int level, string toolP // We need to link against libc and libm, but since NDK is not in use, the linker won't be able to find the actual Android libraries. // Therefore, we will use their stubs to satisfy the linker. At runtime they will, of course, use the actual Android libraries. - string libstubsPath = Path.Combine (AndroidBinUtilsDirectory, "..", "..", "..", "libstubs", ArchToRid (arch)); + string libstubsPath = Path.Combine (AndroidBinUtilsDirectory, "..", "..", "..", "libstubs", ArchToRid (arch)).Replace (" ", "\\ "); libs.Add (Path.Combine (libstubsPath, "libc.so")); libs.Add (Path.Combine (libstubsPath, "libm.so")); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs index 3e777babfde..dd55a51d39e 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs @@ -83,6 +83,41 @@ public void CheckMonoComponentsMask (bool enableProfiler, bool useInterpreter, b } } + [Test] + public void CheckWhetherLibcAndLibmAreReferencedInAOTLibraries () + { + var proj = new XamarinAndroidApplicationProject { + IsRelease = true, + EmbedAssembliesIntoApk = true, + AotAssemblies = true, + }; + + var abis = new [] { "arm64-v8a", "x86_64" }; + proj.SetAndroidSupportedAbis (abis); + + var libPaths = new List (); + if (Builder.UseDotNet) { + libPaths.Add (Path.Combine ("aot", "arm64-v8a", "libaot-Mono.Android.dll.so")); + libPaths.Add (Path.Combine ("aot", "x86_64", "libaot-Mono.Android.dll.so")); + } else { + libPaths.Add (Path.Combine ("android-arm64", "aot", "Mono.Android.dll.so")); + libPaths.Add (Path.Combine ("android-x64", "aot", "Mono.Android.dll.so")); + } + + using (var b = CreateApkBuilder ()) { + Assert.IsTrue (b.Build (proj), "Build should have succeeded."); + string objPath = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath); + + foreach (string libPath in libPaths) { + string lib = Path.Combine (objPath, libPath); + + Assert.IsTrue (File.Exists (lib), $"Library {lib} should exist on disk"); + Assert.IsTrue (ELFHelper.ReferencesLibrary (lib, "libc.so"), $"Library {lib} should reference libc.so"); + Assert.IsTrue (ELFHelper.ReferencesLibrary (lib, "libm.so"), $"Library {lib} should reference libm.so"); + } + } + } + static object [] CheckAssemblyCountsSource = new object [] { new object[] { /*isRelease*/ false, diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ELFHelper.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ELFHelper.cs index 66cc957cab7..fece59a56fd 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ELFHelper.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ELFHelper.cs @@ -28,7 +28,49 @@ public static bool IsEmptyAOTLibrary (TaskLoggingHelper log, string path) log.LogWarningFromException (ex, showStackTrace: true); return false; } + } + + public static bool ReferencesLibrary (string libraryPath, string referencedLibraryName) + { + if (String.IsNullOrEmpty (libraryPath) || !File.Exists (libraryPath)) { + return false; + } + + IELF elf = ELFReader.Load (libraryPath); + + // TODO: we need .dynstr + var dynstr = GetSection (elf, ".dynstr") as IStringTable; + if (dynstr == null) { + return false; + } + + foreach (IDynamicSection section in elf.GetSections ()) { + foreach (IDynamicEntry entry in section.Entries) { + if (IsLibraryReference (dynstr, entry, referencedLibraryName)) { + return true; + } + } + } + + return false; + } + + static bool IsLibraryReference (IStringTable stringTable, IDynamicEntry dynEntry, string referencedLibraryName) + { + if (dynEntry.Tag != DynamicTag.Needed) { + return false; + } + + ulong index; + if (dynEntry is DynamicEntry entry64) { + index = entry64.Value; + } else if (dynEntry is DynamicEntry entry32) { + index = (ulong)entry32.Value; + } else { + return false; + } + return String.Compare (referencedLibraryName, stringTable[(long)index], StringComparison.Ordinal) == 0; } static bool IsEmptyAOTLibrary (TaskLoggingHelper log, string path, IELF elf) From 5ca26c7712440342486a7a4b24632b75aa3b961b Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Fri, 21 Oct 2022 10:10:54 +0200 Subject: [PATCH 04/17] Package library stubs for all platforms Also try another way of passing paths to the stub libs, hopefully it works --- build-tools/installers/create-installers.targets | 8 ++++++++ build-tools/installers/unix-binutils.projitems | 9 --------- .../Tasks/GetAotArguments.cs | 10 ++++------ .../Tests/Xamarin.Android.Build.Tests/BuildTest.cs | 6 +++--- 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/build-tools/installers/create-installers.targets b/build-tools/installers/create-installers.targets index 51ff5bb9a1d..fad09cf5083 100644 --- a/build-tools/installers/create-installers.targets +++ b/build-tools/installers/create-installers.targets @@ -325,6 +325,14 @@ <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)K4os.Hash.xxHash.dll" /> <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)ELFSharp.dll" /> <_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-arm\libc.so" /> + <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-arm\libm.so" /> + <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x64\libc.so" /> + <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x64\libm.so" /> + <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x86\libc.so" /> + <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x86\libm.so" /> <_MSBuildTargetsSrcFiles Include="$(MSBuildTargetsSrcDir)\Xamarin.Android.AvailableItems.targets" /> diff --git a/build-tools/installers/unix-binutils.projitems b/build-tools/installers/unix-binutils.projitems index 64c23f18a4c..2c29381728a 100644 --- a/build-tools/installers/unix-binutils.projitems +++ b/build-tools/installers/unix-binutils.projitems @@ -24,15 +24,6 @@ <_BinUtilsFilesUnixSignAndHarden Include="$(MicrosoftAndroidSdkOutDir)$(HostOS)\binutils\bin\x86_64-linux-android-ld" /> <_BinUtilsFilesUnixSignAndHarden Include="$(MicrosoftAndroidSdkOutDir)$(HostOS)\binutils\bin\x86_64-linux-android-strip" /> - <_BinUtilsFilesUnixSign Include="$(MicrosoftAndroidSdkOutDir)\libstubs\android-arm64\libc.so" /> - <_BinUtilsFilesUnixSign Include="$(MicrosoftAndroidSdkOutDir)\libstubs\android-arm64\libm.so" /> - <_BinUtilsFilesUnixSign Include="$(MicrosoftAndroidSdkOutDir)\libstubs\android-arm\libc.so" /> - <_BinUtilsFilesUnixSign Include="$(MicrosoftAndroidSdkOutDir)\libstubs\android-arm\libm.so" /> - <_BinUtilsFilesUnixSign Include="$(MicrosoftAndroidSdkOutDir)\libstubs\android-x64\libc.so" /> - <_BinUtilsFilesUnixSign Include="$(MicrosoftAndroidSdkOutDir)\libstubs\android-x64\libm.so" /> - <_BinUtilsFilesUnixSign Include="$(MicrosoftAndroidSdkOutDir)\libstubs\android-x86\libc.so" /> - <_BinUtilsFilesUnixSign Include="$(MicrosoftAndroidSdkOutDir)\libstubs\android-x86\libm.so" /> - <_BinUtilsFilesUnixSign Include="$(MicrosoftAndroidSdkOutDir)$(HostOS)\binutils\lib\liblldCOFF.$(_LlvmLibExtension)" /> <_BinUtilsFilesUnixSign Include="$(MicrosoftAndroidSdkOutDir)$(HostOS)\binutils\lib\liblldCommon.$(_LlvmLibExtension)" /> <_BinUtilsFilesUnixSign Include="$(MicrosoftAndroidSdkOutDir)$(HostOS)\binutils\lib\liblldELF.$(_LlvmLibExtension)" /> diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs index 3400c458cd3..de32717d286 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs @@ -292,6 +292,8 @@ string GetLdFlags (NdkTools ndk, AndroidTargetArch arch, int level, string toolP } libs.Add (Path.Combine (androidLibPath, "libc.so")); libs.Add (Path.Combine (androidLibPath, "libm.so")); + + ldFlags.Append ($"\\\"{string.Join ("\\\";\\\"", libs)}\\\""); } else { if (ldFlags.Length > 0) { ldFlags.Append (' '); @@ -312,8 +314,8 @@ string GetLdFlags (NdkTools ndk, AndroidTargetArch arch, int level, string toolP // Therefore, we will use their stubs to satisfy the linker. At runtime they will, of course, use the actual Android libraries. string libstubsPath = Path.Combine (AndroidBinUtilsDirectory, "..", "..", "..", "libstubs", ArchToRid (arch)).Replace (" ", "\\ "); - libs.Add (Path.Combine (libstubsPath, "libc.so")); - libs.Add (Path.Combine (libstubsPath, "libm.so")); + ldFlags.Append (Path.Combine (libstubsPath, "libc.so")); + ldFlags.Append (Path.Combine (libstubsPath, "libm.so")); } } @@ -321,10 +323,6 @@ string GetLdFlags (NdkTools ndk, AndroidTargetArch arch, int level, string toolP ldFlags.Append (' '); } - if (libs.Count > 0) { - ldFlags.Append ($"\\\"{string.Join ("\\\";\\\"", libs)}\\\""); - } - if (StripLibraries) { if (ldFlags.Length > 0) { ldFlags.Append (' '); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs index dd55a51d39e..f3872b40b15 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs @@ -97,11 +97,11 @@ public void CheckWhetherLibcAndLibmAreReferencedInAOTLibraries () var libPaths = new List (); if (Builder.UseDotNet) { - libPaths.Add (Path.Combine ("aot", "arm64-v8a", "libaot-Mono.Android.dll.so")); - libPaths.Add (Path.Combine ("aot", "x86_64", "libaot-Mono.Android.dll.so")); - } else { libPaths.Add (Path.Combine ("android-arm64", "aot", "Mono.Android.dll.so")); libPaths.Add (Path.Combine ("android-x64", "aot", "Mono.Android.dll.so")); + } else { + libPaths.Add (Path.Combine ("aot", "arm64-v8a", "libaot-Mono.Android.dll.so")); + libPaths.Add (Path.Combine ("aot", "x86_64", "libaot-Mono.Android.dll.so")); } using (var b = CreateApkBuilder ()) { From 6b008423d1e8ee5bb152fa29d9897ef021a4ec2f Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Fri, 21 Oct 2022 12:28:36 +0200 Subject: [PATCH 05/17] Spaces are rather important in command-line arguments --- src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs index de32717d286..f896836b9d4 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs @@ -308,7 +308,7 @@ string GetLdFlags (NdkTools ndk, AndroidTargetArch arch, int level, string toolP // Without the flag, `lld` will modify AOT-generated code in a way that the Mono runtime doesn't support. Until // the runtime issue is fixed, we need to pass this flag then. // - ldFlags.Append ("--no-relax"); + ldFlags.Append ("--no-relax "); // We need to link against libc and libm, but since NDK is not in use, the linker won't be able to find the actual Android libraries. // Therefore, we will use their stubs to satisfy the linker. At runtime they will, of course, use the actual Android libraries. From fbc4cf48736a69bd5a4c81c2d36e54d5b34782de Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Fri, 21 Oct 2022 15:30:42 +0200 Subject: [PATCH 06/17] Those spaces are REALLY important, duh --- src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs index f896836b9d4..64554d24991 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs @@ -315,6 +315,7 @@ string GetLdFlags (NdkTools ndk, AndroidTargetArch arch, int level, string toolP string libstubsPath = Path.Combine (AndroidBinUtilsDirectory, "..", "..", "..", "libstubs", ArchToRid (arch)).Replace (" ", "\\ "); ldFlags.Append (Path.Combine (libstubsPath, "libc.so")); + ldFlags.Append (' '); ldFlags.Append (Path.Combine (libstubsPath, "libm.so")); } } From 1bea16c9163d1ab5b43c604436c6f508918b9921 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Wed, 26 Oct 2022 11:10:01 +0200 Subject: [PATCH 07/17] Let's see if tests on Windows can find the stubs now --- src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs | 8 +++++++- src/Xamarin.Android.Build.Tasks/Utilities/ELFHelper.cs | 2 -- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs index 64554d24991..a951a52c532 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs @@ -312,7 +312,13 @@ string GetLdFlags (NdkTools ndk, AndroidTargetArch arch, int level, string toolP // We need to link against libc and libm, but since NDK is not in use, the linker won't be able to find the actual Android libraries. // Therefore, we will use their stubs to satisfy the linker. At runtime they will, of course, use the actual Android libraries. - string libstubsPath = Path.Combine (AndroidBinUtilsDirectory, "..", "..", "..", "libstubs", ArchToRid (arch)).Replace (" ", "\\ "); + string relPath = Path.Combine ("..", ".."); + if (!OS.IsWindows) { + // the `binutils` directory is one level down (${OS}/binutils) than the Windows one + relPath = Path.Combine (relPath, ".."); + } + + string libstubsPath = Path.Combine (AndroidBinUtilsDirectory, relPath, "libstubs", ArchToRid (arch)).Replace (" ", "\\ "); ldFlags.Append (Path.Combine (libstubsPath, "libc.so")); ldFlags.Append (' '); diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ELFHelper.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ELFHelper.cs index fece59a56fd..a7a00b4af60 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ELFHelper.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ELFHelper.cs @@ -37,8 +37,6 @@ public static bool ReferencesLibrary (string libraryPath, string referencedLibra } IELF elf = ELFReader.Load (libraryPath); - - // TODO: we need .dynstr var dynstr = GetSection (elf, ".dynstr") as IStringTable; if (dynstr == null) { return false; From 63db768f7cf8925f623a0228f2705a30a6d0783a Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Wed, 26 Oct 2022 14:27:56 +0200 Subject: [PATCH 08/17] Yet another attempt at quoting for classic Mono --- src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs index a951a52c532..69758cc103f 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs @@ -320,9 +320,13 @@ string GetLdFlags (NdkTools ndk, AndroidTargetArch arch, int level, string toolP string libstubsPath = Path.Combine (AndroidBinUtilsDirectory, relPath, "libstubs", ArchToRid (arch)).Replace (" ", "\\ "); + ldFlags.Append ("\\\""); ldFlags.Append (Path.Combine (libstubsPath, "libc.so")); - ldFlags.Append (' '); + ldFlags.Append ("\\\""); + ldFlags.Append (" \\\""); ldFlags.Append (Path.Combine (libstubsPath, "libm.so")); + ldFlags.Append ("\\\""); + } } From 3dab27d1334586a4960d8926f24c5b377a968bb4 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Wed, 26 Oct 2022 21:54:29 +0200 Subject: [PATCH 09/17] Try quoting the entire `ld-flags` value --- src/Xamarin.Android.Build.Tasks/Tasks/Aot.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/Aot.cs b/src/Xamarin.Android.Build.Tasks/Tasks/Aot.cs index 8e6cd1dce0e..8104e868387 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/Aot.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/Aot.cs @@ -170,7 +170,7 @@ IEnumerable GetAotConfigs (NdkTools ndk) aotOptions.Add ($"ld-name={LdName}"); } if (!string.IsNullOrEmpty (LdFlags)) { - aotOptions.Add ($"ld-flags={LdFlags}"); + aotOptions.Add ($"ld-flags=\\\"{LdFlags}\\\""); } // We don't check whether any mode option was added via `AotAdditionalArguments`, the `AndroidAotMode` property should always win here. From 33e661096790ee89537c898c3551eec8e916113a Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Wed, 26 Oct 2022 22:51:07 +0200 Subject: [PATCH 10/17] Let's try this change from @peterc --- .../Tasks/GetAotArguments.cs | 60 ++++++++----------- 1 file changed, 25 insertions(+), 35 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs index 69758cc103f..2942e7bc856 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs @@ -292,46 +292,36 @@ string GetLdFlags (NdkTools ndk, AndroidTargetArch arch, int level, string toolP } libs.Add (Path.Combine (androidLibPath, "libc.so")); libs.Add (Path.Combine (androidLibPath, "libm.so")); + } else if (!UseAndroidNdk && EnableLLVM) { + // We need to link against libc and libm, but since NDK is not in use, the linker won't be able to find the actual Android libraries. + // Therefore, we will use their stubs to satisfy the linker. At runtime they will, of course, use the actual Android libraries. + string relPath = Path.Combine ("..", ".."); + if (!OS.IsWindows) { + // the `binutils` directory is one level down (${OS}/binutils) than the Windows one + relPath = Path.Combine (relPath, ".."); + } + string libstubsPath = Path.GetFullPath (Path.Combine (AndroidBinUtilsDirectory, relPath, "libstubs", ArchToRid (arch)).Replace (" ", "\\ ")); + libs.Add (Path.Combine (libstubsPath, "libc.so")); + libs.Add (Path.Combine (libstubsPath, "libm.so")); + } + if (libs.Count > 0) { ldFlags.Append ($"\\\"{string.Join ("\\\";\\\"", libs)}\\\""); - } else { + } + + // + // This flag is needed for Mono AOT to work correctly with the LLVM 14 `lld` linker due to the following change: + // + // The AArch64 port now supports adrp+ldr and adrp+add optimizations. --no-relax can suppress the optimization. + // + // Without the flag, `lld` will modify AOT-generated code in a way that the Mono runtime doesn't support. Until + // the runtime issue is fixed, we need to pass this flag then. + // + if (!UseAndroidNdk) { if (ldFlags.Length > 0) { ldFlags.Append (' '); } - - if (!UseAndroidNdk) { - // - // This flag is needed for Mono AOT to work correctly with the LLVM 14 `lld` linker due to the following change: - // - // The AArch64 port now supports adrp+ldr and adrp+add optimizations. --no-relax can suppress the optimization. - // - // Without the flag, `lld` will modify AOT-generated code in a way that the Mono runtime doesn't support. Until - // the runtime issue is fixed, we need to pass this flag then. - // - ldFlags.Append ("--no-relax "); - - // We need to link against libc and libm, but since NDK is not in use, the linker won't be able to find the actual Android libraries. - // Therefore, we will use their stubs to satisfy the linker. At runtime they will, of course, use the actual Android libraries. - string relPath = Path.Combine ("..", ".."); - if (!OS.IsWindows) { - // the `binutils` directory is one level down (${OS}/binutils) than the Windows one - relPath = Path.Combine (relPath, ".."); - } - - string libstubsPath = Path.Combine (AndroidBinUtilsDirectory, relPath, "libstubs", ArchToRid (arch)).Replace (" ", "\\ "); - - ldFlags.Append ("\\\""); - ldFlags.Append (Path.Combine (libstubsPath, "libc.so")); - ldFlags.Append ("\\\""); - ldFlags.Append (" \\\""); - ldFlags.Append (Path.Combine (libstubsPath, "libm.so")); - ldFlags.Append ("\\\""); - - } - } - - if (ldFlags.Length > 0) { - ldFlags.Append (' '); + ldFlags.Append ("--no-relax"); } if (StripLibraries) { From edea1e9239d0033cd63ab5892965fd7a43448fc8 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 27 Oct 2022 09:12:33 +0200 Subject: [PATCH 11/17] Don't quote `ld-flags` value --- src/Xamarin.Android.Build.Tasks/Tasks/Aot.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/Aot.cs b/src/Xamarin.Android.Build.Tasks/Tasks/Aot.cs index 8104e868387..8e6cd1dce0e 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/Aot.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/Aot.cs @@ -170,7 +170,7 @@ IEnumerable GetAotConfigs (NdkTools ndk) aotOptions.Add ($"ld-name={LdName}"); } if (!string.IsNullOrEmpty (LdFlags)) { - aotOptions.Add ($"ld-flags=\\\"{LdFlags}\\\""); + aotOptions.Add ($"ld-flags={LdFlags}"); } // We don't check whether any mode option was added via `AotAdditionalArguments`, the `AndroidAotMode` property should always win here. From 057d2f96bb4d925c909c6c81b6757503ea18ce52 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 27 Oct 2022 12:56:04 +0200 Subject: [PATCH 12/17] Use the stubs whenever not using Android NDK --- src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs index 2942e7bc856..33919ea8ddd 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs @@ -292,7 +292,7 @@ string GetLdFlags (NdkTools ndk, AndroidTargetArch arch, int level, string toolP } libs.Add (Path.Combine (androidLibPath, "libc.so")); libs.Add (Path.Combine (androidLibPath, "libm.so")); - } else if (!UseAndroidNdk && EnableLLVM) { + } else if (!UseAndroidNdk) { // We need to link against libc and libm, but since NDK is not in use, the linker won't be able to find the actual Android libraries. // Therefore, we will use their stubs to satisfy the linker. At runtime they will, of course, use the actual Android libraries. string relPath = Path.Combine ("..", ".."); From f8b82c17b8bea97c0b9afe7a8dae8c790582dacb Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 27 Oct 2022 17:36:34 +0200 Subject: [PATCH 13/17] Enable LLVM usage in the libc/libm reference test --- src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs | 2 +- .../Tests/Xamarin.Android.Build.Tests/BuildTest.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs index 33919ea8ddd..2942e7bc856 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs @@ -292,7 +292,7 @@ string GetLdFlags (NdkTools ndk, AndroidTargetArch arch, int level, string toolP } libs.Add (Path.Combine (androidLibPath, "libc.so")); libs.Add (Path.Combine (androidLibPath, "libm.so")); - } else if (!UseAndroidNdk) { + } else if (!UseAndroidNdk && EnableLLVM) { // We need to link against libc and libm, but since NDK is not in use, the linker won't be able to find the actual Android libraries. // Therefore, we will use their stubs to satisfy the linker. At runtime they will, of course, use the actual Android libraries. string relPath = Path.Combine ("..", ".."); diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs index f3872b40b15..1637b077032 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs @@ -91,6 +91,7 @@ public void CheckWhetherLibcAndLibmAreReferencedInAOTLibraries () EmbedAssembliesIntoApk = true, AotAssemblies = true, }; + proj.SetProperty ("EnableLLVM", "True"); var abis = new [] { "arm64-v8a", "x86_64" }; proj.SetAndroidSupportedAbis (abis); From 79421d3b7795627ada2f6a5c618caf1b71736b9f Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 27 Oct 2022 19:41:16 +0200 Subject: [PATCH 14/17] Don't quote spaces --- src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs index 2942e7bc856..43618225ac5 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs @@ -300,7 +300,7 @@ string GetLdFlags (NdkTools ndk, AndroidTargetArch arch, int level, string toolP // the `binutils` directory is one level down (${OS}/binutils) than the Windows one relPath = Path.Combine (relPath, ".."); } - string libstubsPath = Path.GetFullPath (Path.Combine (AndroidBinUtilsDirectory, relPath, "libstubs", ArchToRid (arch)).Replace (" ", "\\ ")); + string libstubsPath = Path.GetFullPath (Path.Combine (AndroidBinUtilsDirectory, relPath, "libstubs", ArchToRid (arch)); libs.Add (Path.Combine (libstubsPath, "libc.so")); libs.Add (Path.Combine (libstubsPath, "libm.so")); } From 523a2bbe956f6253d85649d6572b43b8476971ef Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 27 Oct 2022 19:48:22 +0200 Subject: [PATCH 15/17] Close yer parentheses, fool --- src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs index 43618225ac5..3037f957c7d 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GetAotArguments.cs @@ -300,7 +300,7 @@ string GetLdFlags (NdkTools ndk, AndroidTargetArch arch, int level, string toolP // the `binutils` directory is one level down (${OS}/binutils) than the Windows one relPath = Path.Combine (relPath, ".."); } - string libstubsPath = Path.GetFullPath (Path.Combine (AndroidBinUtilsDirectory, relPath, "libstubs", ArchToRid (arch)); + string libstubsPath = Path.GetFullPath (Path.Combine (AndroidBinUtilsDirectory, relPath, "libstubs", ArchToRid (arch))); libs.Add (Path.Combine (libstubsPath, "libc.so")); libs.Add (Path.Combine (libstubsPath, "libm.so")); } From 32667323997538b1fc5e1dafa40d7a37b2be9dc9 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 27 Oct 2022 22:20:25 +0200 Subject: [PATCH 16/17] Let's put ld-flags last --- src/Xamarin.Android.Build.Tasks/Tasks/Aot.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/Aot.cs b/src/Xamarin.Android.Build.Tasks/Tasks/Aot.cs index 8e6cd1dce0e..80f5a5f8013 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/Aot.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/Aot.cs @@ -169,9 +169,6 @@ IEnumerable GetAotConfigs (NdkTools ndk) if (!string.IsNullOrEmpty (LdName)) { aotOptions.Add ($"ld-name={LdName}"); } - if (!string.IsNullOrEmpty (LdFlags)) { - aotOptions.Add ($"ld-flags={LdFlags}"); - } // We don't check whether any mode option was added via `AotAdditionalArguments`, the `AndroidAotMode` property should always win here. // Modes not supported by us directly can be set by setting `AndroidAotMode` to "normal" and adding the desired mode name to the @@ -186,6 +183,10 @@ IEnumerable GetAotConfigs (NdkTools ndk) break; } + if (!string.IsNullOrEmpty (LdFlags)) { + aotOptions.Add ($"ld-flags={LdFlags}"); + } + // we need to quote the entire --aot arguments here to make sure it is parsed // on windows as one argument. Otherwise it will be split up into multiple // values, which wont work. From e8b9e6104bff3f9de71f8ffe96dd43ab80c84d4a Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Mon, 21 Nov 2022 16:08:22 -0500 Subject: [PATCH 17/17] Update RunWithLLVMEnabled() to verify that no .so files failed to load --- .../Tests/InstallAndRunTests.cs | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs index f79b85b3222..ce63f5a5d42 100644 --- a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs +++ b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs @@ -702,8 +702,30 @@ public void RunWithLLVMEnabled () else AdbStartActivity ($"{proj.PackageName}/{proj.JavaPackageName}.MainActivity"); - Assert.IsTrue (WaitForActivityToStart (proj.PackageName, "MainActivity", - Path.Combine (Root, builder.ProjectDirectory, "startup-logcat.log"))); + var activityNamespace = proj.PackageName; + var activityName = "MainActivity"; + var logcatFilePath = Path.Combine (Root, builder.ProjectDirectory, "startup-logcat.log"); + var failedToLoad = new List (); + bool appLaunched = MonitorAdbLogcat ((line) => { + if (SeenFailedToLoad (line)) + failedToLoad.Add (line); + return SeenActivityDisplayed (line); + }, logcatFilePath, timeout: 120); + + Assert.IsTrue (appLaunched, "LLVM app did not launch"); + Assert.AreEqual (0, failedToLoad.Count, $"LLVM .so files not loaded:\n{string.Join ("\n", failedToLoad)}"); + + bool SeenActivityDisplayed (string line) + { + var idx1 = line.IndexOf ("ActivityManager: Displayed", StringComparison.OrdinalIgnoreCase); + var idx2 = idx1 > 0 ? 0 : line.IndexOf ("ActivityTaskManager: Displayed", StringComparison.OrdinalIgnoreCase); + return (idx1 > 0 || idx2 > 0) && line.Contains (activityNamespace) && line.Contains (activityName); + } + + bool SeenFailedToLoad (string line) + { + return line.Contains ("Failed to load shared library"); + } } [Test]