From cc9b6859f31e16e7d4728c8adddede6c67ac947a Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Mon, 29 Jul 2024 17:20:08 +0200 Subject: [PATCH 01/17] DSO stub --- build-tools/create-packs/SignList.xml | 1 + .../installers/create-installers.targets | 1 + .../installers/unix-binutils.projitems | 1 + .../xaprepare/ConfigAndData/Configurables.cs | 3 ++- src/native/CMakeLists.txt | 3 +++ src/native/archive-dso-stub/CMakeLists.txt | 23 +++++++++++++++++++ src/native/archive-dso-stub/stub.cc | 2 ++ 7 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 src/native/archive-dso-stub/CMakeLists.txt create mode 100644 src/native/archive-dso-stub/stub.cc diff --git a/build-tools/create-packs/SignList.xml b/build-tools/create-packs/SignList.xml index f61c00fd16b..a71ea2c7fcf 100644 --- a/build-tools/create-packs/SignList.xml +++ b/build-tools/create-packs/SignList.xml @@ -16,6 +16,7 @@ + diff --git a/build-tools/installers/create-installers.targets b/build-tools/installers/create-installers.targets index bb3e307a6c1..29813191f0f 100644 --- a/build-tools/installers/create-installers.targets +++ b/build-tools/installers/create-installers.targets @@ -207,6 +207,7 @@ <_MSBuildFilesWin Include="$(MicrosoftAndroidSdkOutDir)binutils\bin\ld.exe" /> <_MSBuildFilesWin Include="$(MicrosoftAndroidSdkOutDir)binutils\bin\llc.exe" /> <_MSBuildFilesWin Include="$(MicrosoftAndroidSdkOutDir)binutils\bin\llvm-mc.exe" /> + <_MSBuildFilesWin Include="$(MicrosoftAndroidSdkOutDir)binutils\bin\llvm-objcopy.exe" /> <_MSBuildFilesWin Include="$(MicrosoftAndroidSdkOutDir)binutils\bin\llvm-strip.exe" /> <_MSBuildFilesWin Include="$(MicrosoftAndroidSdkOutDir)binutils\bin\aarch64-linux-android-as.cmd" /> <_MSBuildFilesWin Include="$(MicrosoftAndroidSdkOutDir)binutils\bin\aarch64-linux-android-ld.cmd" /> diff --git a/build-tools/installers/unix-binutils.projitems b/build-tools/installers/unix-binutils.projitems index 519c1daed2b..3e01812136f 100644 --- a/build-tools/installers/unix-binutils.projitems +++ b/build-tools/installers/unix-binutils.projitems @@ -19,6 +19,7 @@ <_BinUtilsFilesUnixSignAndHarden Include="$(MicrosoftAndroidSdkOutDir)$(HostOS)\binutils\bin\ld" /> <_BinUtilsFilesUnixSignAndHarden Include="$(MicrosoftAndroidSdkOutDir)$(HostOS)\binutils\bin\llc" /> <_BinUtilsFilesUnixSignAndHarden Include="$(MicrosoftAndroidSdkOutDir)$(HostOS)\binutils\bin\llvm-mc" /> + <_BinUtilsFilesUnixSignAndHarden Include="$(MicrosoftAndroidSdkOutDir)$(HostOS)\binutils\bin\llvm-objcopy" /> <_BinUtilsFilesUnixSignAndHarden Include="$(MicrosoftAndroidSdkOutDir)$(HostOS)\binutils\bin\llvm-strip" /> <_BinUtilsFilesUnixSignAndHarden Include="$(MicrosoftAndroidSdkOutDir)$(HostOS)\binutils\bin\x86_64-linux-android-as" /> <_BinUtilsFilesUnixSignAndHarden Include="$(MicrosoftAndroidSdkOutDir)$(HostOS)\binutils\bin\x86_64-linux-android-ld" /> diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs index 0d298316d7a..9a2edceec81 100644 --- a/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs @@ -15,7 +15,7 @@ namespace Xamarin.Android.Prepare // partial class Configurables { - const string BinutilsVersion = "L_18.1.6-8.0.0"; + const string BinutilsVersion = "L_18.1.7-8.0.0"; const string MicrosoftOpenJDK17Version = "17.0.12"; const string MicrosoftOpenJDK17Release = "17.0.12"; @@ -157,6 +157,7 @@ public static partial class Defaults new NDKTool (name: "ld"), new NDKTool (name: "llc"), new NDKTool (name: "llvm-mc"), + new NDKTool (name: "llvm-objcopy"), new NDKTool (name: "llvm-strip"), }; } diff --git a/src/native/CMakeLists.txt b/src/native/CMakeLists.txt index 5af5b4b92f2..fe77ec1d48c 100644 --- a/src/native/CMakeLists.txt +++ b/src/native/CMakeLists.txt @@ -482,6 +482,9 @@ add_subdirectory(pinvoke-override) if(DEBUG_BUILD) add_subdirectory(xamarin-debug-app-helper) +else() + # We need to build this only once per target architecture + add_subdirectory(archive-dso-stub) endif() add_subdirectory(monodroid) diff --git a/src/native/archive-dso-stub/CMakeLists.txt b/src/native/archive-dso-stub/CMakeLists.txt new file mode 100644 index 00000000000..ec66f0ff084 --- /dev/null +++ b/src/native/archive-dso-stub/CMakeLists.txt @@ -0,0 +1,23 @@ +set(LIB_NAME archive-dso-stub) + +set(LIB_SOURCES + stub.cc +) + +add_library( + ${LIB_NAME} + SHARED + ${LIB_SOURCES} +) + +target_compile_options( + ${LIB_NAME} + PRIVATE + ${XA_COMMON_CXX_ARGS} -nostdlib -fno-exceptions -fno-rtti +) + +target_link_options( + ${LIB_NAME} + PRIVATE + ${XA_COMMON_CXX_LINKER_ARGS} -nostdlib -fno-exceptions -fno-rtti -s +) diff --git a/src/native/archive-dso-stub/stub.cc b/src/native/archive-dso-stub/stub.cc new file mode 100644 index 00000000000..a5bd2861db1 --- /dev/null +++ b/src/native/archive-dso-stub/stub.cc @@ -0,0 +1,2 @@ +[[gnu::visibility("default")]] +bool i_am_a_dummy_stub = true; From d55e1709f3fe0f23b5253be22f63e9bccdc25997 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Tue, 30 Jul 2024 22:25:36 +0200 Subject: [PATCH 02/17] Build archive DSO stub in a separate step --- .../Utilities/DSOWrapperGenerator.cs | 32 ++++++++ src/native/CMakeLists.txt | 79 +++++++++++-------- src/native/archive-dso-stub/CMakeLists.txt | 17 +++- src/native/cmake/ArchiveDSOStub.cmake | 2 + src/native/monodroid/CMakeLists.txt | 42 ++++++++++ .../monodroid/archive-dso-stub-config.hh.in | 12 +++ src/native/native.targets | 39 ++++++++- 7 files changed, 188 insertions(+), 35 deletions(-) create mode 100644 src/Xamarin.Android.Build.Tasks/Utilities/DSOWrapperGenerator.cs create mode 100644 src/native/cmake/ArchiveDSOStub.cmake create mode 100644 src/native/monodroid/archive-dso-stub-config.hh.in diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/DSOWrapperGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/DSOWrapperGenerator.cs new file mode 100644 index 00000000000..ecc1ed2910a --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Utilities/DSOWrapperGenerator.cs @@ -0,0 +1,32 @@ +using System; + +namespace Xamarin.Android.Tasks; + +/// +/// +/// Puts passed files inside a real ELF shared library so that they +/// pass scrutiny when examined. The payload is placed inside its own +/// section of a file, so the entire file is a 100% valid ELF image. +/// +/// +/// +/// The generated files have their payload section positioned at the offset of +/// 16k (0x4000) from the beginning of file. It's done this way because it not +/// only gives us enough room for the stub part of the ELF image to precede that +/// offset, but it also complies with Google policy of aligning to 16k **and** +/// is still nicely aligned to a 4k boundary on 32-bit systems. This helps mmapping +/// the section on both 64-bit and 32-bit systems. +/// +/// +/// +/// The generated file **MUST NOT** be stripped with `llvm-strip` etc, +/// as it will remove the payload together with other sections it deems +/// unnecessary. +/// +class DSOWrapperGenerator +{ + public static string WrapIt (string payloadFilePath) + { + return String.Empty; + } +} diff --git a/src/native/CMakeLists.txt b/src/native/CMakeLists.txt index fe77ec1d48c..b0cbcc0ae4b 100644 --- a/src/native/CMakeLists.txt +++ b/src/native/CMakeLists.txt @@ -42,6 +42,7 @@ set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS OFF) include("${CMAKE_ANDROID_NDK}/build/cmake/abis.cmake") +include("${CMAKE_SOURCE_DIR}/cmake/ArchiveDSOStub.cmake") if(CMAKE_BUILD_TYPE STREQUAL Debug) set(DEBUG_BUILD True) @@ -69,6 +70,10 @@ if(ENABLE_CLANG_ASAN AND ENABLE_CLANG_UBSAN) endif() if(ENABLE_CLANG_ASAN OR ENABLE_CLANG_UBSAN) + if(BUILD_ARCHIVE_DSO_STUB) + message(FATAL_ERROR "ASAN/UBSAN builds aren't supported by the archive DSO target") + endif() + set(STRIP_DEBUG_DEFAULT OFF) set(ANALYZERS_ENABLED ON) else() @@ -191,12 +196,14 @@ macro(xa_add_compile_definitions TARGET) endif() endmacro() -if(DEBUG_BUILD AND NOT DISABLE_DEBUG) - add_compile_definitions(DEBUG) -endif() +if(NOT BUILD_ARCHIVE_DSO_STUB) + if(DEBUG_BUILD AND NOT DISABLE_DEBUG) + add_compile_definitions(DEBUG) + endif() -if(NOT DEBUG_BUILD) - add_compile_definitions(RELEASE NDEBUG) + if(NOT DEBUG_BUILD) + add_compile_definitions(RELEASE NDEBUG) + endif() endif() # @@ -326,10 +333,15 @@ set(POTENTIAL_COMMON_COMPILER_ARGS -funswitch-loops -Wa,-noexecstack -fPIC - -g -O2 ) +if(NOT BUILD_ARCHIVE_DSO_STUB) + list(APPEND POTENTIAL_COMMON_COMPILER_ARGS + -g + ) +endif() + set(POTENTIAL_COMMON_LINKER_ARGS -fstack-protector-strong LINKER:-fstrict-return @@ -451,11 +463,13 @@ xa_check_c_linker_args(XA_COMMON_C_LINKER_ARGS "${POTENTIAL_XA_COMMON_LINKER_ARG xa_check_c_linker_args(XA_C_DSO_LINKER_ARGS "${POTENTIAL_XA_DSO_LINKER_ARGS}") xa_check_cxx_linker_args(XA_CXX_DSO_LINKER_ARGS "${POTENTIAL_XA_DSO_LINKER_ARGS}") -add_compile_options("$<$:${COMMON_CXX_ARGS}>") -add_compile_options("$<$:${COMMON_C_ARGS}>") +if(NOT BUILD_ARCHIVE_DSO_STUB) + add_compile_options("$<$:${COMMON_CXX_ARGS}>") + add_compile_options("$<$:${COMMON_C_ARGS}>") -add_link_options("$<$:${COMMON_CXX_LINKER_ARGS}>") -add_link_options("$<$:${COMMON_C_LINKER_ARGS}>") + add_link_options("$<$:${COMMON_CXX_LINKER_ARGS}>") + add_link_options("$<$:${COMMON_C_LINKER_ARGS}>") +endif() # # Helper macros @@ -470,28 +484,29 @@ macro(set_static_library_suffix TARGET_NAME) endif() endmacro() -add_subdirectory(libunwind) -add_subdirectory(lz4) -add_subdirectory(libstub) -add_subdirectory(shared) -add_subdirectory(java-interop) -add_subdirectory(xamarin-app-stub) -add_subdirectory(runtime-base) -add_subdirectory(tracing) -add_subdirectory(pinvoke-override) - -if(DEBUG_BUILD) - add_subdirectory(xamarin-debug-app-helper) -else() - # We need to build this only once per target architecture +if(BUILD_ARCHIVE_DSO_STUB) add_subdirectory(archive-dso-stub) -endif() +else() + add_subdirectory(libunwind) + add_subdirectory(lz4) + add_subdirectory(libstub) + add_subdirectory(shared) + add_subdirectory(java-interop) + add_subdirectory(xamarin-app-stub) + add_subdirectory(runtime-base) + add_subdirectory(tracing) + add_subdirectory(pinvoke-override) + + if(DEBUG_BUILD) + add_subdirectory(xamarin-debug-app-helper) + endif() -add_subdirectory(monodroid) + add_subdirectory(monodroid) -add_custom_target(run_static_analysis - COMMAND ${ANDROID_TOOLCHAIN_ROOT}/bin/clang-check -analyze -p="${CMAKE_CURRENT_BINARY_DIR}" ${CLANG_CHECK_SOURCES} > ${CMAKE_SOURCE_DIR}/static-analysis.${ANDROID_ABI}.${CMAKE_BUILD_TYPE}.txt 2>&1 - COMMAND_EXPAND_LISTS - WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" - USES_TERMINAL -) + add_custom_target(run_static_analysis + COMMAND ${ANDROID_TOOLCHAIN_ROOT}/bin/clang-check -analyze -p="${CMAKE_CURRENT_BINARY_DIR}" ${CLANG_CHECK_SOURCES} > ${CMAKE_SOURCE_DIR}/static-analysis.${ANDROID_ABI}.${CMAKE_BUILD_TYPE}.txt 2>&1 + COMMAND_EXPAND_LISTS + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + USES_TERMINAL + ) +endif() diff --git a/src/native/archive-dso-stub/CMakeLists.txt b/src/native/archive-dso-stub/CMakeLists.txt index ec66f0ff084..f474638866b 100644 --- a/src/native/archive-dso-stub/CMakeLists.txt +++ b/src/native/archive-dso-stub/CMakeLists.txt @@ -1,3 +1,5 @@ +include("${CMAKE_SOURCE_DIR}/cmake/ArchiveDSOStub.cmake") + set(LIB_NAME archive-dso-stub) set(LIB_SOURCES @@ -10,14 +12,25 @@ add_library( ${LIB_SOURCES} ) +set(ARCHIVE_DSO_STUB_LIB_NAME "lib${LIB_NAME}.so" PARENT_SCOPE) + target_compile_options( ${LIB_NAME} PRIVATE - ${XA_COMMON_CXX_ARGS} -nostdlib -fno-exceptions -fno-rtti + ${XA_DEFAULT_SYMBOL_VISIBILITY} + ${XA_COMMON_CXX_ARGS} + -nostdlib -fno-exceptions -fno-rtti -s ) target_link_options( ${LIB_NAME} PRIVATE - ${XA_COMMON_CXX_LINKER_ARGS} -nostdlib -fno-exceptions -fno-rtti -s + ${XA_COMMON_CXX_LINKER_ARGS} + -nostdlib -fno-exceptions -fno-rtti -s +) + +add_custom_command( + TARGET ${LIB_NAME} + POST_BUILD + COMMAND ${CMAKE_STRIP} "$" ) diff --git a/src/native/cmake/ArchiveDSOStub.cmake b/src/native/cmake/ArchiveDSOStub.cmake new file mode 100644 index 00000000000..6c43377007f --- /dev/null +++ b/src/native/cmake/ArchiveDSOStub.cmake @@ -0,0 +1,2 @@ +set(ARCHIVE_DSO_STUB_LIB_NAME "archive-dso-stub") +set(ARCHIVE_DSO_STUB_LIB_FILE_NAME "lib${ARCHIVE_DSO_STUB_LIB_NAME}.so") diff --git a/src/native/monodroid/CMakeLists.txt b/src/native/monodroid/CMakeLists.txt index 09d4b16c4dd..002ca547800 100644 --- a/src/native/monodroid/CMakeLists.txt +++ b/src/native/monodroid/CMakeLists.txt @@ -1,5 +1,47 @@ option(ENABLE_TIMING "Build with timing support" OFF) +# First generate some code +file(COPY "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/${ARCHIVE_DSO_STUB_LIB_FILE_NAME}" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) +set(ARCHIVE_DSO_STUB_LIB_PATH "${CMAKE_CURRENT_BINARY_DIR}/${ARCHIVE_DSO_STUB_LIB_FILE_NAME}") + +# Emulate what we do when embedding something inside the ELF file +set(PAYLOAD_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt) + +execute_process( + COMMAND ${CMAKE_OBJCOPY} --add-section payload=${PAYLOAD_PATH} ${ARCHIVE_DSO_STUB_LIB_PATH} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ECHO_ERROR_VARIABLE + COMMAND_ERROR_IS_FATAL ANY +) + +execute_process( + COMMAND ${CMAKE_OBJCOPY} --set-section-flags payload=readonly,data --set-section-alignment payload=0x4000 ${ARCHIVE_DSO_STUB_LIB_PATH} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ECHO_ERROR_VARIABLE + COMMAND_ERROR_IS_FATAL ANY +) + +execute_process( + COMMAND ${CMAKE_READELF} --file-header --section-headers ${ARCHIVE_DSO_STUB_LIB_PATH} --elf-output-style=JSON + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + OUTPUT_VARIABLE ARCHIVE_DSO_STUB_HEADER_JSON + ECHO_ERROR_VARIABLE + COMMAND_ERROR_IS_FATAL ANY +) +string(JSON SECTION_HEADER_ENTRY_SIZE GET "${ARCHIVE_DSO_STUB_HEADER_JSON}" 0 "ElfHeader" "SectionHeaderEntrySize") +string(JSON SECTION_HEADER_ENTRY_COUNT GET "${ARCHIVE_DSO_STUB_HEADER_JSON}" 0 "ElfHeader" "SectionHeaderCount") + +math(EXPR PAYLOAD_SECTION_INDEX "${SECTION_HEADER_ENTRY_COUNT} - 1") +string(JSON PAYLOAD_SECTION_OFFSET GET "${ARCHIVE_DSO_STUB_HEADER_JSON}" 0 "Sections" ${PAYLOAD_SECTION_INDEX} "Section" "Offset") +file(REMOVE ${ARCHIVE_DSO_STUB_LIB_PATH}) + +message(STATUS "Header entry size: ${SECTION_HEADER_ENTRY_SIZE}; count: ${SECTION_HEADER_ENTRY_COUNT}; payload offset: ${PAYLOAD_SECTION_OFFSET}") +configure_file( + archive-dso-stub-config.hh.in + ${CMAKE_CURRENT_BINARY_DIR}/archive-dso-stub-config.hh + USE_SOURCE_PERMISSIONS +) + # Needed modules include(CheckIncludeFile) diff --git a/src/native/monodroid/archive-dso-stub-config.hh.in b/src/native/monodroid/archive-dso-stub-config.hh.in new file mode 100644 index 00000000000..14ac7ff66ae --- /dev/null +++ b/src/native/monodroid/archive-dso-stub-config.hh.in @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace xamarin::android { + struct ArchiveDSOStubConfig + { + static inline constexpr size_t SectionHeaderEntrySize = @SECTION_HEADER_ENTRY_SIZE@; + static inline constexpr size_t SectionHeaderEntryCount = @SECTION_HEADER_ENTRY_COUNT@; + static inline constexpr size_t PayloadSectionOffset = @PAYLOAD_SECTION_OFFSET@; + }; +} diff --git a/src/native/native.targets b/src/native/native.targets index ab4539bd4fa..91ca4b82996 100644 --- a/src/native/native.targets +++ b/src/native/native.targets @@ -2,7 +2,7 @@ + DependsOnTargets="_ConfigureAndBuildArchiveDSOStub;_ConfigureRuntimes;_BuildAndroidRuntimes;_BuildAndroidAnalyzerRuntimes;_CopyToPackDirs"> @@ -51,6 +51,39 @@ + + + <_ArchiveDSOInput Include="archive-dso-stub\CMakeLists.txt" /> + + <_ArchiveDSOOutput Include="@(AndroidSupportedTargetJitAbi->'$(IntermediateOutputPath)\%(AndroidRID)-archive-dso-stub\CMakeCache.txt')" /> + <_ArchiveDSOOutput Include="@(AndroidSupportedTargetJitAbi->'$(OutputPath)\%(AndroidRID)\libarchive-dso-stub.so')" /> + + <_ArchiveOutputDirToCreate Include="$(IntermediateOutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)-archive-dso-stub" /> + + + + + + <_ConfigureArchiveDSOStubCommands Include="@(AndroidSupportedTargetJitAbi)"> + $(CmakePath) + --preset default-release-%(AndroidSupportedTargetJitAbi.Identity) -DBUILD_ARCHIVE_DSO_STUB=ON -DSTRIP_DEBUG=ON "$(MSBuildThisFileDirectory)" + $(IntermediateOutputPath)%(AndroidSupportedTargetJitAbi.AndroidRID)-archive-dso-stub + + + + + + + + + + + + <_MonoDroidSources Include="android-dso-stub\*.cc;libstub\*.hh" /> <_MonoDroidSources Include="libstub\*.cc;libstub\*.hh" /> <_MonoDroidSources Include="monodroid\*.cc;monodroid\*.hh" /> <_MonoDroidSources Include="runtime-base\*.cc;runtime-base\*.hh" /> @@ -183,6 +217,9 @@ + From e6cacf01b607ec7095cd33ee0c6d11efe1333aa7 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Wed, 31 Jul 2024 18:04:55 +0200 Subject: [PATCH 03/17] Wrapping works for some items already TODO: config files, runtime config blob --- .../Microsoft.Android.Runtime.proj | 1 + ...oft.Android.Sdk.AssemblyResolution.targets | 12 +++ .../Tasks/BuildApk.cs | 13 ++-- .../Tasks/PrepareDSOWrapperState.cs | 54 +++++++++++++ .../Utilities/DSOWrapperGenerator.cs | 77 ++++++++++++++++++- .../Utilities/MonoAndroidHelper.cs | 69 +++++++++++++++++ src/native/archive-dso-stub/CMakeLists.txt | 2 +- src/native/monodroid/CMakeLists.txt | 12 ++- .../monodroid/archive-dso-stub-config.hh.in | 1 + 9 files changed, 230 insertions(+), 11 deletions(-) create mode 100644 src/Xamarin.Android.Build.Tasks/Tasks/PrepareDSOWrapperState.cs diff --git a/build-tools/create-packs/Microsoft.Android.Runtime.proj b/build-tools/create-packs/Microsoft.Android.Runtime.proj index 6ccb3fa4847..1ab3bc20d1b 100644 --- a/build-tools/create-packs/Microsoft.Android.Runtime.proj +++ b/build-tools/create-packs/Microsoft.Android.Runtime.proj @@ -42,6 +42,7 @@ projects that use the Microsoft.Android framework in .NET 6+. <_AndroidRuntimePackAssets Include="$(MicrosoftAndroidSdkOutDir)lib\$(AndroidRID)\libmono-android.release.so" /> <_AndroidRuntimePackAssets Include="$(MicrosoftAndroidSdkOutDir)lib\$(AndroidRID)\libxamarin-debug-app-helper.so" /> <_AndroidRuntimePackAssets Include="$(MicrosoftAndroidSdkOutDir)lib\$(AndroidRID)\libxamarin-native-tracing.so" /> + <_AndroidRuntimePackAssets Include="$(MicrosoftAndroidSdkOutDir)lib\$(AndroidRID)\libarchive-dso-stub.so" /> <_AndroidRuntimePackAssets Include="$(MicrosoftAndroidSdkOutDir)lib\$(AndroidRID)\libunwind_xamarin.a" /> 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 f3ed040b0d9..285c72dafb3 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 @@ -13,6 +13,7 @@ _ResolveAssemblies MSBuild target. + + + <_ResolvedArchiveDSOStub Include="@(ResolvedFileToPublish)" Condition=" '%(ResolvedFileToPublish.Filename)%(ResolvedFileToPublish.Extension)' == 'libarchive-dso-stub.so' " /> + + + + + + assemblies) + void DoAddAssembliesFromArchCollection (AndroidTargetArch arch, Dictionary assemblies) { // In the "all assemblies are per-RID" world, assemblies, pdb and config are disguised as shared libraries (that is, // their names end with the .so extension) so that Android allows us to put them in the `lib/{ARCH}` directory. @@ -492,7 +493,8 @@ void DoAddAssembliesFromArchCollection (Dictionary assemblies if (UseAssemblyStore) { storeAssemblyInfo = new AssemblyStoreAssemblyInfo (sourcePath, assembly); } else { - AddFileToArchiveIfNewer (apk, sourcePath, assemblyPath, compressionMethod: GetCompressionMethod (assemblyPath)); + string wrappedSourcePath = DSOWrapperGenerator.WrapIt (arch, sourcePath, Path.GetFileName (assemblyPath), BuildEngine4, Log); + AddFileToArchiveIfNewer (apk, wrappedSourcePath, assemblyPath, compressionMethod: GetCompressionMethod (assemblyPath)); } // Try to add config if exists @@ -519,9 +521,10 @@ void DoAddAssembliesFromArchCollection (Dictionary assemblies storeAssemblyInfo.SymbolsFile = new FileInfo (symbolsPath); } else { string archiveSymbolsPath = assemblyDirectory + MonoAndroidHelper.MakeDiscreteAssembliesEntryName (Path.GetFileName (symbols)); + string wrappedSymbolsPath = DSOWrapperGenerator.WrapIt (arch, symbolsPath, Path.GetFileName (archiveSymbolsPath), BuildEngine4, Log); AddFileToArchiveIfNewer ( apk, - symbolsPath, + wrappedSymbolsPath, archiveSymbolsPath, compressionMethod: GetCompressionMethod (archiveSymbolsPath) ); diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/PrepareDSOWrapperState.cs b/src/Xamarin.Android.Build.Tasks/Tasks/PrepareDSOWrapperState.cs new file mode 100644 index 00000000000..433e2e02d3e --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Tasks/PrepareDSOWrapperState.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.IO; + +using Microsoft.Build.Framework; +using Microsoft.Android.Build.Tasks; +using Xamarin.Android.Tools; + +namespace Xamarin.Android.Tasks; + +/// +/// Registers a state object used by the DSOWrapperGenerator class later on during +/// the build. This is to avoid having to pask parameters to some tasks (esp. BuildApk) +/// which not necessarily need those parameters directly. Registering the state here +/// also avoids having to update monodroid whenever any required parameter is added to +/// BuildApk. +/// +public class PrepareDSOWrapperState : AndroidTask +{ + public override string TaskPrefix => "PDWS"; + + [Required] + public ITaskItem[] ArchiveDSOStubs { get; set; } + + [Required] + public string AndroidBinUtilsDirectory { get; set; } + + [Required] + public string BaseOutputDirectory { get; set; } + + public override bool RunTask () + { + var stubPaths = new Dictionary (); + + foreach (ITaskItem stubItem in ArchiveDSOStubs) { + string rid = stubItem.GetRequiredMetadata ("ArchiveDSOStub", "RuntimeIdentifier", Log); + AndroidTargetArch arch = MonoAndroidHelper.RidToArch (rid); + if (stubPaths.ContainsKey (arch)) { + throw new InvalidOperationException ($"Internal error: duplicate archive DSO stub architecture '{arch}' (RID: '{rid}')"); + } + + if (!File.Exists (stubItem.ItemSpec)) { + throw new InvalidOperationException ($"Internal error: archive DSO stub file '{stubItem.ItemSpec}' does not exist"); + } + + stubPaths.Add (arch, stubItem); + } + + var config = new DSOWrapperGenerator.Config (stubPaths, AndroidBinUtilsDirectory, BaseOutputDirectory); + BuildEngine4.RegisterTaskObjectAssemblyLocal (DSOWrapperGenerator.RegisteredConfigKey, config, RegisteredTaskObjectLifetime.Build); + + return !Log.HasLoggedErrors; + } +} diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/DSOWrapperGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/DSOWrapperGenerator.cs index ecc1ed2910a..573dd224016 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/DSOWrapperGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/DSOWrapperGenerator.cs @@ -1,4 +1,11 @@ using System; +using System.Collections.Generic; +using System.IO; + +using Microsoft.Android.Build.Tasks; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using Xamarin.Android.Tools; namespace Xamarin.Android.Tasks; @@ -25,8 +32,74 @@ namespace Xamarin.Android.Tasks; /// class DSOWrapperGenerator { - public static string WrapIt (string payloadFilePath) + internal const string RegisteredConfigKey = ".:!DSOWrapperGeneratorConfig!:."; + + internal class Config + { + public Dictionary DSOStubPaths { get; } + public string AndroidBinUtilsDirectory { get; } + public string BaseOutputDirectory { get; } + + public Config (Dictionary stubPaths, string androidBinUtilsDirectory, string baseOutputDirectory) + { + DSOStubPaths = stubPaths; + AndroidBinUtilsDirectory = androidBinUtilsDirectory; + BaseOutputDirectory = baseOutputDirectory; + } + }; + + // + // Must be the same avalue as ARCHIVE_DSO_STUB_PAYLOAD_SECTION_ALIGNMENT in src/native/CMakeLists.txt + // + const ulong PayloadSectionAlignment = 0x4000; + + /// + /// Puts the indicated file () inside an ELF shared library and returns + /// path to the wrapped file. + /// + public static string WrapIt (AndroidTargetArch targetArch, string payloadFilePath, string outputFileName, IBuildEngine4 buildEngine, TaskLoggingHelper log) + { + log.LogDebugMessage ($"[{targetArch}] Putting '{payloadFilePath}' inside ELF shared library '{outputFileName}'"); + var config = buildEngine.GetRegisteredTaskObjectAssemblyLocal (RegisteredConfigKey, RegisteredTaskObjectLifetime.Build); + if (config == null) { + throw new InvalidOperationException ("Internal error: no registered config found"); + } + + string outputDir = Path.Combine (config.BaseOutputDirectory, MonoAndroidHelper.ArchToRid (targetArch), "wrapped"); + Directory.CreateDirectory (outputDir); + string outputFile = Path.Combine (outputDir, outputFileName); + log.LogDebugMessage ($" output file path: {outputFile}"); + + if (!config.DSOStubPaths.TryGetValue (targetArch, out ITaskItem? stubItem)) { + throw new InvalidOperationException ($"Internal error: archive DSO stub location not known for architecture '{targetArch}'"); + } + + File.Copy (stubItem.ItemSpec, outputFile); + + string quotedOutputFile = MonoAndroidHelper.QuoteFileNameArgument (outputFile); + string objcopy = Path.Combine (config.AndroidBinUtilsDirectory, MonoAndroidHelper.GetExecutablePath (config.AndroidBinUtilsDirectory, "llvm-objcopy")); + var args = new List { + "--add-section", + $"payload={MonoAndroidHelper.QuoteFileNameArgument (payloadFilePath)}", + quotedOutputFile, + }; + + int ret = MonoAndroidHelper.RunProcess (objcopy, String.Join (" ", args), log); + if (ret != 0) { + return outputFile; + } + + args.Clear (); + args.Add ("--set-section-flags"); + args.Add ("payload=readonly,data"); + args.Add ($"--set-section-alignment payload={PayloadSectionAlignment}"); + args.Add (quotedOutputFile); + ret = MonoAndroidHelper.RunProcess (objcopy, String.Join (" ", args), log); + + return outputFile; + } + + public static void CleanUp (IBuildEngine4 buildEngine, TaskLoggingHelper log) { - return String.Empty; } } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs index 9fe90178ea4..4b7ce76c853 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs @@ -6,6 +6,7 @@ using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Text; +using System.Threading; using Xamarin.Android.Tools; using Xamarin.Tools.Zip; @@ -50,6 +51,67 @@ void AppendLines (string prefix, List lines, StringBuilder sb) } } + public static int RunProcess (string command, string arguments, TaskLoggingHelper log, DataReceivedEventHandler? onOutput = null, DataReceivedEventHandler? onError = null) + { + var stdout_completed = new ManualResetEvent (false); + var stderr_completed = new ManualResetEvent (false); + var psi = new ProcessStartInfo () { + FileName = command, + Arguments = arguments, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden, + }; + + var stdoutLines = new List (); + var stderrLines = new List (); + + log.LogDebugMessage ($"Running process: {psi.FileName} {psi.Arguments}"); + using var proc = new Process (); + proc.OutputDataReceived += (s, e) => { + if (e.Data != null) { + onOutput?.Invoke (s, e); + stdoutLines.Add (e.Data); + } else + stdout_completed.Set (); + }; + + proc.ErrorDataReceived += (s, e) => { + if (e.Data != null) { + onError?.Invoke (s, e); + stderrLines.Add (e.Data); + } else + stderr_completed.Set (); + }; + + proc.StartInfo = psi; + proc.Start (); + proc.BeginOutputReadLine (); + proc.BeginErrorReadLine (); + proc.WaitForExit (); + + if (psi.RedirectStandardError) { + stderr_completed.WaitOne (TimeSpan.FromSeconds (30)); + } + + if (psi.RedirectStandardOutput) { + stdout_completed.WaitOne (TimeSpan.FromSeconds (30)); + } + + if (proc.ExitCode != 0) { + var sb = MergeStdoutAndStderrMessages (stdoutLines, stderrLines); + log.LogError ($"Command '{psi.FileName} {psi.Arguments}' failed.\n{sb.ToString ()}"); + } + + try { + return proc.ExitCode; + } finally { + proc.Close (); + } + } + public static int RunProcess (string name, string args, DataReceivedEventHandler onOutput, DataReceivedEventHandler onError, Dictionary environmentVariables = null) { var psi = new ProcessStartInfo (name, args) { @@ -719,5 +781,12 @@ static uint ZipAlignmentToMaskOrPageSize (int alignment, bool needMask) _ => throw new InvalidOperationException ($"Internal error: unsupported zip page alignment value {alignment}") }; } + + public static string QuoteFileNameArgument (string fileName) + { + var builder = new CommandLineBuilder (); + builder.AppendFileNameIfNotNull (fileName); + return builder.ToString (); + } } } diff --git a/src/native/archive-dso-stub/CMakeLists.txt b/src/native/archive-dso-stub/CMakeLists.txt index f474638866b..d21669498d0 100644 --- a/src/native/archive-dso-stub/CMakeLists.txt +++ b/src/native/archive-dso-stub/CMakeLists.txt @@ -19,7 +19,7 @@ target_compile_options( PRIVATE ${XA_DEFAULT_SYMBOL_VISIBILITY} ${XA_COMMON_CXX_ARGS} - -nostdlib -fno-exceptions -fno-rtti -s + -nostdlib -fno-exceptions -fno-rtti ) target_link_options( diff --git a/src/native/monodroid/CMakeLists.txt b/src/native/monodroid/CMakeLists.txt index 002ca547800..65dd350931a 100644 --- a/src/native/monodroid/CMakeLists.txt +++ b/src/native/monodroid/CMakeLists.txt @@ -1,6 +1,12 @@ option(ENABLE_TIMING "Build with timing support" OFF) # First generate some code + +# +# Must be the same value as DSOWrapperGenerator.PayloadSectionAlignment in src/Xamarin.Android.Build.Tasks/Utilities/DSOWrapperGenerator.cs +# +set(ARCHIVE_DSO_STUB_PAYLOAD_SECTION_ALIGNMENT 0x4000) + file(COPY "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/${ARCHIVE_DSO_STUB_LIB_FILE_NAME}" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) set(ARCHIVE_DSO_STUB_LIB_PATH "${CMAKE_CURRENT_BINARY_DIR}/${ARCHIVE_DSO_STUB_LIB_FILE_NAME}") @@ -15,7 +21,7 @@ execute_process( ) execute_process( - COMMAND ${CMAKE_OBJCOPY} --set-section-flags payload=readonly,data --set-section-alignment payload=0x4000 ${ARCHIVE_DSO_STUB_LIB_PATH} + COMMAND ${CMAKE_OBJCOPY} --set-section-flags payload=readonly,data --set-section-alignment payload=${ARCHIVE_DSO_STUB_PAYLOAD_SECTION_ALIGNMENT} ${ARCHIVE_DSO_STUB_LIB_PATH} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ECHO_ERROR_VARIABLE COMMAND_ERROR_IS_FATAL ANY @@ -35,10 +41,10 @@ math(EXPR PAYLOAD_SECTION_INDEX "${SECTION_HEADER_ENTRY_COUNT} - 1") string(JSON PAYLOAD_SECTION_OFFSET GET "${ARCHIVE_DSO_STUB_HEADER_JSON}" 0 "Sections" ${PAYLOAD_SECTION_INDEX} "Section" "Offset") file(REMOVE ${ARCHIVE_DSO_STUB_LIB_PATH}) -message(STATUS "Header entry size: ${SECTION_HEADER_ENTRY_SIZE}; count: ${SECTION_HEADER_ENTRY_COUNT}; payload offset: ${PAYLOAD_SECTION_OFFSET}") +message(STATUS "Archive DSO stub header entry size: ${SECTION_HEADER_ENTRY_SIZE}; section count: ${SECTION_HEADER_ENTRY_COUNT}; payload offset: ${PAYLOAD_SECTION_OFFSET}") configure_file( archive-dso-stub-config.hh.in - ${CMAKE_CURRENT_BINARY_DIR}/archive-dso-stub-config.hh + ${CMAKE_CURRENT_BINARY_DIR}/include/archive-dso-stub-config.hh USE_SOURCE_PERMISSIONS ) diff --git a/src/native/monodroid/archive-dso-stub-config.hh.in b/src/native/monodroid/archive-dso-stub-config.hh.in index 14ac7ff66ae..fee7bc6de41 100644 --- a/src/native/monodroid/archive-dso-stub-config.hh.in +++ b/src/native/monodroid/archive-dso-stub-config.hh.in @@ -5,6 +5,7 @@ namespace xamarin::android { struct ArchiveDSOStubConfig { + static inline constexpr size_t PayloadSectionAlignment = @ARCHIVE_DSO_STUB_PAYLOAD_SECTION_ALIGNMENT@; static inline constexpr size_t SectionHeaderEntrySize = @SECTION_HEADER_ENTRY_SIZE@; static inline constexpr size_t SectionHeaderEntryCount = @SECTION_HEADER_ENTRY_COUNT@; static inline constexpr size_t PayloadSectionOffset = @PAYLOAD_SECTION_OFFSET@; From 44fdba44056c6b767167d9bdde9ffbe987c48302 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 1 Aug 2024 22:24:58 +0200 Subject: [PATCH 04/17] Wrapping continued Handling individual assemblies loaded from the filesystem and the apk is next. --- .../Tasks/BuildApk.cs | 17 +++---- .../Utilities/DSOWrapperGenerator.cs | 41 +++++++++++++--- .../monodroid/archive-dso-stub-config.hh.in | 6 ++- .../monodroid/embedded-assemblies-zip.cc | 13 +++-- src/native/monodroid/embedded-assemblies.hh | 47 +++++++++++++++++-- src/native/monodroid/monodroid-glue.cc | 6 +-- src/native/runtime-base/shared-constants.hh | 2 + 7 files changed, 103 insertions(+), 29 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs b/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs index a71214437cb..6c9aff630ab 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs @@ -371,6 +371,7 @@ public override bool RunTask () OutputFiles = outputFiles.Select (a => new TaskItem (a)).ToArray (); Log.LogDebugTaskItems (" [Output] OutputFiles :", OutputFiles); + DSOWrapperGenerator.CleanUp (BuildEngine4, Log); return !Log.HasLoggedErrors; } @@ -404,7 +405,8 @@ void AddRuntimeConfigBlob (ZipArchiveEx apk) // Prefix it with `a` because bundletool sorts entries alphabetically, and this will place it right next to `assemblies.*.blob.so`, which is what we // like since we can finish scanning the zip central directory earlier at startup. string inArchivePath = MakeArchiveLibPath (abi, "libarc.bin.so"); - AddFileToArchiveIfNewer (apk, RuntimeConfigBinFilePath, inArchivePath, compressionMethod: GetCompressionMethod (inArchivePath)); + string wrappedSourcePath = DSOWrapperGenerator.WrapIt (MonoAndroidHelper.AbiToTargetArch (abi), RuntimeConfigBinFilePath, Path.GetFileName (inArchivePath), BuildEngine4, Log); + AddFileToArchiveIfNewer (apk, wrappedSourcePath, inArchivePath, compressionMethod: GetCompressionMethod (inArchivePath)); } } } @@ -504,7 +506,7 @@ void DoAddAssembliesFromArchCollection (AndroidTargetArch arch, Dictionary diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/DSOWrapperGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/DSOWrapperGenerator.cs index 573dd224016..cdd47c233b7 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/DSOWrapperGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/DSOWrapperGenerator.cs @@ -53,6 +53,21 @@ public Config (Dictionary stubPaths, string androi // const ulong PayloadSectionAlignment = 0x4000; + static Config EnsureConfig (IBuildEngine4 buildEngine) + { + var config = buildEngine.GetRegisteredTaskObjectAssemblyLocal (RegisteredConfigKey, RegisteredTaskObjectLifetime.Build); + if (config == null) { + throw new InvalidOperationException ("Internal error: no registered config found"); + } + + return config; + } + + static string GetArchOutputPath (AndroidTargetArch targetArch, Config config) + { + return Path.Combine (config.BaseOutputDirectory, MonoAndroidHelper.ArchToRid (targetArch), "wrapped"); + } + /// /// Puts the indicated file () inside an ELF shared library and returns /// path to the wrapped file. @@ -60,13 +75,10 @@ public Config (Dictionary stubPaths, string androi public static string WrapIt (AndroidTargetArch targetArch, string payloadFilePath, string outputFileName, IBuildEngine4 buildEngine, TaskLoggingHelper log) { log.LogDebugMessage ($"[{targetArch}] Putting '{payloadFilePath}' inside ELF shared library '{outputFileName}'"); - var config = buildEngine.GetRegisteredTaskObjectAssemblyLocal (RegisteredConfigKey, RegisteredTaskObjectLifetime.Build); - if (config == null) { - throw new InvalidOperationException ("Internal error: no registered config found"); - } - - string outputDir = Path.Combine (config.BaseOutputDirectory, MonoAndroidHelper.ArchToRid (targetArch), "wrapped"); + Config config = EnsureConfig (buildEngine); + string outputDir = GetArchOutputPath (targetArch, config); Directory.CreateDirectory (outputDir); + string outputFile = Path.Combine (outputDir, outputFileName); log.LogDebugMessage ($" output file path: {outputFile}"); @@ -74,7 +86,7 @@ public static string WrapIt (AndroidTargetArch targetArch, string payloadFilePat throw new InvalidOperationException ($"Internal error: archive DSO stub location not known for architecture '{targetArch}'"); } - File.Copy (stubItem.ItemSpec, outputFile); + File.Copy (stubItem.ItemSpec, outputFile, overwrite: true); string quotedOutputFile = MonoAndroidHelper.QuoteFileNameArgument (outputFile); string objcopy = Path.Combine (config.AndroidBinUtilsDirectory, MonoAndroidHelper.GetExecutablePath (config.AndroidBinUtilsDirectory, "llvm-objcopy")); @@ -99,7 +111,22 @@ public static string WrapIt (AndroidTargetArch targetArch, string payloadFilePat return outputFile; } + /// + /// Call when all packaging is done. The method will remove all the wrapper shared libraries that were previously + /// created by this class. The reason to do so is to ensure that we don't package any "stale" content and those + /// wrapper files aren't part of any dependency chain so it's hard to check their up to date state. + /// public static void CleanUp (IBuildEngine4 buildEngine, TaskLoggingHelper log) { + Config config = EnsureConfig (buildEngine); + + foreach (var kvp in config.DSOStubPaths) { + string outputDir = GetArchOutputPath (kvp.Key, config); + if (!Directory.Exists (outputDir)) { + continue; + } + + Directory.Delete (outputDir, recursive: true); + } } } diff --git a/src/native/monodroid/archive-dso-stub-config.hh.in b/src/native/monodroid/archive-dso-stub-config.hh.in index fee7bc6de41..b0aa77eb3e6 100644 --- a/src/native/monodroid/archive-dso-stub-config.hh.in +++ b/src/native/monodroid/archive-dso-stub-config.hh.in @@ -8,6 +8,10 @@ namespace xamarin::android { static inline constexpr size_t PayloadSectionAlignment = @ARCHIVE_DSO_STUB_PAYLOAD_SECTION_ALIGNMENT@; static inline constexpr size_t SectionHeaderEntrySize = @SECTION_HEADER_ENTRY_SIZE@; static inline constexpr size_t SectionHeaderEntryCount = @SECTION_HEADER_ENTRY_COUNT@; - static inline constexpr size_t PayloadSectionOffset = @PAYLOAD_SECTION_OFFSET@; + static inline constexpr uint32_t PayloadSectionOffset = @PAYLOAD_SECTION_OFFSET@; + + // We know that payload section is the last one in the binary, this is an index into + // the section header table. + static inline constexpr size_t PayloadSectionIndex = SectionHeaderEntryCount - 1; }; } diff --git a/src/native/monodroid/embedded-assemblies-zip.cc b/src/native/monodroid/embedded-assemblies-zip.cc index a078cf0c1cd..300306e04b8 100644 --- a/src/native/monodroid/embedded-assemblies-zip.cc +++ b/src/native/monodroid/embedded-assemblies-zip.cc @@ -53,8 +53,12 @@ EmbeddedAssemblies::zip_load_entry_common (size_t entry_index, std::vector close_fd = false; } + const auto [offset, size] = get_adjusted_wrapped_entry_offset_and_size (state); md_mmap_info assembly_store_map = md_mmap_apk_file (fd, state.data_offset, state.file_size, entry_name.get ()); if (close_fd) { close (fd); } - auto header = static_cast(assembly_store_map.area); + auto [payload_start, payload_size] = get_wrapper_dso_payload_pointer_and_size (assembly_store_map.area); + log_debug (LOG_ASSEMBLY, "Adjusted assembly store pointer: %p; size: %zu", payload_start, payload_size); + auto header = static_cast(payload_start); if (header->magic != ASSEMBLY_STORE_MAGIC) { log_fatal (LOG_ASSEMBLY, "Assembly store '%s' is not a valid .NET for Android assembly store file", entry_name.get ()); @@ -200,7 +207,7 @@ EmbeddedAssemblies::map_assembly_store (dynamic_local_string constexpr size_t header_size = sizeof(AssemblyStoreHeader); - assembly_store.data_start = static_cast(assembly_store_map.area); + assembly_store.data_start = static_cast(payload_start); assembly_store.assembly_count = header->entry_count; assembly_store.index_entry_count = header->index_entry_count; assembly_store.assemblies = reinterpret_cast(assembly_store.data_start + header_size + header->index_size); diff --git a/src/native/monodroid/embedded-assemblies.hh b/src/native/monodroid/embedded-assemblies.hh index 4e26e933eb7..7661fec0f9e 100644 --- a/src/native/monodroid/embedded-assemblies.hh +++ b/src/native/monodroid/embedded-assemblies.hh @@ -11,12 +11,15 @@ #include #include +#include #include +#include #include #include #include +#include "archive-dso-stub-config.hh" #include "strings.hh" #include "xamarin-app.hh" #include "cpp-util.hh" @@ -101,6 +104,7 @@ namespace xamarin::android::internal { static constexpr size_t assembly_store_file_path_size = calc_size(apk_lib_dir_name, zip_path_separator, SharedConstants::android_lib_abi, zip_path_separator, assembly_store_prefix, SharedConstants::android_lib_abi, assembly_store_extension, dso_suffix); static constexpr auto assembly_store_file_path = concat_string_views (apk_lib_dir_name, zip_path_separator, SharedConstants::android_lib_abi, zip_path_separator, assembly_store_prefix, SharedConstants::android_lib_abi, assembly_store_extension, dso_suffix); + static constexpr size_t dso_size_overhead = ArchiveDSOStubConfig::PayloadSectionOffset + (ArchiveDSOStubConfig::SectionHeaderEntryCount * ArchiveDSOStubConfig::SectionHeaderEntrySize); public: /* filename is e.g. System.dll, System.dll.mdb, System.pdb */ @@ -155,10 +159,21 @@ namespace xamarin::android::internal { void get_runtime_config_blob (const char *& area, uint32_t& size) const { - area = static_cast(runtime_config_blob_mmap.area); + area = static_cast(runtime_config_data); - abort_unless (runtime_config_blob_mmap.size < std::numeric_limits::max (), "Runtime config binary blob size exceeds %u bytes", std::numeric_limits::max ()); - size = static_cast(runtime_config_blob_mmap.size); + abort_unless (runtime_config_data_size < std::numeric_limits::max (), "Runtime config binary blob size exceeds %u bytes", std::numeric_limits::max ()); + size = static_cast(runtime_config_data_size); + } + + void unmap_runtime_config_blob () + { + if (runtime_config_blob_mmap.area == nullptr) { + return; + } + + munmap (runtime_config_blob_mmap.area, runtime_config_blob_mmap.size); + runtime_config_blob_mmap.area = nullptr; + runtime_config_blob_mmap.size = 0; } bool have_runtime_config_blob () const noexcept @@ -262,6 +277,30 @@ namespace xamarin::android::internal { bool zip_read_entry_info (std::vector const& buf, dynamic_local_string& file_name, ZipEntryLoadState &state); + static std::tuple get_wrapper_dso_payload_pointer_and_size (const void* const mapped_elf) noexcept + { + using Elf_Header = std::conditional_t; + using Elf_SHeader = std::conditional_t; + + auto elf_bytes = static_cast(mapped_elf); + auto elf_header = reinterpret_cast(mapped_elf); + auto section_header = reinterpret_cast(elf_bytes + elf_header->e_shoff); + Elf_SHeader const& payload_hdr = section_header[ArchiveDSOStubConfig::PayloadSectionIndex]; + + return { + const_cast(reinterpret_cast (elf_bytes + ArchiveDSOStubConfig::PayloadSectionOffset)), + payload_hdr.sh_size + }; + } + + static std::tuple get_adjusted_wrapped_entry_offset_and_size (ZipEntryLoadState const& state) noexcept + { + return { + state.data_offset + ArchiveDSOStubConfig::PayloadSectionOffset, + state.file_size - dso_size_overhead + }; + } + std::tuple get_assemblies_prefix_and_length () const noexcept { if (assemblies_prefix_override != nullptr) { @@ -382,6 +421,8 @@ namespace xamarin::android::internal { const char *assemblies_prefix_override = nullptr; md_mmap_info runtime_config_blob_mmap{}; + void *runtime_config_data = nullptr; + size_t runtime_config_data_size = 0; bool runtime_config_blob_found = false; uint32_t number_of_mapped_assembly_stores = 0; uint32_t number_of_zip_dso_entries = 0; diff --git a/src/native/monodroid/monodroid-glue.cc b/src/native/monodroid/monodroid-glue.cc index 1c5aed8025c..3faa74af385 100644 --- a/src/native/monodroid/monodroid-glue.cc +++ b/src/native/monodroid/monodroid-glue.cc @@ -700,11 +700,7 @@ MonodroidRuntime::mono_runtime_init ([[maybe_unused]] JNIEnv *env, [[maybe_unuse void MonodroidRuntime::cleanup_runtime_config (MonovmRuntimeConfigArguments *args, [[maybe_unused]] void *user_data) { - if (args == nullptr || args->kind != 1 || args->runtimeconfig.data.data == nullptr) { - return; - } - - munmap (static_cast(const_cast(args->runtimeconfig.data.data)), args->runtimeconfig.data.data_len); + embeddedAssemblies.unmap_runtime_config_blob (); } MonoDomain* diff --git a/src/native/runtime-base/shared-constants.hh b/src/native/runtime-base/shared-constants.hh index d2c0cfcd08d..fa576c466bf 100644 --- a/src/native/runtime-base/shared-constants.hh +++ b/src/native/runtime-base/shared-constants.hh @@ -124,6 +124,8 @@ namespace xamarin::android::internal static constexpr std::string_view xamarin_native_tracing_name { "libxamarin-native-tracing.so" }; static constexpr hash_t xamarin_native_tracing_name_hash = xxhash::hash (xamarin_native_tracing_name); + + static constexpr bool is_64_bit_target = __SIZEOF_POINTER__ == 8; }; } #endif // __SHARED_CONSTANTS_HH From f9b71fa3f9c66c8af49cd5e05a1d20bf58d13e32 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Fri, 2 Aug 2024 17:48:36 +0200 Subject: [PATCH 05/17] Things appear to work fine locally... ...let's see what breaks on CI --- .../monodroid/embedded-assemblies-zip.cc | 10 ++----- src/native/monodroid/embedded-assemblies.cc | 13 ++++++--- src/native/monodroid/embedded-assemblies.hh | 29 +++++++++++++++---- src/native/runtime-base/shared-constants.hh | 5 ++++ 4 files changed, 40 insertions(+), 17 deletions(-) diff --git a/src/native/monodroid/embedded-assemblies-zip.cc b/src/native/monodroid/embedded-assemblies-zip.cc index 300306e04b8..c3b37ab0471 100644 --- a/src/native/monodroid/embedded-assemblies-zip.cc +++ b/src/native/monodroid/embedded-assemblies-zip.cc @@ -54,11 +54,7 @@ EmbeddedAssemblies::zip_load_entry_common (size_t entry_index, std::vector close_fd = false; } - const auto [offset, size] = get_adjusted_wrapped_entry_offset_and_size (state); md_mmap_info assembly_store_map = md_mmap_apk_file (fd, state.data_offset, state.file_size, entry_name.get ()); if (close_fd) { close (fd); } - auto [payload_start, payload_size] = get_wrapper_dso_payload_pointer_and_size (assembly_store_map.area); + + auto [payload_start, payload_size] = get_wrapper_dso_payload_pointer_and_size (assembly_store_map); log_debug (LOG_ASSEMBLY, "Adjusted assembly store pointer: %p; size: %zu", payload_start, payload_size); auto header = static_cast(payload_start); diff --git a/src/native/monodroid/embedded-assemblies.cc b/src/native/monodroid/embedded-assemblies.cc index 57733c6349e..722d9b67959 100644 --- a/src/native/monodroid/embedded-assemblies.cc +++ b/src/native/monodroid/embedded-assemblies.cc @@ -161,7 +161,7 @@ EmbeddedAssemblies::map_runtime_file (XamarinAndroidBundledAssembly& file) noexc int fd; bool close_fd; if (!AndroidSystem::is_embedded_dso_mode_enabled ()) { - log_debug (LOG_ASSEMBLY, "Mapping a runtime file from a filesystem"); + log_debug (LOG_ASSEMBLY, "Mapping a runtime file from filesystem"); close_fd = true; // file.file_fd refers to the directory where our files live @@ -180,14 +180,19 @@ EmbeddedAssemblies::map_runtime_file (XamarinAndroidBundledAssembly& file) noexc close (fd); } + auto [payload_data, payload_size] = get_wrapper_dso_payload_pointer_and_size (map_info); + + // `data_size` might have been ELF wrapper file size, we must store payload size here (the actual DLL size) + file.data_size = static_cast(payload_size); + if (MonodroidState::is_startup_in_progress ()) { - file.data = static_cast(map_info.area); + file.data = static_cast(payload_data); } else { uint8_t *expected_null = nullptr; bool already_mapped = !__atomic_compare_exchange ( /* ptr */ &file.data, /* expected */ &expected_null, - /* desired */ reinterpret_cast(&map_info.area), + /* desired */ reinterpret_cast(&payload_data), /* weak */ false, /* success_memorder */ __ATOMIC_ACQUIRE, /* failure_memorder */ __ATOMIC_RELAXED @@ -1339,7 +1344,7 @@ EmbeddedAssemblies::register_from_filesystem (const char *lib_dir_path,bool look } runtime_config_blob_mmap = md_mmap_apk_file (fd.value (), 0, file_size.value (), cur->d_name); - runtime_config_blob_found = true; + store_mapped_runtime_config_data (runtime_config_blob_mmap); continue; } diff --git a/src/native/monodroid/embedded-assemblies.hh b/src/native/monodroid/embedded-assemblies.hh index 7661fec0f9e..a09b61520ea 100644 --- a/src/native/monodroid/embedded-assemblies.hh +++ b/src/native/monodroid/embedded-assemblies.hh @@ -277,13 +277,28 @@ namespace xamarin::android::internal { bool zip_read_entry_info (std::vector const& buf, dynamic_local_string& file_name, ZipEntryLoadState &state); - static std::tuple get_wrapper_dso_payload_pointer_and_size (const void* const mapped_elf) noexcept + [[gnu::always_inline]] + static std::tuple get_wrapper_dso_payload_pointer_and_size (md_mmap_info const& map_info) noexcept { using Elf_Header = std::conditional_t; using Elf_SHeader = std::conditional_t; + const void* const mapped_elf = map_info.area; auto elf_bytes = static_cast(mapped_elf); auto elf_header = reinterpret_cast(mapped_elf); + + if constexpr (SharedConstants::debug_build) { + // In debug mode we might be dealing with plain data, without DSO wrapper + if (elf_header->e_ident[0] != EI_MAG0 || + elf_header->e_ident[1] != EI_MAG1 || + elf_header->e_ident[2] != EI_MAG2 || + elf_header->e_ident[3] != EI_MAG3) { + log_debug (LOG_ASSEMBLY, "Not an ELF image"); + // Not an ELF image, just return what we mmapped before + return { map_info.area, map_info.size }; + } + } + auto section_header = reinterpret_cast(elf_bytes + elf_header->e_shoff); Elf_SHeader const& payload_hdr = section_header[ArchiveDSOStubConfig::PayloadSectionIndex]; @@ -293,12 +308,14 @@ namespace xamarin::android::internal { }; } - static std::tuple get_adjusted_wrapped_entry_offset_and_size (ZipEntryLoadState const& state) noexcept + [[gnu::always_inline]] + void store_mapped_runtime_config_data (md_mmap_info const& map_info) noexcept { - return { - state.data_offset + ArchiveDSOStubConfig::PayloadSectionOffset, - state.file_size - dso_size_overhead - }; + auto [payload_start, payload_size] = get_wrapper_dso_payload_pointer_and_size (map_info); + log_debug (LOG_ASSEMBLY, "Runtime config: payload pointer %p ; size %zu", payload_start, payload_size); + runtime_config_data = payload_start; + runtime_config_data_size = payload_size; + runtime_config_blob_found = true; } std::tuple get_assemblies_prefix_and_length () const noexcept diff --git a/src/native/runtime-base/shared-constants.hh b/src/native/runtime-base/shared-constants.hh index fa576c466bf..d21c99fa79f 100644 --- a/src/native/runtime-base/shared-constants.hh +++ b/src/native/runtime-base/shared-constants.hh @@ -126,6 +126,11 @@ namespace xamarin::android::internal static constexpr hash_t xamarin_native_tracing_name_hash = xxhash::hash (xamarin_native_tracing_name); static constexpr bool is_64_bit_target = __SIZEOF_POINTER__ == 8; +#if defined(DEBUG) + static constexpr bool debug_build = true; +#else + static constexpr bool debug_build = false; +#endif }; } #endif // __SHARED_CONSTANTS_HH From dbc7e4649cec1ed32ca57893be6868e535a96f74 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Wed, 21 Aug 2024 17:37:50 +0200 Subject: [PATCH 06/17] Add support for stores inside real ELF libraries --- .../Tasks/PrepareDSOWrapperState.cs | 4 +- .../AssemblyStore/AssemblyStoreExplorer.cs | 1 + .../AssemblyStore/FileFormat.cs | 1 + .../AssemblyStore/StoreReader_V2.cs | 73 ++++++++++++++++++- .../AssemblyStore/Utils.cs | 2 + .../assembly-store-reader.csproj | 1 + 6 files changed, 79 insertions(+), 3 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/PrepareDSOWrapperState.cs b/src/Xamarin.Android.Build.Tasks/Tasks/PrepareDSOWrapperState.cs index 433e2e02d3e..fca949c7b6c 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/PrepareDSOWrapperState.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/PrepareDSOWrapperState.cs @@ -10,8 +10,8 @@ namespace Xamarin.Android.Tasks; /// /// Registers a state object used by the DSOWrapperGenerator class later on during -/// the build. This is to avoid having to pask parameters to some tasks (esp. BuildApk) -/// which not necessarily need those parameters directly. Registering the state here +/// the build. This is to avoid having to pass parameters to some tasks (esp. BuildApk) +/// which do not necessarily need those parameters directly. Registering the state here /// also avoids having to update monodroid whenever any required parameter is added to /// BuildApk. /// diff --git a/tools/assembly-store-reader-mk2/AssemblyStore/AssemblyStoreExplorer.cs b/tools/assembly-store-reader-mk2/AssemblyStore/AssemblyStoreExplorer.cs index c99a23d0636..52e6f20fbf8 100644 --- a/tools/assembly-store-reader-mk2/AssemblyStore/AssemblyStoreExplorer.cs +++ b/tools/assembly-store-reader-mk2/AssemblyStore/AssemblyStoreExplorer.cs @@ -61,6 +61,7 @@ public static (IList? explorers, string? errorMessage) Op return (null, $"File '{inputFile}' is a ZIP archive, but not an Android one."); case FileFormat.AssemblyStore: + case FileFormat.ELF: return (new List { new AssemblyStoreExplorer (info)}, null); case FileFormat.Aab: diff --git a/tools/assembly-store-reader-mk2/AssemblyStore/FileFormat.cs b/tools/assembly-store-reader-mk2/AssemblyStore/FileFormat.cs index 4a02e0ae0c3..7cca3f8d0a1 100644 --- a/tools/assembly-store-reader-mk2/AssemblyStore/FileFormat.cs +++ b/tools/assembly-store-reader-mk2/AssemblyStore/FileFormat.cs @@ -6,6 +6,7 @@ enum FileFormat AabBase, Apk, AssemblyStore, + ELF, Zip, Unknown, } diff --git a/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs b/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs index f434139387a..5a18d5ee0df 100644 --- a/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs +++ b/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs @@ -3,6 +3,9 @@ using System.IO; using System.Text; +using ELFSharp.ELF; +using ELFSharp.ELF.Sections; + using Xamarin.Android.Tools; using Xamarin.Android.Tasks; @@ -31,6 +34,7 @@ partial class StoreReader_V2 : AssemblyStoreReader readonly HashSet supportedVersions; Header? header; + ulong elfOffset = 0; static StoreReader_V2 () { @@ -89,6 +93,14 @@ protected override bool IsSupported () using var reader = CreateReader (); uint magic = reader.ReadUInt32 (); + if (magic == Utils.ELF_MAGIC) { + elfOffset = FindELFPayloadSectionOffset (); + if (elfOffset >= 0) { + StoreStream.Seek ((long)elfOffset, SeekOrigin.Begin); + magic = reader.ReadUInt32 (); + } + } + if (magic != Utils.ASSEMBLY_STORE_MAGIC) { Log.Debug ($"Store '{StorePath}' has invalid header magic number."); return false; @@ -108,6 +120,65 @@ protected override bool IsSupported () return true; } + ulong FindELFPayloadSectionOffset () + { + StoreStream.Seek (0, SeekOrigin.Begin); + Class elfClass = ELFReader.CheckELFType (StoreStream); + if (elfClass == Class.NotELF) { + Log.Debug ($"Store '{StorePath}' is not a valid ELF binary"); + return 0; + } + + if (!ELFReader.TryLoad (StoreStream, shouldOwnStream: false, out IELF? elf)) { + return LogErrorAndReturn ($"Store '{StorePath}' could not be loaded", elf); + } + + if (elf.Type != FileType.SharedObject) { + return LogErrorAndReturn ($"Store '{StorePath}' is not a shared ELF library", elf); + } + + if (elf.Endianess != ELFSharp.Endianess.LittleEndian) { + return LogErrorAndReturn ($"Store '{StorePath}' is not a little-endian ELF image", elf); + } + + if (!elf.TryGetSection ("payload", out ISection? payloadSection)) { + return LogErrorAndReturn ($"Store '{StorePath}' does not contain the 'payload' section", elf); + } + + bool is64 = elf.Machine switch { + Machine.ARM => false, + Machine.Intel386 => false, + + Machine.AArch64 => true, + Machine.AMD64 => true, + + _ => throw new NotSupportedException ($"Unsupported ELF architecture '{elf.Machine}'") + }; + + ulong offset; + if (is64) { + offset = GetDataOffset ((Section)payloadSection); + } else { + offset = GetDataOffset ((Section)payloadSection); + } + + elf.Dispose (); + return offset; + + ulong GetDataOffset (Section payload) where T: struct + { + return (ulong)(object)payload.Offset; + } + + ulong LogErrorAndReturn (string message, IELF? elf) + { + Log.Debug (message); + elf?.Dispose (); + + return 0; + } + } + protected override void Prepare () { if (header == null) { @@ -126,7 +197,7 @@ protected override void Prepare () AssemblyCount = header.entry_count; IndexEntryCount = header.index_entry_count; - StoreStream.Seek (Header.NativeSize, SeekOrigin.Begin); + StoreStream.Seek ((long)elfOffset + Header.NativeSize, SeekOrigin.Begin); using var reader = CreateReader (); var index = new List (); diff --git a/tools/assembly-store-reader-mk2/AssemblyStore/Utils.cs b/tools/assembly-store-reader-mk2/AssemblyStore/Utils.cs index d36d86fbd71..75cfcac5aac 100644 --- a/tools/assembly-store-reader-mk2/AssemblyStore/Utils.cs +++ b/tools/assembly-store-reader-mk2/AssemblyStore/Utils.cs @@ -23,6 +23,7 @@ static class Utils public const uint ZIP_MAGIC = 0x4034b50; public const uint ASSEMBLY_STORE_MAGIC = 0x41424158; + public const uint ELF_MAGIC = 0x464c457f; public static readonly ArrayPool BytePool = ArrayPool.Shared; @@ -42,6 +43,7 @@ public static (FileFormat format, FileInfo? info) DetectFileFormat (string path) // ATM, all formats we recognize have 4-byte magic at the start FileFormat format = reader.ReadUInt32 () switch { Utils.ZIP_MAGIC => FileFormat.Zip, + Utils.ELF_MAGIC => FileFormat.ELF, Utils.ASSEMBLY_STORE_MAGIC => FileFormat.AssemblyStore, _ => FileFormat.Unknown }; diff --git a/tools/assembly-store-reader-mk2/assembly-store-reader.csproj b/tools/assembly-store-reader-mk2/assembly-store-reader.csproj index 81520d99671..ec4c90fecb1 100644 --- a/tools/assembly-store-reader-mk2/assembly-store-reader.csproj +++ b/tools/assembly-store-reader-mk2/assembly-store-reader.csproj @@ -19,6 +19,7 @@ + From 259e4b7d7c2f328df558e234d1660b86e588f978 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 22 Aug 2024 12:07:00 +0200 Subject: [PATCH 07/17] Don't use generics here --- .../AssemblyStore/StoreReader_V2.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs b/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs index 5a18d5ee0df..7db4a0e108c 100644 --- a/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs +++ b/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs @@ -157,17 +157,22 @@ ulong FindELFPayloadSectionOffset () ulong offset; if (is64) { - offset = GetDataOffset ((Section)payloadSection); + offset = GetDataOffset64 ((Section)payloadSection); } else { - offset = GetDataOffset ((Section)payloadSection); + offset = GetDataOffset32 ((Section)payloadSection); } elf.Dispose (); return offset; - ulong GetDataOffset (Section payload) where T: struct + ulong GetDataOffset64 (Section payload) { - return (ulong)(object)payload.Offset; + return payload.Offset; + } + + ulong GetDataOffset32 (Section payload) + { + return (ulong)payload.Offset; } ulong LogErrorAndReturn (string message, IELF? elf) From 38e0bd589499a823e7ee6620d1aa47dd0766d24d Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 22 Aug 2024 17:06:00 +0200 Subject: [PATCH 08/17] Well. OK. Umm. Fixed. --- src/native/monodroid/embedded-assemblies.hh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/native/monodroid/embedded-assemblies.hh b/src/native/monodroid/embedded-assemblies.hh index a09b61520ea..5b703b359de 100644 --- a/src/native/monodroid/embedded-assemblies.hh +++ b/src/native/monodroid/embedded-assemblies.hh @@ -289,10 +289,10 @@ namespace xamarin::android::internal { if constexpr (SharedConstants::debug_build) { // In debug mode we might be dealing with plain data, without DSO wrapper - if (elf_header->e_ident[0] != EI_MAG0 || - elf_header->e_ident[1] != EI_MAG1 || - elf_header->e_ident[2] != EI_MAG2 || - elf_header->e_ident[3] != EI_MAG3) { + if (elf_header->e_ident[EI_MAG0] != ELFMAG0 || + elf_header->e_ident[EI_MAG1] != ELFMAG1 || + elf_header->e_ident[EI_MAG2] != ELFMAG2 || + elf_header->e_ident[EI_MAG3] != ELFMAG3) { log_debug (LOG_ASSEMBLY, "Not an ELF image"); // Not an ELF image, just return what we mmapped before return { map_info.area, map_info.size }; From fcc7d6e6f4b08cbb0e68f7ce9e6a72b27b9bb4df Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 22 Aug 2024 21:25:54 +0200 Subject: [PATCH 09/17] Let's see what that'll do --- .../Utilities/ArchiveAssemblyHelper.cs | 40 ++++++++- .../AssemblyStore/ELFPayloadError.cs | 11 +++ .../AssemblyStore/StoreReader_V2.cs | 83 ++++--------------- .../AssemblyStore/Utils.cs | 66 +++++++++++++++ 4 files changed, 129 insertions(+), 71 deletions(-) create mode 100644 tools/assembly-store-reader-mk2/AssemblyStore/ELFPayloadError.cs diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/ArchiveAssemblyHelper.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/ArchiveAssemblyHelper.cs index e4dfa7b26de..23beb499d7f 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/ArchiveAssemblyHelper.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/ArchiveAssemblyHelper.cs @@ -64,8 +64,44 @@ public ArchiveAssemblyHelper (string archivePath, bool useAssemblyStores = true, ret = ReadZipEntry (path, arch, uncompressIfNecessary); } - ret?.Seek (0, SeekOrigin.Begin); - return ret; + if (ret == null) { + return null; + } + + ret.Seek (0, SeekOrigin.Begin); + (ulong elfPayloadOffset, ulong elfPayloadSize, ELFPayloadError error) = Xamarin.Android.AssemblyStore.Utils.FindELFPayloadSectionOffsetAndSize (ret); + + if (error != ELFPayloadError.None) { + string message = error switch { + ELFPayloadError.NotELF => $"Entry '{path}' is not a valid ELF binary", + ELFPayloadError.LoadFailed => $"Entry '{path}' could not be loaded", + ELFPayloadError.NotSharedLibrary => $"Entry '{path}' is not a shared ELF library", + ELFPayloadError.NotLittleEndian => $"Entry '{path}' is not a little-endian ELF image", + ELFPayloadError.NoPayloadSection => $"Entry '{path}' does not contain the 'payload' section", + _ => $"Unknown ELF payload section error for entry '{path}': {error}" + }; + Console.WriteLine (message); + } else { + Console.WriteLine ($"Extracted content from ELF image '{path}'"); + } + + ret.Seek (0, SeekOrigin.Begin); + if (elfPayloadOffset == 0) { + return ret; + } + + // Make a copy of JUST the payload section, so that it contains only the data the tests expect and support + var payload = new MemoryStream (); + var data = buffers.Rent (16384); + int nRead = 0; + + while ((nRead = ret.Read (data, 0, data.Length)) > 0) { + payload.Write (data, 0, nRead); + } + payload.Flush (); + ret.Dispose (); + + return payload; } Stream? ReadZipEntry (string path, AndroidTargetArch arch, bool uncompressIfNecessary) diff --git a/tools/assembly-store-reader-mk2/AssemblyStore/ELFPayloadError.cs b/tools/assembly-store-reader-mk2/AssemblyStore/ELFPayloadError.cs new file mode 100644 index 00000000000..932f151c80f --- /dev/null +++ b/tools/assembly-store-reader-mk2/AssemblyStore/ELFPayloadError.cs @@ -0,0 +1,11 @@ +namespace Xamarin.Android.AssemblyStore; + +enum ELFPayloadError +{ + None, + NotELF, + LoadFailed, + NotSharedLibrary, + NotLittleEndian, + NoPayloadSection, +} diff --git a/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs b/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs index 7db4a0e108c..ba12f52749e 100644 --- a/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs +++ b/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs @@ -3,9 +3,6 @@ using System.IO; using System.Text; -using ELFSharp.ELF; -using ELFSharp.ELF.Sections; - using Xamarin.Android.Tools; using Xamarin.Android.Tasks; @@ -94,8 +91,20 @@ protected override bool IsSupported () uint magic = reader.ReadUInt32 (); if (magic == Utils.ELF_MAGIC) { - elfOffset = FindELFPayloadSectionOffset (); - if (elfOffset >= 0) { + ELFPayloadError error; + (elfOffset, _, error) = Utils.FindELFPayloadSectionOffsetAndSize (StoreStream); + + if (error != ELFPayloadError.None) { + string message = error switch { + ELFPayloadError.NotELF => $"Store '{StorePath}' is not a valid ELF binary", + ELFPayloadError.LoadFailed => $"Store '{StorePath}' could not be loaded", + ELFPayloadError.NotSharedLibrary => $"Store '{StorePath}' is not a shared ELF library", + ELFPayloadError.NotLittleEndian => $"Store '{StorePath}' is not a little-endian ELF image", + ELFPayloadError.NoPayloadSection => $"Store '{StorePath}' does not contain the 'payload' section", + _ => $"Unknown ELF payload section error for store '{StorePath}': {error}" + }; + Log.Debug (message); + } else if (elfOffset >= 0) { StoreStream.Seek ((long)elfOffset, SeekOrigin.Begin); magic = reader.ReadUInt32 (); } @@ -120,70 +129,6 @@ protected override bool IsSupported () return true; } - ulong FindELFPayloadSectionOffset () - { - StoreStream.Seek (0, SeekOrigin.Begin); - Class elfClass = ELFReader.CheckELFType (StoreStream); - if (elfClass == Class.NotELF) { - Log.Debug ($"Store '{StorePath}' is not a valid ELF binary"); - return 0; - } - - if (!ELFReader.TryLoad (StoreStream, shouldOwnStream: false, out IELF? elf)) { - return LogErrorAndReturn ($"Store '{StorePath}' could not be loaded", elf); - } - - if (elf.Type != FileType.SharedObject) { - return LogErrorAndReturn ($"Store '{StorePath}' is not a shared ELF library", elf); - } - - if (elf.Endianess != ELFSharp.Endianess.LittleEndian) { - return LogErrorAndReturn ($"Store '{StorePath}' is not a little-endian ELF image", elf); - } - - if (!elf.TryGetSection ("payload", out ISection? payloadSection)) { - return LogErrorAndReturn ($"Store '{StorePath}' does not contain the 'payload' section", elf); - } - - bool is64 = elf.Machine switch { - Machine.ARM => false, - Machine.Intel386 => false, - - Machine.AArch64 => true, - Machine.AMD64 => true, - - _ => throw new NotSupportedException ($"Unsupported ELF architecture '{elf.Machine}'") - }; - - ulong offset; - if (is64) { - offset = GetDataOffset64 ((Section)payloadSection); - } else { - offset = GetDataOffset32 ((Section)payloadSection); - } - - elf.Dispose (); - return offset; - - ulong GetDataOffset64 (Section payload) - { - return payload.Offset; - } - - ulong GetDataOffset32 (Section payload) - { - return (ulong)payload.Offset; - } - - ulong LogErrorAndReturn (string message, IELF? elf) - { - Log.Debug (message); - elf?.Dispose (); - - return 0; - } - } - protected override void Prepare () { if (header == null) { diff --git a/tools/assembly-store-reader-mk2/AssemblyStore/Utils.cs b/tools/assembly-store-reader-mk2/AssemblyStore/Utils.cs index 75cfcac5aac..284f501c9ac 100644 --- a/tools/assembly-store-reader-mk2/AssemblyStore/Utils.cs +++ b/tools/assembly-store-reader-mk2/AssemblyStore/Utils.cs @@ -2,6 +2,8 @@ using System.IO; using System.Buffers; +using ELFSharp.ELF; +using ELFSharp.ELF.Sections; using Xamarin.Tools.Zip; namespace Xamarin.Android.AssemblyStore; @@ -27,6 +29,70 @@ static class Utils public static readonly ArrayPool BytePool = ArrayPool.Shared; + public static (ulong offset, ulong size, ELFPayloadError error) FindELFPayloadSectionOffsetAndSize (Stream stream) + { + stream.Seek (0, SeekOrigin.Begin); + Class elfClass = ELFReader.CheckELFType (stream); + if (elfClass == Class.NotELF) { + return ReturnError (null, ELFPayloadError.NotELF); + } + + if (!ELFReader.TryLoad (stream, shouldOwnStream: false, out IELF? elf)) { + return ReturnError (elf, ELFPayloadError.LoadFailed); + } + + if (elf.Type != FileType.SharedObject) { + return ReturnError (elf, ELFPayloadError.NotSharedLibrary); + } + + if (elf.Endianess != ELFSharp.Endianess.LittleEndian) { + return ReturnError (elf, ELFPayloadError.NotLittleEndian); + } + + if (!elf.TryGetSection ("payload", out ISection? payloadSection)) { + return ReturnError (elf, ELFPayloadError.NoPayloadSection); + } + + bool is64 = elf.Machine switch { + Machine.ARM => false, + Machine.Intel386 => false, + + Machine.AArch64 => true, + Machine.AMD64 => true, + + _ => throw new NotSupportedException ($"Unsupported ELF architecture '{elf.Machine}'") + }; + + ulong offset; + ulong size; + + if (is64) { + (offset, size) = GetOffsetAndSize64 ((Section)payloadSection); + } else { + (offset, size) = GetOffsetAndSize32 ((Section)payloadSection); + } + + elf.Dispose (); + return (offset, size, ELFPayloadError.None); + + (ulong offset, ulong size) GetOffsetAndSize64 (Section payload) + { + return (payload.Offset, payload.Size); + } + + (ulong offset, ulong size) GetOffsetAndSize32 (Section payload) + { + return ((ulong)payload.Offset, (ulong)payload.Size); + } + + (ulong offset, ulong size, ELFPayloadError error) ReturnError (IELF? elf, ELFPayloadError error) + { + elf?.Dispose (); + + return (0, 0, error); + } + } + public static (FileFormat format, FileInfo? info) DetectFileFormat (string path) { if (String.IsNullOrEmpty (path)) { From 525fcf0471e0ba3adffb41300daab04fc7b9d30b Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Fri, 23 Aug 2024 12:44:00 +0200 Subject: [PATCH 10/17] Fixes --- .../Utilities/ArchiveAssemblyHelper.cs | 17 +++++++++++++++-- .../AssemblyStore/AssemblyStoreReader.cs | 5 ++++- .../AssemblyStore/StoreReader_V1.cs | 2 ++ .../AssemblyStore/StoreReader_V2.cs | 2 ++ 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/ArchiveAssemblyHelper.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/ArchiveAssemblyHelper.cs index 23beb499d7f..73541abf637 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/ArchiveAssemblyHelper.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/ArchiveAssemblyHelper.cs @@ -68,6 +68,7 @@ public ArchiveAssemblyHelper (string archivePath, bool useAssemblyStores = true, return null; } + ret.Flush (); ret.Seek (0, SeekOrigin.Begin); (ulong elfPayloadOffset, ulong elfPayloadSize, ELFPayloadError error) = Xamarin.Android.AssemblyStore.Utils.FindELFPayloadSectionOffsetAndSize (ret); @@ -85,22 +86,34 @@ public ArchiveAssemblyHelper (string archivePath, bool useAssemblyStores = true, Console.WriteLine ($"Extracted content from ELF image '{path}'"); } - ret.Seek (0, SeekOrigin.Begin); if (elfPayloadOffset == 0) { + ret.Seek (0, SeekOrigin.Begin); return ret; } // Make a copy of JUST the payload section, so that it contains only the data the tests expect and support var payload = new MemoryStream (); var data = buffers.Rent (16384); + int toRead = data.Length; int nRead = 0; + ulong remaining = elfPayloadSize; - while ((nRead = ret.Read (data, 0, data.Length)) > 0) { + ret.Seek ((long)elfPayloadOffset, SeekOrigin.Begin); + while (remaining > 0 && (nRead = ret.Read (data, 0, toRead)) > 0) { payload.Write (data, 0, nRead); + remaining -= (ulong)nRead; + + if (remaining < (ulong)data.Length) { + // Make sure the last chunk doesn't gobble in more than we need + toRead = (int)remaining; + } } + buffers.Return (data); + payload.Flush (); ret.Dispose (); + payload.Seek (0, SeekOrigin.Begin); return payload; } diff --git a/tools/assembly-store-reader-mk2/AssemblyStore/AssemblyStoreReader.cs b/tools/assembly-store-reader-mk2/AssemblyStore/AssemblyStoreReader.cs index cc39baa6ecc..982b62645e9 100644 --- a/tools/assembly-store-reader-mk2/AssemblyStore/AssemblyStoreReader.cs +++ b/tools/assembly-store-reader-mk2/AssemblyStore/AssemblyStoreReader.cs @@ -58,10 +58,12 @@ protected AssemblyStoreReader (Stream store, string path) protected abstract bool IsSupported (); protected abstract void Prepare (); + protected abstract ulong GetStoreStartDataOffset (); public Stream ReadEntryImageData (AssemblyStoreItem entry, bool uncompressIfNeeded = false) { - StoreStream.Seek (entry.DataOffset, SeekOrigin.Begin); + ulong startOffset = GetStoreStartDataOffset (); + StoreStream.Seek ((uint)startOffset + entry.DataOffset, SeekOrigin.Begin); var stream = new MemoryStream (); if (uncompressIfNeeded) { @@ -78,6 +80,7 @@ public Stream ReadEntryImageData (AssemblyStoreItem entry, bool uncompressIfNeed remainingToRead -= (long)nread; } stream.Flush (); + stream.Seek (0, SeekOrigin.Begin); return stream; } diff --git a/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V1.cs b/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V1.cs index d907721c088..f60fa3a067b 100644 --- a/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V1.cs +++ b/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V1.cs @@ -31,4 +31,6 @@ protected override bool IsSupported () protected override void Prepare () { } + + protected override ulong GetStoreStartDataOffset () => 0; } diff --git a/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs b/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs index ba12f52749e..a43960102e2 100644 --- a/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs +++ b/tools/assembly-store-reader-mk2/AssemblyStore/StoreReader_V2.cs @@ -84,6 +84,8 @@ public StoreReader_V2 (Stream store, string path) static string GetBlobName (string abi) => $"libassemblies.{abi}.blob.so"; + protected override ulong GetStoreStartDataOffset () => elfOffset; + protected override bool IsSupported () { StoreStream.Seek (0, SeekOrigin.Begin); From 38ae1bc79700cda53b2cd7899b448ce5e8f532eb Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Mon, 26 Aug 2024 12:18:25 +0200 Subject: [PATCH 11/17] Bump LLVM toolchain to 18.1.8 --- build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs index 9a2edceec81..4415d70037d 100644 --- a/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs @@ -15,7 +15,7 @@ namespace Xamarin.Android.Prepare // partial class Configurables { - const string BinutilsVersion = "L_18.1.7-8.0.0"; + const string BinutilsVersion = "L_18.1.8-8.0.0"; const string MicrosoftOpenJDK17Version = "17.0.12"; const string MicrosoftOpenJDK17Release = "17.0.12"; From 3f7b329533018b7373395119f436430272d57a14 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Tue, 27 Aug 2024 10:47:22 +0200 Subject: [PATCH 12/17] Back to LLVM 18.1.6 --- build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs index 4415d70037d..103d07403fc 100644 --- a/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs +++ b/build-tools/xaprepare/xaprepare/ConfigAndData/Configurables.cs @@ -15,7 +15,7 @@ namespace Xamarin.Android.Prepare // partial class Configurables { - const string BinutilsVersion = "L_18.1.8-8.0.0"; + const string BinutilsVersion = "L_18.1.6-8.0.0-1"; const string MicrosoftOpenJDK17Version = "17.0.12"; const string MicrosoftOpenJDK17Release = "17.0.12"; From f3f19c2dfb966e8a46d42a2ddb5d25e3d6f869b0 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Wed, 11 Sep 2024 12:23:55 +0200 Subject: [PATCH 13/17] Update apkdesc --- .../BuildReleaseArm64SimpleDotNet.apkdesc | 26 +-- .../BuildReleaseArm64XFormsDotNet.apkdesc | 148 +++++++++--------- 2 files changed, 87 insertions(+), 87 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc index afb5d34690b..54d10a91441 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc @@ -8,43 +8,43 @@ "Size": 22488 }, "lib/arm64-v8a/lib__Microsoft.Android.Resource.Designer.dll.so": { - "Size": 1114 + "Size": 18208 }, "lib/arm64-v8a/lib_Java.Interop.dll.so": { - "Size": 69279 + "Size": 86368 }, "lib/arm64-v8a/lib_Mono.Android.dll.so": { - "Size": 98660 + "Size": 115752 }, "lib/arm64-v8a/lib_Mono.Android.Runtime.dll.so": { - "Size": 5315 + "Size": 22408 }, "lib/arm64-v8a/lib_System.Console.dll.so": { - "Size": 7294 + "Size": 24384 }, "lib/arm64-v8a/lib_System.Linq.dll.so": { - "Size": 9390 + "Size": 26480 }, "lib/arm64-v8a/lib_System.Private.CoreLib.dll.so": { - "Size": 616700 + "Size": 633792 }, "lib/arm64-v8a/lib_System.Runtime.dll.so": { - "Size": 2956 + "Size": 20048 }, "lib/arm64-v8a/lib_System.Runtime.InteropServices.dll.so": { - "Size": 4502 + "Size": 21592 }, "lib/arm64-v8a/lib_UnnamedProject.dll.so": { - "Size": 2932 + "Size": 20024 }, "lib/arm64-v8a/libarc.bin.so": { - "Size": 1685 + "Size": 18776 }, "lib/arm64-v8a/libmono-component-marshal-ilgen.so": { "Size": 87432 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 484920 + "Size": 485800 }, "lib/arm64-v8a/libmonosgen-2.0.so": { "Size": 3196336 @@ -98,5 +98,5 @@ "Size": 1904 } }, - "PackageSize": 2783765 + "PackageSize": 2791957 } \ No newline at end of file diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc index 59a284fc02a..43092050ff6 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc @@ -29,223 +29,223 @@ "Size": 2396 }, "lib/arm64-v8a/lib__Microsoft.Android.Resource.Designer.dll.so": { - "Size": 2363 + "Size": 19456 }, "lib/arm64-v8a/lib_FormsViewGroup.dll.so": { - "Size": 8330 + "Size": 25424 }, "lib/arm64-v8a/lib_Java.Interop.dll.so": { - "Size": 77673 + "Size": 94768 }, "lib/arm64-v8a/lib_Mono.Android.dll.so": { - "Size": 506473 + "Size": 523568 }, "lib/arm64-v8a/lib_Mono.Android.Runtime.dll.so": { - "Size": 5315 + "Size": 22408 }, "lib/arm64-v8a/lib_mscorlib.dll.so": { - "Size": 4344 + "Size": 21432 }, "lib/arm64-v8a/lib_netstandard.dll.so": { - "Size": 5985 + "Size": 23080 }, "lib/arm64-v8a/lib_System.Collections.Concurrent.dll.so": { - "Size": 12714 + "Size": 29808 }, "lib/arm64-v8a/lib_System.Collections.dll.so": { - "Size": 19198 + "Size": 36288 }, "lib/arm64-v8a/lib_System.Collections.NonGeneric.dll.so": { - "Size": 8668 + "Size": 25760 }, "lib/arm64-v8a/lib_System.Collections.Specialized.dll.so": { - "Size": 6756 + "Size": 23848 }, "lib/arm64-v8a/lib_System.ComponentModel.dll.so": { - "Size": 2496 + "Size": 19584 }, "lib/arm64-v8a/lib_System.ComponentModel.Primitives.dll.so": { - "Size": 4212 + "Size": 21304 }, "lib/arm64-v8a/lib_System.ComponentModel.TypeConverter.dll.so": { - "Size": 25362 + "Size": 42456 }, "lib/arm64-v8a/lib_System.Console.dll.so": { - "Size": 7331 + "Size": 24424 }, "lib/arm64-v8a/lib_System.Core.dll.so": { - "Size": 2365 + "Size": 19456 }, "lib/arm64-v8a/lib_System.Diagnostics.DiagnosticSource.dll.so": { - "Size": 11349 + "Size": 28440 }, "lib/arm64-v8a/lib_System.Diagnostics.TraceSource.dll.so": { - "Size": 7601 + "Size": 24696 }, "lib/arm64-v8a/lib_System.dll.so": { - "Size": 2765 + "Size": 19856 }, "lib/arm64-v8a/lib_System.Drawing.dll.so": { - "Size": 2342 + "Size": 19432 }, "lib/arm64-v8a/lib_System.Drawing.Primitives.dll.so": { - "Size": 12959 + "Size": 30048 }, "lib/arm64-v8a/lib_System.Formats.Asn1.dll.so": { - "Size": 32843 + "Size": 49936 }, "lib/arm64-v8a/lib_System.IO.Compression.Brotli.dll.so": { - "Size": 12393 + "Size": 29488 }, "lib/arm64-v8a/lib_System.IO.Compression.dll.so": { - "Size": 16696 + "Size": 33784 }, "lib/arm64-v8a/lib_System.IO.IsolatedStorage.dll.so": { - "Size": 11196 + "Size": 28288 }, "lib/arm64-v8a/lib_System.Linq.dll.so": { - "Size": 21645 + "Size": 38736 }, "lib/arm64-v8a/lib_System.Linq.Expressions.dll.so": { - "Size": 168722 + "Size": 185816 }, "lib/arm64-v8a/lib_System.Net.Http.dll.so": { - "Size": 72400 + "Size": 89488 }, "lib/arm64-v8a/lib_System.Net.Primitives.dll.so": { - "Size": 24022 + "Size": 41112 }, "lib/arm64-v8a/lib_System.Net.Requests.dll.so": { - "Size": 4461 + "Size": 21552 }, "lib/arm64-v8a/lib_System.ObjectModel.dll.so": { - "Size": 9981 + "Size": 27072 }, "lib/arm64-v8a/lib_System.Private.CoreLib.dll.so": { - "Size": 939274 + "Size": 956368 }, "lib/arm64-v8a/lib_System.Private.DataContractSerialization.dll.so": { - "Size": 199596 + "Size": 216688 }, "lib/arm64-v8a/lib_System.Private.Uri.dll.so": { - "Size": 45095 + "Size": 62184 }, "lib/arm64-v8a/lib_System.Private.Xml.dll.so": { - "Size": 220007 + "Size": 237096 }, "lib/arm64-v8a/lib_System.Private.Xml.Linq.dll.so": { - "Size": 18497 + "Size": 35592 }, "lib/arm64-v8a/lib_System.Runtime.dll.so": { - "Size": 3111 + "Size": 20200 }, "lib/arm64-v8a/lib_System.Runtime.InteropServices.dll.so": { - "Size": 4502 + "Size": 21592 }, "lib/arm64-v8a/lib_System.Runtime.Numerics.dll.so": { - "Size": 37312 + "Size": 54400 }, "lib/arm64-v8a/lib_System.Runtime.Serialization.dll.so": { - "Size": 2265 + "Size": 19360 }, "lib/arm64-v8a/lib_System.Runtime.Serialization.Formatters.dll.so": { - "Size": 3243 + "Size": 20336 }, "lib/arm64-v8a/lib_System.Runtime.Serialization.Primitives.dll.so": { - "Size": 4361 + "Size": 21456 }, "lib/arm64-v8a/lib_System.Security.Cryptography.dll.so": { - "Size": 63408 + "Size": 80496 }, "lib/arm64-v8a/lib_System.Text.RegularExpressions.dll.so": { - "Size": 166506 + "Size": 183600 }, "lib/arm64-v8a/lib_System.Xml.dll.so": { - "Size": 2167 + "Size": 19256 }, "lib/arm64-v8a/lib_System.Xml.Linq.dll.so": { - "Size": 2181 + "Size": 19272 }, "lib/arm64-v8a/lib_UnnamedProject.dll.so": { - "Size": 5007 + "Size": 22096 }, "lib/arm64-v8a/lib_Xamarin.AndroidX.Activity.dll.so": { - "Size": 17872 + "Size": 34960 }, "lib/arm64-v8a/lib_Xamarin.AndroidX.AppCompat.AppCompatResources.dll.so": { - "Size": 7425 + "Size": 24520 }, "lib/arm64-v8a/lib_Xamarin.AndroidX.AppCompat.dll.so": { - "Size": 146152 + "Size": 163240 }, "lib/arm64-v8a/lib_Xamarin.AndroidX.CardView.dll.so": { - "Size": 7469 + "Size": 24560 }, "lib/arm64-v8a/lib_Xamarin.AndroidX.CoordinatorLayout.dll.so": { - "Size": 18823 + "Size": 35912 }, "lib/arm64-v8a/lib_Xamarin.AndroidX.Core.dll.so": { - "Size": 134318 + "Size": 151408 }, "lib/arm64-v8a/lib_Xamarin.AndroidX.CursorAdapter.dll.so": { - "Size": 10076 + "Size": 27168 }, "lib/arm64-v8a/lib_Xamarin.AndroidX.DrawerLayout.dll.so": { - "Size": 16856 + "Size": 33944 }, "lib/arm64-v8a/lib_Xamarin.AndroidX.Fragment.dll.so": { - "Size": 55435 + "Size": 72528 }, "lib/arm64-v8a/lib_Xamarin.AndroidX.Legacy.Support.Core.UI.dll.so": { - "Size": 6806 + "Size": 23896 }, "lib/arm64-v8a/lib_Xamarin.AndroidX.Lifecycle.Common.dll.so": { - "Size": 7982 + "Size": 25072 }, "lib/arm64-v8a/lib_Xamarin.AndroidX.Lifecycle.LiveData.Core.dll.so": { - "Size": 7770 + "Size": 24864 }, "lib/arm64-v8a/lib_Xamarin.AndroidX.Lifecycle.ViewModel.dll.so": { - "Size": 8114 + "Size": 25208 }, "lib/arm64-v8a/lib_Xamarin.AndroidX.Loader.dll.so": { - "Size": 14499 + "Size": 31592 }, "lib/arm64-v8a/lib_Xamarin.AndroidX.RecyclerView.dll.so": { - "Size": 95162 + "Size": 112256 }, "lib/arm64-v8a/lib_Xamarin.AndroidX.SavedState.dll.so": { - "Size": 6050 + "Size": 23144 }, "lib/arm64-v8a/lib_Xamarin.AndroidX.SwipeRefreshLayout.dll.so": { - "Size": 14858 + "Size": 31952 }, "lib/arm64-v8a/lib_Xamarin.AndroidX.ViewPager.dll.so": { - "Size": 20961 + "Size": 38056 }, "lib/arm64-v8a/lib_Xamarin.Forms.Core.dll.so": { - "Size": 563905 + "Size": 581000 }, "lib/arm64-v8a/lib_Xamarin.Forms.Platform.Android.dll.so": { - "Size": 373374 + "Size": 390464 }, "lib/arm64-v8a/lib_Xamarin.Forms.Platform.dll.so": { - "Size": 18753 + "Size": 35848 }, "lib/arm64-v8a/lib_Xamarin.Forms.Xaml.dll.so": { - "Size": 63542 + "Size": 80632 }, "lib/arm64-v8a/lib_Xamarin.Google.Android.Material.dll.so": { - "Size": 67675 + "Size": 84768 }, "lib/arm64-v8a/libarc.bin.so": { - "Size": 1685 + "Size": 18776 }, "lib/arm64-v8a/libmono-component-marshal-ilgen.so": { "Size": 87432 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 484920 + "Size": 485800 }, "lib/arm64-v8a/libmonosgen-2.0.so": { "Size": 3196336 @@ -416,7 +416,7 @@ "Size": 6 }, "META-INF/BNDLTOOL.RSA": { - "Size": 1223 + "Size": 1221 }, "META-INF/BNDLTOOL.SF": { "Size": 98577 @@ -2486,5 +2486,5 @@ "Size": 812848 } }, - "PackageSize": 10579211 + "PackageSize": 10628363 } \ No newline at end of file From d55e91a22fb8c9bdb008cbcdab6ea130cde8cbff Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Wed, 11 Sep 2024 13:00:17 +0200 Subject: [PATCH 14/17] Use a coded error --- Documentation/docs-mobile/messages/index.md | 3 ++- Documentation/docs-mobile/messages/xa0142.md | 17 +++++++++++++++++ .../Properties/Resources.Designer.cs | 11 ++++++++++- .../Properties/Resources.resx | 4 ++++ .../Utilities/MonoAndroidHelper.cs | 2 +- 5 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 Documentation/docs-mobile/messages/xa0142.md diff --git a/Documentation/docs-mobile/messages/index.md b/Documentation/docs-mobile/messages/index.md index 8d46b161ab2..71a9b0f1962 100644 --- a/Documentation/docs-mobile/messages/index.md +++ b/Documentation/docs-mobile/messages/index.md @@ -1,4 +1,4 @@ ---- +-- title: .NET for Android errors and warnings reference description: Build and deployment error and warning codes in .NET for Android, their meanings, and guidance on how to address them. ms.date: 04/11/2024 @@ -104,6 +104,7 @@ or 'Help->Report a Problem' in Visual Studio for Mac. + [XA0139](xa0139.md): `@(AndroidAsset)` `{0}` has invalid `DeliveryType` metadata of `{1}`. Supported values are `installtime`, `ondemand` or `fastfollow` + [XA0140](xa0140.md): + [XA0141](xa0141.md): NuGet package '{0}' version '{1}' contains a shared library '{2}' which is not correctly aligned. See https://developer.android.com/guide/practices/page-sizes for more details ++ [XA0142](xa0142.md): Command '{0}' failed.\n{1} ## XA1xxx: Project related diff --git a/Documentation/docs-mobile/messages/xa0142.md b/Documentation/docs-mobile/messages/xa0142.md new file mode 100644 index 00000000000..a34b20bf12c --- /dev/null +++ b/Documentation/docs-mobile/messages/xa0142.md @@ -0,0 +1,17 @@ +--- +title: .NET for Android error XA0142 +description: XA0141 error code +ms.date: 11/09/2024 +--- +# .NET for Android warning XA0142 + +## Issue + +Command '{0}' failed.\n{1} + +## Solution + +Examine logged output of the failed command for indications of what caused the issue. If no immediate +solution is suggested by the logged messages, please file an issue at https://github.com/dotnet/android +including the full error message and steps that led to the command failing (possibly including a small +repro application). diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs b/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs index 2f44a1df58e..a7aa207a8ee 100644 --- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs +++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs @@ -531,6 +531,15 @@ public static string XA0141 { } /// + /// Looks up a localized string similar to Command '{0}' failed.\n{1} + /// + public static string XA0142 { + get { + return ResourceManager.GetString("XA0142", resourceCulture); + } + } + + /// /// Looks up a localized string similar to There was a problem parsing {0}. This is likely due to incomplete or invalid XML. Exception: {1}. /// public static string XA1000 { @@ -1382,7 +1391,7 @@ public static string XA4249 { return ResourceManager.GetString("XA4249", resourceCulture); } } - + /// /// Looks up a localized string similar to Native library '{0}' will not be bundled because it has an unsupported ABI. Move this file to a directory with a valid Android ABI name such as 'libs/armeabi-v7a/'.. /// diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx index 9f4a6fa5fbd..04864c1f9af 100644 --- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx +++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx @@ -1072,4 +1072,8 @@ To use a custom JDK path for a command line build, set the 'JavaSdkDirectory' MS The following are literal names and should not be translated: Maven, group_id, artifact_id {0} - A Maven artifact specification + + Command '{0}' failed.\n{1} + '{0}' is a failed command name (potentially with path) followed by all the arguments passed to it. {1} is the combined output on the standard error and standard output streams. + diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs index 4b7ce76c853..38335fcebb8 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs @@ -102,7 +102,7 @@ public static int RunProcess (string command, string arguments, TaskLoggingHelpe if (proc.ExitCode != 0) { var sb = MergeStdoutAndStderrMessages (stdoutLines, stderrLines); - log.LogError ($"Command '{psi.FileName} {psi.Arguments}' failed.\n{sb.ToString ()}"); + log.LogCodedError ("XA0142", Properties.Resources.XA0142, $"{psi.FileName} {psi.Arguments}", sb.ToString ()); } try { From e20ec1daf633eb55bdc8be8f093c43b9bbfa821f Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 12 Sep 2024 11:45:08 +0200 Subject: [PATCH 15/17] Address feedback --- .../project-docs/ApkSharedLibraries.md | 190 ++++++++++++++++++ src/native/archive-dso-stub/stub.cc | 7 +- .../monodroid/embedded-assemblies-zip.cc | 4 +- src/native/monodroid/embedded-assemblies.cc | 4 +- src/native/monodroid/embedded-assemblies.hh | 8 +- 5 files changed, 204 insertions(+), 9 deletions(-) create mode 100644 Documentation/project-docs/ApkSharedLibraries.md diff --git a/Documentation/project-docs/ApkSharedLibraries.md b/Documentation/project-docs/ApkSharedLibraries.md new file mode 100644 index 00000000000..c09dc4a20f9 --- /dev/null +++ b/Documentation/project-docs/ApkSharedLibraries.md @@ -0,0 +1,190 @@ +# Shared libraries in .NET for Android applications + +Applications contain a number of shared libraries which are placed in the +per-rid directories inside APK/AAB archives (`lib/ABI/lib*.so`). The libraries +have different purposes and come from different sources: + + 1. .NET PAL (Platform Abstraction Layer), used by various Base Class Library + assemblies. + 2. .NET runtime (`libmonosgen-2.0.so` containing the Mono VM) + 3. AOT images (`libaot*.so`, containing pre-JITed **data** which is loaded by + MonoVM at runtime and processed to turn into executable code) + 4. .NET for Android runtime and support libraries + 5. .NET for Android data payload libraries + +Most of those libraries have fairly obvious purpose and layout, this document +focuses on `.NET for Android` data payload libraries. + +# `.NET for Android` data payload libraries + +## Android packaging introduction + +Android allows applications to ship ABI-specific code inside the APK/AAB archives in +order to enable applications which need some sort of native code, while otherwise written +in a managed language like C#, Java or Kotlin. These libraries must be compiled to target +the platforms supported by Android and they must somehow co-exist in the same APK/AAB +archive (they always have the same name, just target a different platform/ABI). The way +chosen by Android to implement it is to place the per-ABI libraries in the `lib/{ABI}/` +directory of the archive. + +All of the libraries placed in the `lib/{ABI}` directories are expected to be ELF shared +library images, as required by the Android Linux kernel. + +## .NET for Android runtime, libraries and data + +`.NET for Android` runtime is composed of two libraries, one being the pre-compiled runtime +itself (`libmonodroid.so` in the APK) and another library being built together with the +application, containing application-specific dynamically generated code (`libxamarin-app.so` +in the APK). These two libraries together contain all the code and data to make the application +run properly on all the supported targets. + +In addition to the above, `.NET for Android` ships a number of managed assemblies. For a number +of years (starting with `Mono for Android`, through `Xamarin.Android`), all the assemblies had +been completely platform agnostic and, thus, were shipped in a custom directory in the APK archive +named `assemblies/`. However, at some point during transition to `dotnet/runtime` and its BCL, a +handful of managed libraries became platform specific and, thus, had to be shipped in a way that took +the platform requirement into account. As all those libraries shared the same name across platforms, +we had to find a way to package them so that they wouldn't conflict with each other. Thus the +`assemblies/` directory gained a subdirectory per ABI, which contained the platform specific assemblies. +Later on, the same was implemented in [assembly stores](AssemblyStores.md) - they would contain both kinds +of managed assemblies. + +The downside of packaging all the assemblies (or assembly stores) in the `assemblies/` directory was that +all the platforms would get copies of platform specific assemblies for the other supported ABIs, thus wasting +storage on the end user devices. + +Introduction of platform specific assemblies posed another problem. We discovered that in some instances, the +dotnet linker/trimmer would generate assemblies that might fail on certain platforms without us having any +prior warning. The solution to this was to make **all** the assemblies platform specific, making sure that +whatever the trimmer did, we'd always have the correct assembly loaded on the right platform. + +Making all assemblies platform specific, however, poses a problem of APK/AAB size - all of the assemblies would +exist in X copies and we couldn't allow such a big increase of archive size. Thus, all the assemblies (and also +assembly stores as well as a runtime configuration blob file) were moved to the `lib/{ABI}/` directories and +"masqueraded" as ELF shared libraries, by giving them the `lib*.so` names. However, the files were still managed +assemblies, not valid ELF images. + +Earlier this year, however, Google [announced](https://android-developers.googleblog.com/2024/08/adding-16-kb-page-size-to-android.html) that +Android 15 will enable shared libraries aligned to 16k instead of the "traditional" 4k and, at some point, the alignment +will become a requirement for submission to the Play Store. This made us suspect that the libraries in `lib/{ABI}/` will +be actually verified to be valid ELF images at some point and we decided to proactively turn our data files shipped in +those directories into actual ELF shared libraries. The way it is done is described in the following section. + +## Data payload stub library + +ELF binaries consist of a number of sections, which contain code, data (read-only and read-write), debug symbols etc. +However, the ELF specification doesn't dictate names of any of those sections and, thus, developers are free to lay out +ELF binaries any way they see fit, as long as the binary conforms to the ELF specification and the operating system +requirements. This gave us the idea of placing our data files (assemblies, assembly stores, debug data, config files etc) +in a custom section inside the ELF image. The resulting file would pass any verification Android will perform at some +point and, at the same time, it won't slow down our operation because we can still load data directly from the shared +library (by using the `mmap(2)` Unix call) without having to load the ELF image into memory. + +To implement that, we added to our distribution a "stub" of a shared ELF library, which is essentially a small, valid +but otherwise empty ELF image. This stub is built together with the rest of the `.NET for Android` runtime and its +layout is discovered and remembered, so that at runtime we can quickly move to the location where our data lives and +load it as we see fit. The runtime `mmap`s the entire file, looks at the file header and finds the start of payload +section, then stores that location in a pointer for further use. + +The way the data is placed in the ELF image is by appending a new section, called `payload`, to the stub binary at +application build time. This is done by using the `llvm-objcopy` utility, which we ship, and then the result is +packaged into the `lib/{ABI}/` directory. The section is properly aligned, the entire file is a valid ELF image. + +One downside of this approach is that if one were to run the `llvm-strip` or `strip` utility on the resulting +shared libray, the `payload` section (as it uses a "non-standard" name) would be considered by the strip utility +to be unnecessary and summarily removed. + +### Layout of the payload library + +In order to examine content of our "payload" ELF shared library, one can run the `llvm-readelf` utility which is +shipped with the Android NDK (and also part of native developer tools on macOS and Linux distributions which have +the LLVM Clang toolchain installed), or the `readelf` utility which is part of GNU binutils. + +File used in the samples below is the `.NET for Android` assembly store, wrapped in an ELF image for the Arm64 +(`AArch64`) architecture. + +The first command verifies that the file is a valid ELF image and shows the header information, including the +target platform/abi/machine: + +```shell +$ llvm-readelf --file-header libassemblies.arm64-v8a.blob.so +ELF Header: + Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 + Class: ELF64 + Data: 2's complement, little endian + Version: 1 (current) + OS/ABI: UNIX - System V + ABI Version: 0 + Type: DYN (Shared object file) + Machine: AArch64 + Version: 0x1 + Entry point address: 0x0 + Start of program headers: 64 (bytes into file) + Start of section headers: 849480 (bytes into file) + Flags: 0x0 + Size of this header: 64 (bytes) + Size of program headers: 56 (bytes) + Number of program headers: 8 + Size of section headers: 64 (bytes) + Number of section headers: 11 + Section header string table index: 9 +``` + +The second command lists the sections contained within the ELF image, their alignment, sizes and offsets +into the file where the sections begin: + +```shell +$ llvm-readelf --section-headers libassemblies.arm64-v8a.blob.so +There are 11 section headers, starting at offset 0xcf648: + +Section Headers: + [Nr] Name Type Address Off Size ES Flg Lk Inf Al + [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 + [ 1] .note.gnu.build-id NOTE 0000000000000200 000200 000024 00 A 0 0 4 + [ 2] .dynsym DYNSYM 0000000000000228 000228 000030 18 A 5 1 8 + [ 3] .gnu.hash GNU_HASH 0000000000000258 000258 000020 00 A 2 0 8 + [ 4] .hash HASH 0000000000000278 000278 000018 04 A 2 0 4 + [ 5] .dynstr STRTAB 0000000000000290 000290 000032 00 A 0 0 1 + [ 6] .dynamic DYNAMIC 00000000000042c8 0002c8 0000b0 10 WA 5 0 8 + [ 7] .relro_padding NOBITS 0000000000004378 000378 000c88 00 WA 0 0 1 + [ 8] .data PROGBITS 0000000000008378 000378 000001 00 WA 0 0 1 + [ 9] .shstrtab STRTAB 0000000000000000 000379 00005e 00 0 0 1 + [10] payload PROGBITS 0000000000000000 004000 0cb647 00 0 0 16384 +Key to Flags: + W (write), A (alloc), X (execute), M (merge), S (strings), I (info), + L (link order), O (extra OS processing required), G (group), T (TLS), + C (compressed), x (unknown), o (OS specific), E (exclude), + R (retain), p (processor specific) +``` + +Of interest to us is the presence of the `payload` section, its starting offset (it will usually +be `0x4000`, that is 16k into the file but it might be a multiple of the value, if the stub ever +grows) and its size will, obviously, differ depending on the payload. + +The information above is sufficient to verify that the file is valid `.NET for Android` payload +shared library. + +In order to extract payload from the ELF image, one can use the following command: + +```shell +$ llvm-objcopy --dump-section=payload=payload.bin libassemblies.arm64-v8a.blob.so +$ ls -gG payload.bin +-rw-rw-r-- 1 833095 Sep 12 11:32 payload.bin +``` + +To verify the size is correct, we can convert the section size indicated in the section headers +output from hexadecimal to decimal: + +```shell +$ printf "%d\n" 0x0cb647 +833095 +``` + +In this case, the payload file is an assembly store, which should have its first 4 bytes read +`XABA`, we can verify this with the following command: + +```shell +$ hexdump -c -n 4 payload.bin +0000000 X A B A +0000004 +``` diff --git a/src/native/archive-dso-stub/stub.cc b/src/native/archive-dso-stub/stub.cc index a5bd2861db1..7e3d5e0e978 100644 --- a/src/native/archive-dso-stub/stub.cc +++ b/src/native/archive-dso-stub/stub.cc @@ -1,2 +1,7 @@ +// +// This is here to clearly indicate that a `lib/{ABI}/lib*.so` file is really a +// data file for .NET for Android and not a "regular" library. Might be useful +// when analyzing APK/AABs. +// [[gnu::visibility("default")]] -bool i_am_a_dummy_stub = true; +bool dotnet_for_android_data_payload = true; diff --git a/src/native/monodroid/embedded-assemblies-zip.cc b/src/native/monodroid/embedded-assemblies-zip.cc index c3b37ab0471..317b6cb9011 100644 --- a/src/native/monodroid/embedded-assemblies-zip.cc +++ b/src/native/monodroid/embedded-assemblies-zip.cc @@ -54,7 +54,7 @@ EmbeddedAssemblies::zip_load_entry_common (size_t entry_index, std::vector close (fd); } - auto [payload_start, payload_size] = get_wrapper_dso_payload_pointer_and_size (assembly_store_map); + auto [payload_start, payload_size] = get_wrapper_dso_payload_pointer_and_size (assembly_store_map, entry_name.get ()); log_debug (LOG_ASSEMBLY, "Adjusted assembly store pointer: %p; size: %zu", payload_start, payload_size); auto header = static_cast(payload_start); diff --git a/src/native/monodroid/embedded-assemblies.cc b/src/native/monodroid/embedded-assemblies.cc index 722d9b67959..5ecaa8c8623 100644 --- a/src/native/monodroid/embedded-assemblies.cc +++ b/src/native/monodroid/embedded-assemblies.cc @@ -180,7 +180,7 @@ EmbeddedAssemblies::map_runtime_file (XamarinAndroidBundledAssembly& file) noexc close (fd); } - auto [payload_data, payload_size] = get_wrapper_dso_payload_pointer_and_size (map_info); + auto [payload_data, payload_size] = get_wrapper_dso_payload_pointer_and_size (map_info, file.name); // `data_size` might have been ELF wrapper file size, we must store payload size here (the actual DLL size) file.data_size = static_cast(payload_size); @@ -1344,7 +1344,7 @@ EmbeddedAssemblies::register_from_filesystem (const char *lib_dir_path,bool look } runtime_config_blob_mmap = md_mmap_apk_file (fd.value (), 0, file_size.value (), cur->d_name); - store_mapped_runtime_config_data (runtime_config_blob_mmap); + store_mapped_runtime_config_data (runtime_config_blob_mmap, cur->d_name); continue; } diff --git a/src/native/monodroid/embedded-assemblies.hh b/src/native/monodroid/embedded-assemblies.hh index 5b703b359de..39a30b73cfc 100644 --- a/src/native/monodroid/embedded-assemblies.hh +++ b/src/native/monodroid/embedded-assemblies.hh @@ -278,7 +278,7 @@ namespace xamarin::android::internal { bool zip_read_entry_info (std::vector const& buf, dynamic_local_string& file_name, ZipEntryLoadState &state); [[gnu::always_inline]] - static std::tuple get_wrapper_dso_payload_pointer_and_size (md_mmap_info const& map_info) noexcept + static std::tuple get_wrapper_dso_payload_pointer_and_size (md_mmap_info const& map_info, const char *file_name) noexcept { using Elf_Header = std::conditional_t; using Elf_SHeader = std::conditional_t; @@ -293,7 +293,7 @@ namespace xamarin::android::internal { elf_header->e_ident[EI_MAG1] != ELFMAG1 || elf_header->e_ident[EI_MAG2] != ELFMAG2 || elf_header->e_ident[EI_MAG3] != ELFMAG3) { - log_debug (LOG_ASSEMBLY, "Not an ELF image"); + log_debug (LOG_ASSEMBLY, "Not an ELF image: %s", file_name); // Not an ELF image, just return what we mmapped before return { map_info.area, map_info.size }; } @@ -309,9 +309,9 @@ namespace xamarin::android::internal { } [[gnu::always_inline]] - void store_mapped_runtime_config_data (md_mmap_info const& map_info) noexcept + void store_mapped_runtime_config_data (md_mmap_info const& map_info, const char *file_name) noexcept { - auto [payload_start, payload_size] = get_wrapper_dso_payload_pointer_and_size (map_info); + auto [payload_start, payload_size] = get_wrapper_dso_payload_pointer_and_size (map_info, file_name); log_debug (LOG_ASSEMBLY, "Runtime config: payload pointer %p ; size %zu", payload_start, payload_size); runtime_config_data = payload_start; runtime_config_data_size = payload_size; From 2377f07ec073ca6484807672337328e4b196541a Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Mon, 23 Sep 2024 14:53:35 -0400 Subject: [PATCH 16/17] Update TOC.yml --- Documentation/docs-mobile/TOC.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/docs-mobile/TOC.yml b/Documentation/docs-mobile/TOC.yml index ef5e9432fa8..7e6106f9572 100644 --- a/Documentation/docs-mobile/TOC.yml +++ b/Documentation/docs-mobile/TOC.yml @@ -184,6 +184,10 @@ href: messages/xa0139.md - name: XA0140 href: messages/xa0140.md + - name: XA0141 + href: messages/xa0141.md + - name: XA0142 + href: messages/xa0142.md - name: "XA1xxx: Project related" items: - name: "XA1xxx: Project related" From cc8fffa66011a21638af48423807c36531a404ec Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Mon, 23 Sep 2024 15:36:10 -0400 Subject: [PATCH 17/17] Update embedded-assemblies.hh Fix indentation --- src/native/monodroid/embedded-assemblies.hh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/native/monodroid/embedded-assemblies.hh b/src/native/monodroid/embedded-assemblies.hh index 39a30b73cfc..70384ffbcc8 100644 --- a/src/native/monodroid/embedded-assemblies.hh +++ b/src/native/monodroid/embedded-assemblies.hh @@ -290,12 +290,12 @@ namespace xamarin::android::internal { if constexpr (SharedConstants::debug_build) { // In debug mode we might be dealing with plain data, without DSO wrapper if (elf_header->e_ident[EI_MAG0] != ELFMAG0 || - elf_header->e_ident[EI_MAG1] != ELFMAG1 || - elf_header->e_ident[EI_MAG2] != ELFMAG2 || - elf_header->e_ident[EI_MAG3] != ELFMAG3) { - log_debug (LOG_ASSEMBLY, "Not an ELF image: %s", file_name); - // Not an ELF image, just return what we mmapped before - return { map_info.area, map_info.size }; + elf_header->e_ident[EI_MAG1] != ELFMAG1 || + elf_header->e_ident[EI_MAG2] != ELFMAG2 || + elf_header->e_ident[EI_MAG3] != ELFMAG3) { + log_debug (LOG_ASSEMBLY, "Not an ELF image: %s", file_name); + // Not an ELF image, just return what we mmapped before + return { map_info.area, map_info.size }; } }