From 79fb7403cc9aad4091010b63d2fdc222f5f0c4a0 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Tue, 2 Jun 2026 17:15:04 -0700 Subject: [PATCH] Privatize NativeAOT libunwind symbols Fold the NativeAOT libunwind-dependent objects into a private relocatable object and localize the libunwind-origin symbols before archiving the runtime libraries. This prevents bundled libunwind symbols from colliding with platform libunwind implementations while keeping runtime entrypoints externally visible. Assisted-by: Copilot CLI:gpt-5.5 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/nativeaot/Runtime/CMakeLists.txt | 20 +++++- .../nativeaot/Runtime/Full/CMakeLists.txt | 38 ++++++++++- .../GeneratePrivateLibunwindSymbols.cmake | 64 +++++++++++++++++++ 3 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 src/coreclr/nativeaot/Runtime/Full/GeneratePrivateLibunwindSymbols.cmake diff --git a/src/coreclr/nativeaot/Runtime/CMakeLists.txt b/src/coreclr/nativeaot/Runtime/CMakeLists.txt index e491f54a562445..2fbc53d3bf6650 100644 --- a/src/coreclr/nativeaot/Runtime/CMakeLists.txt +++ b/src/coreclr/nativeaot/Runtime/CMakeLists.txt @@ -179,8 +179,23 @@ else() include(${CLR_SRC_NATIVE_DIR}/external/llvm-libunwind.cmake) - list(APPEND FULL_RUNTIME_SOURCES ${LLVM_LIBUNWIND_SOURCES}) - set(RUNTIME_SOURCES_ARCH_ASM ${LLVM_LIBUNWIND_ASM_SOURCES}) + set(NATIVEAOT_LOCALIZE_LIBUNWIND_SYMBOLS OFF) + if(CLR_CMAKE_TARGET_UNIX AND NOT CLR_CMAKE_TARGET_APPLE AND NOT CLR_CMAKE_TARGET_ARCH_WASM) + set(NATIVEAOT_LOCALIZE_LIBUNWIND_SYMBOLS ON) + endif() + + if(NATIVEAOT_LOCALIZE_LIBUNWIND_SYMBOLS) + set(NATIVEAOT_PRIVATE_LIBUNWIND_SOURCES + unix/UnwindHelpers.cpp + unix/UnixNativeCodeManager.cpp + ${LLVM_LIBUNWIND_SOURCES} + ${LLVM_LIBUNWIND_ASM_SOURCES}) + list(REMOVE_ITEM FULL_RUNTIME_SOURCES unix/UnwindHelpers.cpp) + list(REMOVE_ITEM FULL_RUNTIME_SOURCES unix/UnixNativeCodeManager.cpp) + else() + list(APPEND FULL_RUNTIME_SOURCES ${LLVM_LIBUNWIND_SOURCES}) + set(RUNTIME_SOURCES_ARCH_ASM ${LLVM_LIBUNWIND_ASM_SOURCES}) + endif() set(ASM_SUFFIX S) endif() @@ -316,6 +331,7 @@ convert_to_absolute_path(FULL_RUNTIME_SOURCES ${FULL_RUNTIME_SOURCES}) convert_to_absolute_path(SERVER_GC_SOURCES ${SERVER_GC_SOURCES}) convert_to_absolute_path(STANDALONEGC_DISABLED_SOURCES ${STANDALONEGC_DISABLED_SOURCES}) convert_to_absolute_path(STANDALONEGC_ENABLED_SOURCES ${STANDALONEGC_ENABLED_SOURCES}) +convert_to_absolute_path(NATIVEAOT_PRIVATE_LIBUNWIND_SOURCES ${NATIVEAOT_PRIVATE_LIBUNWIND_SOURCES}) convert_to_absolute_path(RUNTIME_SOURCES_ARCH_ASM ${RUNTIME_SOURCES_ARCH_ASM}) convert_to_absolute_path(VXSORT_SOURCES ${VXSORT_SOURCES}) convert_to_absolute_path(DUMMY_VXSORT_SOURCES ${DUMMY_VXSORT_SOURCES}) diff --git a/src/coreclr/nativeaot/Runtime/Full/CMakeLists.txt b/src/coreclr/nativeaot/Runtime/Full/CMakeLists.txt index 1cd73bfd07823d..4dd740dab3d238 100644 --- a/src/coreclr/nativeaot/Runtime/Full/CMakeLists.txt +++ b/src/coreclr/nativeaot/Runtime/Full/CMakeLists.txt @@ -22,11 +22,45 @@ if (CLR_CMAKE_TARGET_WIN32) endif() endif (CLR_CMAKE_TARGET_WIN32) -add_library(Runtime.WorkstationGC STATIC ${COMMON_RUNTIME_SOURCES} ${FULL_RUNTIME_SOURCES} ${RUNTIME_ARCH_ASM_OBJECTS}) +if(NATIVEAOT_PRIVATE_LIBUNWIND_SOURCES) + add_library(Runtime.PrivateLibunwind OBJECT ${NATIVEAOT_PRIVATE_LIBUNWIND_SOURCES}) + + set(NATIVEAOT_PRIVATE_LIBUNWIND_OBJECT "${CMAKE_CURRENT_BINARY_DIR}/Runtime.PrivateLibunwind.o") + set(NATIVEAOT_PRIVATE_LIBUNWIND_SYMBOLS "${CMAKE_CURRENT_BINARY_DIR}/Runtime.PrivateLibunwind.localize") + set(NATIVEAOT_PRIVATE_LIBUNWIND_LINKER "${CMAKE_LINKER}") + if(NATIVEAOT_PRIVATE_LIBUNWIND_LINKER MATCHES "llvm-link(\\.exe)?$") + get_filename_component(NATIVEAOT_PRIVATE_LIBUNWIND_TOOL_DIR "${CMAKE_NM}" DIRECTORY) + set(NATIVEAOT_PRIVATE_LIBUNWIND_LINKER "${NATIVEAOT_PRIVATE_LIBUNWIND_TOOL_DIR}/ld.lld") + endif() + + add_custom_command( + OUTPUT "${NATIVEAOT_PRIVATE_LIBUNWIND_OBJECT}" + COMMAND "${CMAKE_COMMAND}" -E rm -f "${NATIVEAOT_PRIVATE_LIBUNWIND_OBJECT}" + COMMAND "${CMAKE_COMMAND}" + "-DNM_TOOL=${CMAKE_NM}" + "-DOUTPUT_FILE=${NATIVEAOT_PRIVATE_LIBUNWIND_SYMBOLS}" + "-DOBJECT_FILES=$,|>" + -P "${CMAKE_CURRENT_LIST_DIR}/GeneratePrivateLibunwindSymbols.cmake" + COMMAND "${NATIVEAOT_PRIVATE_LIBUNWIND_LINKER}" -r $ -o "${NATIVEAOT_PRIVATE_LIBUNWIND_OBJECT}" + COMMAND "${CMAKE_OBJCOPY}" + "--localize-symbols=${NATIVEAOT_PRIVATE_LIBUNWIND_SYMBOLS}" + "${NATIVEAOT_PRIVATE_LIBUNWIND_OBJECT}" + DEPENDS Runtime.PrivateLibunwind $ + BYPRODUCTS "${NATIVEAOT_PRIVATE_LIBUNWIND_SYMBOLS}" + COMMENT "Privatizing NativeAOT libunwind symbols" + COMMAND_EXPAND_LISTS + VERBATIM + ) + set_source_files_properties("${NATIVEAOT_PRIVATE_LIBUNWIND_OBJECT}" PROPERTIES + EXTERNAL_OBJECT TRUE + GENERATED TRUE) +endif() + +add_library(Runtime.WorkstationGC STATIC ${COMMON_RUNTIME_SOURCES} ${FULL_RUNTIME_SOURCES} ${NATIVEAOT_PRIVATE_LIBUNWIND_OBJECT} ${RUNTIME_ARCH_ASM_OBJECTS}) add_dependencies(Runtime.WorkstationGC aot_eventing_headers) target_link_libraries(Runtime.WorkstationGC PRIVATE aotminipal nativeaot_cdac_contract_descriptor nativeaot_gc_wks_descriptor) -add_library(Runtime.ServerGC STATIC ${COMMON_RUNTIME_SOURCES} ${FULL_RUNTIME_SOURCES} ${SERVER_GC_SOURCES} ${RUNTIME_ARCH_ASM_OBJECTS}) +add_library(Runtime.ServerGC STATIC ${COMMON_RUNTIME_SOURCES} ${FULL_RUNTIME_SOURCES} ${NATIVEAOT_PRIVATE_LIBUNWIND_OBJECT} ${SERVER_GC_SOURCES} ${RUNTIME_ARCH_ASM_OBJECTS}) add_dependencies(Runtime.ServerGC aot_eventing_headers) target_link_libraries(Runtime.ServerGC PRIVATE aotminipal nativeaot_cdac_contract_descriptor nativeaot_gc_wks_descriptor nativeaot_gc_svr_descriptor) diff --git a/src/coreclr/nativeaot/Runtime/Full/GeneratePrivateLibunwindSymbols.cmake b/src/coreclr/nativeaot/Runtime/Full/GeneratePrivateLibunwindSymbols.cmake new file mode 100644 index 00000000000000..39b318be7ca798 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/Full/GeneratePrivateLibunwindSymbols.cmake @@ -0,0 +1,64 @@ +if(NOT DEFINED NM_TOOL) + message(FATAL_ERROR "NM_TOOL is required") +endif() + +if(NOT DEFINED OUTPUT_FILE) + message(FATAL_ERROR "OUTPUT_FILE is required") +endif() + +if(NOT DEFINED OBJECT_FILES) + message(FATAL_ERROR "OBJECT_FILES is required") +endif() + +string(REPLACE "|" ";" OBJECT_FILE_LIST "${OBJECT_FILES}") +set(SYMBOLS) + +foreach(OBJECT_FILE IN LISTS OBJECT_FILE_LIST) + if(OBJECT_FILE MATCHES "UnixNativeCodeManager\\.cpp\\.o$") + continue() + endif() + + execute_process( + COMMAND "${NM_TOOL}" -g --defined-only "${OBJECT_FILE}" + RESULT_VARIABLE NM_RESULT + OUTPUT_VARIABLE NM_OUTPUT + ERROR_VARIABLE NM_ERROR) + + if(NOT NM_RESULT EQUAL 0) + message(FATAL_ERROR "Failed to list symbols from ${OBJECT_FILE}: ${NM_ERROR}") + endif() + + string(REPLACE "\n" ";" NM_LINES "${NM_OUTPUT}") + foreach(NM_LINE IN LISTS NM_LINES) + string(STRIP "${NM_LINE}" NM_LINE) + if(NM_LINE STREQUAL "") + continue() + endif() + + string(REGEX REPLACE "[ \t]+" ";" NM_FIELDS "${NM_LINE}") + list(LENGTH NM_FIELDS NM_FIELD_COUNT) + if(NM_FIELD_COUNT LESS 3) + continue() + endif() + + list(GET NM_FIELDS 2 SYMBOL_NAME) + + # UnwindHelpers.cpp is linked into the private libunwind object because it + # instantiates libunwind templates and references libunwind assembly + # helpers. Its public runtime entrypoints must remain externally visible to + # UnixNativeCodeManager.cpp. + if(SYMBOL_NAME MATCHES "^_ZN13UnwindHelpers") + continue() + endif() + + list(APPEND SYMBOLS "${SYMBOL_NAME}") + endforeach() +endforeach() + +list(REMOVE_DUPLICATES SYMBOLS) +list(SORT SYMBOLS) + +file(WRITE "${OUTPUT_FILE}" "") +foreach(SYMBOL IN LISTS SYMBOLS) + file(APPEND "${OUTPUT_FILE}" "${SYMBOL}\n") +endforeach()