Skip to content

[NAOT][android] Application linking breaks with NDK r29 because of duplicated libunwind symbols #121172

Description

@grendello

Description

Attempting to link the .NET for Android NAOT sample breaks when NDK r29 is installed, with the following error (output broken up into lines to make it easier to read):

Android (13989888, +pgo, +bolt, +lto, +mlgo, based on r563880c) clang version 21.0.0 (https://android.googlesource.com/toolchain/llvm-project 5e96669f06077099aa41290cdb4c5e6fa0f59349)
Target: aarch64-unknown-linux-android21
Thread model: posix
InstalledDir: ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin
 "~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/ld.lld" -EL
 --fix-cortex-a53-843419 -z now -z relro -z max-page-size=16384 --no-rosegment --hash-style=both
 --eh-frame-hdr -m aarch64linux -shared
 -o bin/Release/net10.0-android/android-arm64/native/NativeAOT.so
 ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/../sysroot/usr/lib/aarch64-linux-android/21/crtbegin_so.o
 -L~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/aarch64
 -L~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/../sysroot/usr/lib/aarch64-linux-android/21
 -L~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/../sysroot/usr/lib/aarch64-linux-android
 -L~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/../sysroot/usr/lib
 --compress-debug-sections=zlib obj/Release/net10.0-android/android-arm64/native/NativeAOT.o
 --version-script=obj/Release/net10.0-android/android-arm64/native/NativeAOT.exports
 --export-dynamic
 ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libSystem.Native.a
 ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libSystem.Globalization.Native.a
 ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libSystem.IO.Compression.Native.a
 ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libSystem.Security.Cryptography.Native.Android.a
 ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libbootstrapperdll.o
 ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libRuntime.WorkstationGC.a
 ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libeventpipe-disabled.a
 ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libstandalonegc-disabled.a
 ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libaotminipal.a
 ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libbrotlienc.a
 ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libbrotlidec.a
 ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libbrotlicommon.a
 --build-id=sha1 --as-needed -e 0x0 -ldl -lz -llog -lm -z relro -z now --eh-frame-hdr -z
 max-page-size=16384 -soname libNativeAOT.so --error-unresolved-symbols --no-undefined
 ~/vc/xamarin/xamarin-android-worktrees/ndk-r29/bin/Debug/lib/packs/Microsoft.Android.Runtime.NativeAOT.36.android-arm64/36.1.99/runtimes/android-arm64/native/libnaot-android.release-static-release.a
 ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_static.a
 ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++abi.a
 obj/Release/net10.0-android/android-arm64/android/jni_init_funcs.arm64-v8a.o
 obj/Release/net10.0-android/android-arm64/android/environment.arm64-v8a.o --discard-all
 --gc-sections -T obj/Release/net10.0-android/android-arm64/native/sections.ld
 ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/libclang_rt.builtins-aarch64-android.a
 -l:libunwind.a -ldl -lc
 ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/libclang_rt.builtins-aarch64-android.a
 -l:libunwind.a -ldl
 ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/../sysroot/usr/lib/aarch64-linux-android/21/crtend_so.o
ld.lld: error: duplicate symbol: __unw_init_local
>>> defined at libunwind.cpp:44 (/__w/1/s/src/runtime/src/native/external/llvm-libunwind/src/libunwind.cpp:44)
>>>            libunwind.cpp.o:(__unw_init_local) in archive ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libRuntime.WorkstationGC.a
>>> defined at libunwind.cpp
>>>            libunwind.cpp.o:(.text.__unw_init_local+0x0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/aarch64/libunwind.a

ld.lld: error: duplicate symbol: libunwind::LocalAddressSpace::sThisAddressSpace
>>> defined at libunwind.cpp
>>>            libunwind.cpp.o:(libunwind::LocalAddressSpace::sThisAddressSpace) in archive ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libRuntime.WorkstationGC.a
>>> defined at libunwind.cpp
>>>            libunwind.cpp.o:(.bss._ZN9libunwind17LocalAddressSpace17sThisAddressSpaceE+0x0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/aarch64/libunwind.a

ld.lld: error: duplicate symbol: __unw_get_reg
>>> defined at libunwind.cpp:99 (/__w/1/s/src/runtime/src/native/external/llvm-libunwind/src/libunwind.cpp:99)
>>>            libunwind.cpp.o:(__unw_get_reg) in archive ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libRuntime.WorkstationGC.a
>>> defined at libunwind.cpp
>>>            libunwind.cpp.o:(.text.__unw_get_reg+0x0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/aarch64/libunwind.a

ld.lld: error: duplicate symbol: __unw_set_reg
>>> defined at libunwind.cpp:114 (/__w/1/s/src/runtime/src/native/external/llvm-libunwind/src/libunwind.cpp:114)
>>>            libunwind.cpp.o:(__unw_set_reg) in archive ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libRuntime.WorkstationGC.a
>>> defined at libunwind.cpp
>>>            libunwind.cpp.o:(.text.__unw_set_reg+0x0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/aarch64/libunwind.a

ld.lld: error: duplicate symbol: __unw_get_fpreg
>>> defined at libunwind.cpp:146 (/__w/1/s/src/runtime/src/native/external/llvm-libunwind/src/libunwind.cpp:146)
>>>            libunwind.cpp.o:(__unw_get_fpreg) in archive ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libRuntime.WorkstationGC.a
>>> defined at libunwind.cpp
>>>            libunwind.cpp.o:(.text.__unw_get_fpreg+0x0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/aarch64/libunwind.a

ld.lld: error: duplicate symbol: __unw_set_fpreg
>>> defined at libunwind.cpp:161 (/__w/1/s/src/runtime/src/native/external/llvm-libunwind/src/libunwind.cpp:161)
>>>            libunwind.cpp.o:(__unw_set_fpreg) in archive ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libRuntime.WorkstationGC.a
>>> defined at libunwind.cpp
>>>            libunwind.cpp.o:(.text.__unw_set_fpreg+0x0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/aarch64/libunwind.a

ld.lld: error: duplicate symbol: __unw_step
>>> defined at libunwind.cpp:197 (/__w/1/s/src/runtime/src/native/external/llvm-libunwind/src/libunwind.cpp:197)
>>>            libunwind.cpp.o:(__unw_step) in archive ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libRuntime.WorkstationGC.a
>>> defined at libunwind.cpp
>>>            libunwind.cpp.o:(.text.__unw_step+0x0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/aarch64/libunwind.a

ld.lld: error: duplicate symbol: __unw_step_stage2
>>> defined at libunwind.cpp:207 (/__w/1/s/src/runtime/src/native/external/llvm-libunwind/src/libunwind.cpp:207)
>>>            libunwind.cpp.o:(__unw_step_stage2) in archive ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libRuntime.WorkstationGC.a
>>> defined at libunwind.cpp
>>>            libunwind.cpp.o:(.text.__unw_step_stage2+0x0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/aarch64/libunwind.a

ld.lld: error: duplicate symbol: __unw_get_proc_info
>>> defined at libunwind.cpp:212 (/__w/1/s/src/runtime/src/native/external/llvm-libunwind/src/libunwind.cpp:212)
>>>            libunwind.cpp.o:(__unw_get_proc_info) in archive ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libRuntime.WorkstationGC.a
>>> defined at libunwind.cpp
>>>            libunwind.cpp.o:(.text.__unw_get_proc_info+0x0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/aarch64/libunwind.a

ld.lld: error: duplicate symbol: __unw_resume
>>> defined at libunwind.cpp:224 (/__w/1/s/src/runtime/src/native/external/llvm-libunwind/src/libunwind.cpp:224)
>>>            libunwind.cpp.o:(__unw_resume) in archive ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libRuntime.WorkstationGC.a
>>> defined at libunwind.cpp
>>>            libunwind.cpp.o:(.text.__unw_resume+0x0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/aarch64/libunwind.a

ld.lld: error: duplicate symbol: __unw_get_proc_name
>>> defined at libunwind.cpp:238 (/__w/1/s/src/runtime/src/native/external/llvm-libunwind/src/libunwind.cpp:238)
>>>            libunwind.cpp.o:(__unw_get_proc_name) in archive ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libRuntime.WorkstationGC.a
>>> defined at libunwind.cpp
>>>            libunwind.cpp.o:(.text.__unw_get_proc_name+0x0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/aarch64/libunwind.a

ld.lld: error: duplicate symbol: __unw_is_fpreg
>>> defined at libunwind.cpp:251 (/__w/1/s/src/runtime/src/native/external/llvm-libunwind/src/libunwind.cpp:251)
>>>            libunwind.cpp.o:(__unw_is_fpreg) in archive ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libRuntime.WorkstationGC.a
>>> defined at libunwind.cpp
>>>            libunwind.cpp.o:(.text.__unw_is_fpreg+0x0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/aarch64/libunwind.a

ld.lld: error: duplicate symbol: __unw_regname
>>> defined at libunwind.cpp:265 (/__w/1/s/src/runtime/src/native/external/llvm-libunwind/src/libunwind.cpp:265)
>>>            libunwind.cpp.o:(__unw_regname) in archive ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libRuntime.WorkstationGC.a
>>> defined at libunwind.cpp
>>>            libunwind.cpp.o:(.text.__unw_regname+0x0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/aarch64/libunwind.a

ld.lld: error: duplicate symbol: __unw_is_signal_frame
>>> defined at libunwind.cpp:270 (/__w/1/s/src/runtime/src/native/external/llvm-libunwind/src/libunwind.cpp:270)
>>>            libunwind.cpp.o:(__unw_is_signal_frame) in archive ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libRuntime.WorkstationGC.a
>>> defined at libunwind.cpp
>>>            libunwind.cpp.o:(.text.__unw_is_signal_frame+0x0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/aarch64/libunwind.a

ld.lld: error: duplicate symbol: __unw_iterate_dwarf_unwind_cache
>>> defined at libunwind.cpp:303 (/__w/1/s/src/runtime/src/native/external/llvm-libunwind/src/libunwind.cpp:303)
>>>            libunwind.cpp.o:(__unw_iterate_dwarf_unwind_cache) in archive ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libRuntime.WorkstationGC.a
>>> defined at libunwind.cpp
>>>            libunwind.cpp.o:(.text.__unw_iterate_dwarf_unwind_cache+0x0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/aarch64/libunwind.a

ld.lld: error: duplicate symbol: __unw_add_dynamic_fde
>>> defined at libunwind.cpp:312 (/__w/1/s/src/runtime/src/native/external/llvm-libunwind/src/libunwind.cpp:312)
>>>            libunwind.cpp.o:(__unw_add_dynamic_fde) in archive ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libRuntime.WorkstationGC.a
>>> defined at libunwind.cpp
>>>            libunwind.cpp.o:(.text.__unw_add_dynamic_fde+0x0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/aarch64/libunwind.a

ld.lld: error: duplicate symbol: __unw_remove_dynamic_fde
>>> defined at libunwind.cpp:331 (/__w/1/s/src/runtime/src/native/external/llvm-libunwind/src/libunwind.cpp:331)
>>>            libunwind.cpp.o:(__unw_remove_dynamic_fde) in archive ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libRuntime.WorkstationGC.a
>>> defined at libunwind.cpp
>>>            libunwind.cpp.o:(.text.__unw_remove_dynamic_fde+0x0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/aarch64/libunwind.a

ld.lld: error: duplicate symbol: __unw_add_dynamic_eh_frame_section
>>> defined at libunwind.cpp:336 (/__w/1/s/src/runtime/src/native/external/llvm-libunwind/src/libunwind.cpp:336)
>>>            libunwind.cpp.o:(__unw_add_dynamic_eh_frame_section) in archive ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libRuntime.WorkstationGC.a
>>> defined at libunwind.cpp
>>>            libunwind.cpp.o:(.text.__unw_add_dynamic_eh_frame_section+0x0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/aarch64/libunwind.a

ld.lld: error: duplicate symbol: __unw_remove_dynamic_eh_frame_section
>>> defined at libunwind.cpp:358 (/__w/1/s/src/runtime/src/native/external/llvm-libunwind/src/libunwind.cpp:358)
>>>            libunwind.cpp.o:(__unw_remove_dynamic_eh_frame_section) in archive ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libRuntime.WorkstationGC.a
>>> defined at libunwind.cpp
>>>            libunwind.cpp.o:(.text.__unw_remove_dynamic_eh_frame_section+0x0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/aarch64/libunwind.a

ld.lld: error: duplicate symbol: unw_local_addr_space
>>> defined at libunwind.cpp:38 (/__w/1/s/src/runtime/src/native/external/llvm-libunwind/src/libunwind.cpp:38)
>>>            libunwind.cpp.o:(unw_local_addr_space) in archive ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libRuntime.WorkstationGC.a
>>> defined at libunwind.cpp
>>>            libunwind.cpp.o:(.data.unw_local_addr_space+0x0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/aarch64/libunwind.a

I hoped that passing -unwindlib=none would make linking work, but unfortunately, it causes the following error:

Android (13989888, +pgo, +bolt, +lto, +mlgo, based on r563880c) clang version 21.0.0 (https://android.googlesource.com/toolchain/llvm-project 5e96669f06077099aa41290cdb4c5e6fa0f59349)
Target: aarch64-unknown-linux-android21
Thread model: posix
InstalledDir: ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin
 "~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/ld.lld" -EL
 --fix-cortex-a53-843419 -z now -z relro -z max-page-size=16384 --no-rosegment --hash-style=both
 --eh-frame-hdr -m aarch64linux -shared -o
 bin/Release/net10.0-android/android-arm64/native/NativeAOT.so
 ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/../sysroot/usr/lib/aarch64-linux-android/21/crtbegin_so.o
 -L~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/aarch64
 -L~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/../sysroot/usr/lib/aarch64-linux-android/21
 -L~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/../sysroot/usr/lib/aarch64-linux-android
 -L~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/../sysroot/usr/lib
 --compress-debug-sections=zlib obj/Release/net10.0-android/android-arm64/native/NativeAOT.o
 --version-script=obj/Release/net10.0-android/android-arm64/native/NativeAOT.exports
 --export-dynamic
 ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libSystem.Native.a
 ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libSystem.Globalization.Native.a
 ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libSystem.IO.Compression.Native.a
 ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libSystem.Security.Cryptography.Native.Android.a
 ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libbootstrapperdll.o
 ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libRuntime.WorkstationGC.a
 ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libeventpipe-disabled.a
 ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libstandalonegc-disabled.a
 ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libaotminipal.a
 ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libbrotlienc.a
 ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libbrotlidec.a
 ~/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libbrotlicommon.a
 --build-id=sha1 --as-needed -e 0x0 -ldl -lz -llog -lm -z relro -z now --eh-frame-hdr -z
 max-page-size=16384 -soname libNativeAOT.so --error-unresolved-symbols --no-undefined
 ~/vc/xamarin/xamarin-android-worktrees/ndk-r29/bin/Debug/lib/packs/Microsoft.Android.Runtime.NativeAOT.36.android-arm64/36.1.99/runtimes/android-arm64/native/libnaot-android.release-static-release.a
 ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_static.a
 ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++abi.a
 obj/Release/net10.0-android/android-arm64/android/jni_init_funcs.arm64-v8a.o
 obj/Release/net10.0-android/android-arm64/android/environment.arm64-v8a.o --discard-all
 --gc-sections -T obj/Release/net10.0-android/android-arm64/native/sections.ld
 ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/libclang_rt.builtins-aarch64-android.a
 -ldl -lc
 ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/libclang_rt.builtins-aarch64-android.a
 -ldl
 ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/../sysroot/usr/lib/aarch64-linux-android/21/crtend_so.o
ld.lld: error: undefined symbol: _Unwind_Resume
>>> referenced by stdexcept:0 (out/lib/device-libcxx-aarch64-ndk-cxx/include/c++/v1/stdexcept:0)
>>>               hash.cpp.o:(std::__ndk1::__throw_overflow_error[abi:ne210000](char const*)) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_static.a
>>> referenced by thread.cpp:0 (out/llvm-project/libcxx/src/thread.cpp:0)
>>>               thread.cpp.o:(std::__ndk1::__thread_local_data()) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_static.a
>>> referenced by call_once.cpp:0 (out/llvm-project/libcxx/src/call_once.cpp:0)
>>>               call_once.cpp.o:(std::__ndk1::__call_once(unsigned long volatile&, void*, void (*)(void*))) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_static.a
>>> referenced 198 more times

ld.lld: error: undefined symbol: _Unwind_RaiseException
>>> referenced by cxa_exception.cpp:295 (out/llvm-project/libcxxabi/src/cxa_exception.cpp:295)
>>>               cxa_exception.cpp.o:(__cxa_throw) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++abi.a

ld.lld: error: undefined symbol: _Unwind_SetGR
>>> referenced by cxa_personality.cpp:556 (out/llvm-project/libcxxabi/src/cxa_personality.cpp:556)
>>>               cxa_personality.cpp.o:(__gxx_personality_v0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++abi.a
>>> referenced by cxa_personality.cpp:558 (out/llvm-project/libcxxabi/src/cxa_personality.cpp:558)
>>>               cxa_personality.cpp.o:(__gxx_personality_v0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++abi.a
>>> referenced by cxa_personality.cpp:556 (out/llvm-project/libcxxabi/src/cxa_personality.cpp:556)
>>>               cxa_personality.cpp.o:(__gxx_personality_v0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++abi.a
>>> referenced 1 more times

ld.lld: error: undefined symbol: _Unwind_SetIP
>>> referenced by cxa_personality.cpp:560 (out/llvm-project/libcxxabi/src/cxa_personality.cpp:560)
>>>               cxa_personality.cpp.o:(__gxx_personality_v0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++abi.a
>>> referenced by cxa_personality.cpp:560 (out/llvm-project/libcxxabi/src/cxa_personality.cpp:560)
>>>               cxa_personality.cpp.o:(__gxx_personality_v0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++abi.a

ld.lld: error: undefined symbol: _Unwind_GetLanguageSpecificData
>>> referenced by cxa_personality.cpp:626 (out/llvm-project/libcxxabi/src/cxa_personality.cpp:626)
>>>               cxa_personality.cpp.o:(__gxx_personality_v0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++abi.a

ld.lld: error: undefined symbol: _Unwind_GetIP
>>> referenced by cxa_personality.cpp:641 (out/llvm-project/libcxxabi/src/cxa_personality.cpp:641)
>>>               cxa_personality.cpp.o:(__gxx_personality_v0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++abi.a

ld.lld: error: undefined symbol: _Unwind_GetRegionStart
>>> referenced by cxa_personality.cpp:644 (out/llvm-project/libcxxabi/src/cxa_personality.cpp:644)
>>>               cxa_personality.cpp.o:(__gxx_personality_v0) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++abi.a

ld.lld: error: undefined symbol: _Unwind_DeleteException
>>> referenced by cxa_exception.cpp:586 (out/llvm-project/libcxxabi/src/cxa_exception.cpp:586)
>>>               cxa_exception.cpp.o:(__cxa_end_catch) in archive ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++abi.a

The problem, I think, stems from the fact that libRuntime.WorkstationGC.a exports libunwind symbols where it should keep them hidden:

microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native$ nm -C libRuntime.WorkstationGC.a | grep __unw
0000000000000000 T __unw_add_dynamic_eh_frame_section
0000000000000000 T __unw_add_dynamic_fde
0000000000000000 T __unw_get_fpreg
0000000000000000 T __unw_get_proc_info
0000000000000000 T __unw_get_proc_name
0000000000000000 T __unw_get_reg
0000000000000000 T __unw_get_save_loc
0000000000000000 T __unw_init_local
0000000000000000 T __unw_is_fpreg
0000000000000000 T __unw_is_signal_frame
0000000000000000 T __unw_iterate_dwarf_unwind_cache
0000000000000000 T __unw_regname
0000000000000000 T __unw_remove_dynamic_eh_frame_section
0000000000000000 T __unw_remove_dynamic_fde
0000000000000000 T __unw_resume
0000000000000000 T __unw_set_fpreg
0000000000000000 T __unw_set_reg
0000000000000000 T __unw_step
0000000000000000 T __unw_step_stage2
0000000000000000 T __unw_getcontext

NDK r29 uses LLVM 21

Reproduction Steps

  1. Check out the https://github.com/dotnet/android/ dev/grendel/ndk-r29 branch locally (from this PR)
  2. Go to the checked-out directory and run:
    1. On Linux/macOS: make prepare all
    2. On Windows: build.cmd
  3. After the build completes, go to the samples/NativeAOT directory and run
    1. On Linux/macOS: ../../dotnet-local.sh build -c Release -bl
    2. On Windows: ..\..\dotnet-local.cmd build -c Release -bl

Expected behavior

Application should be linked without errors

Actual behavior

Application linking fails with errors

Regression?

No response

Known Workarounds

No known workarounds

Configuration

  • .NET version: 10.0.100
  • OS: not dependent on the operating system
  • Architecture: not dependent on the architecture
  • Specific to building a .NET for Android app using NativeAOT with NDK r29

Other information

No response

Activity

  1. added
    needs-area-labelAn area label is needed to ensure this gets routed to the appropriate area owners
    on Oct 29, 2025
  2. MichalStrehovsky commented on Oct 29, 2025

    @MichalStrehovsky
    Member

    This does look like we should be hiding symbols, but the issue doesn't repro with a simple hello world (I didn't try the sample in the Android repo, just dotnet new console followed by a dotnet publish -r linux-bionic-arm64 -p:DisableUnsupportedError=true -p:PublishAotUsingRuntimePack=true).

    I wonder if you'd be able to work around by getting these two lines out of the picture:

     ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_static.a
     ~/android-toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++abi.a
    

    The native AOT runtime itself doesn't depend on the C++ stdlib. Does dotnet/android bring the dependency, or is the inclusion of this just accidental? The clang invocation for the above console app I tried doesn't end up with libc++.

  3. added and removed
    needs-area-labelAn area label is needed to ensure this gets routed to the appropriate area owners
    on Oct 29, 2025
  4. added this to the 11.0.0 milestone on Oct 29, 2025
  5. dotnet-policy-service commented on Oct 29, 2025

    @dotnet-policy-service
    Contributor

    Tagging subscribers to 'arch-android': @vitek-karas, @simonrozsival, @steveisok, @akoeplinger
    See info in area-owners.md if you want to be subscribed.

  6. dotnet-policy-service commented on Oct 29, 2025

    @dotnet-policy-service
    Contributor

    Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas
    See info in area-owners.md if you want to be subscribed.

  7. grendello commented on Oct 29, 2025

    @grendello
    ContributorAuthor

    @MichalStrehovsky Android runtime needs libc++, yes, can't remove the dependency alas.

  8. grendello commented on Oct 29, 2025

    @grendello
    ContributorAuthor

    @MichalStrehovsky note that the build works with NDK r28c which uses an older version of LLVM, so it's a (kind of) regression.

  9. MichalStrehovsky commented on Oct 30, 2025

    @MichalStrehovsky
    Member

    I looked at the duplicate symbols and they are already HIDDEN. I'm not sure if there's a way to solve this besides prefixing all symbols in our copy of libunwind. Looks like all symbols share the same "namespace" and it's not possible to make symbols local to the static library. They're either local to object file, or pollute the global namespace.

    As a workaround, linking works with libc++_shared.so.

    @MichalStrehovsky note that the build works with NDK r28c which uses an older version of LLVM, so it's a (kind of) regression.

    Looks like NDK r29 newly comes with libunwind.h so we might be able to use that instead of our own copy (NDK 27c doesn't have it) and then we'd avoid the duplicate. I'm not sure how much fragility would that introduce though (if end users build with a different NDK than we did). It probably won't work.

    @MichalStrehovsky Android runtime needs libc++, yes, can't remove the dependency alas.

    When I replace libc++_static.a in the clang invocation with the libstdc++compat.a shim that comes with the native AOT runtime, I see about 40 undefined symbols, all of them from libnaot-android.release-static-release.a that we control. We should consider applying #76705 treatment to that code. We'd likely get size wins, especially with a statically linked libc++. We even saw a tiny size win with a dynamically linked libc++ and that one was a bit surprising. This would likely work around the problem too since libc++ is the one that depends on libunwind.

  10. 3 remaining items

  11. MichalStrehovsky commented on Oct 30, 2025

    @MichalStrehovsky
    Member

    As for removing libc++ dependency, I'm sorry, but no... We spent quite some time to add it (in order to take advantage of some features) since CoreCLR uses libc++ anyway. Going this route would mean undoing a lot of code, and we would still end up with libc++ when CoreCLR is used. I will see if I can make our NativeAOT runtime independent on libc++, but I don't want it to diverge too much from the other two, since that means more maintenance burden.

    We're in the same situation with native AOT runtime (native AOT is a subset of the CoreCLR VM). We share a lot of code with the full runtime. Full runtime uses libc++. Native AOT doesn't.

    Ballpark estimate (comparing size with libc++_static vs libc++_shared) is that statically linking libc++ adds 0.5 MB to the Android NAOT sample. This is currently 10% of the sample. But NAOT sample is currently deoptimized when it comes to size due to https://github.com/dotnet/android/blob/ea05d7630612a6fc4d456f3278f3846a806c144c/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets#L113-L114 (I discussed that with the Johnatans on Discord last year). Once that's fixed, I expect NAOT sample to get even smaller (would not be surprised if it's 10s of percent smaller, depending on app). So the question will be "How much work are we willing to do for a 10% saving?".

    libc++ is just sparingly used with PublishAot. If we are linking something which requires C++ runtime features (like libicu with <InvariantGlobalization>false), libstdc++/libc++ gets linked explicitly or transitively.

    We'd have to statically link it. For libicu specifically, the NDK only has libicu.so, so I think we're good there.

  12. sbomer commented on Dec 16, 2025

    @sbomer
    Member

    @grendello are you still able to repro this? I am not hitting the error following your instructions.

  13. sbomer commented on Jan 5, 2026

    @sbomer
    Member

    I was able to repro this on a fresh vm. I have a workaround that localizes these libunwind symbols, but then I get a similar failure:

    Restore complete (0.2s)
      NativeAOT net10.0-android android-arm64 failed with 6 error(s) (0.3s)
        ld.lld : error duplicate symbol: RhCollect
        ld.lld : error duplicate symbol: RhGetGcTotalMemory
        ld.lld : error duplicate symbol: RhStartNoGCRegion
        ld.lld : error duplicate symbol: RhEndNoGCRegion
        clang : error linker command failed with exit code 1 (use -v to see invocation)
        /home/sven/.nuget/packages/microsoft.dotnet.ilcompiler/10.0.0/build/Microsoft.NETCore.Native.targets(389,5): error MSB3073: The command ""clang" "obj/Release/net10.0-android/android-arm64/native/NativeAOT.o" -o "bin/Release/net10.0-android/android-arm64/native/NativeAOT.so" -Wl,--version-script="obj/Release/net10.0-android/android-arm64/native/NativeAOT.exports" -Wl,--export-dynamic -gz=zlib -fuse-ld=lld /home/sven/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libSystem.Native.a /home/sven/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libSystem.Globalization.Native.a /home/sven/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libSystem.IO.Compression.Native.a /home/sven/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libSystem.Security.Cryptography.Native.Android.a /home/sven/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libbootstrapperdll.o /home/sven/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libRuntime.WorkstationGC.a /home/sven/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libeventpipe-disabled.a /home/sven/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libstandalonegc-disabled.a /home/sven/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libaotminipal.a /home/sven/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libbrotlienc.a /home/sven/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libbrotlidec.a /home/sven/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.android-arm64/10.0.0/runtimes/android-arm64/native/libbrotlicommon.a --target=aarch64-linux-android21 -g -Wl,--build-id=sha1 -Wl,--as-needed -Wl,-e,0x0 -pthread -ldl -lz -llog -lm -shared -Wl,-z,relro -Wl,-z,now -Wl,--eh-frame-hdr -Wl,-z,max-page-size=16384 "-Wl,-soname,libNativeAOT.so" -Wl,--error-unresolved-symbols -Wl,--no-undefined "/home/sven/android/bin/Debug/lib/packs/Microsoft.Android.Runtime.NativeAOT.36.android-arm64/36.1.99/runtimes/android-arm64/native/libnaot-android.release-static-release.a" "/home/sven/maui-android-native/android-sdk/ndk/29.0.14206865/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_static.a" "/home/sven/maui-android-native/android-sdk/ndk/29.0.14206865/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++abi.a" obj/Release/net10.0-android/android-arm64/android/jni_init_funcs.arm64-v8a.o obj/Release/net10.0-android/android-arm64/android/environment.arm64-v8a.o -Wl,--discard-all -Wl,--gc-sections -Wl,-T,"obj/Release/net10.0-android/android-arm64/native/sections.ld"" exited with code 1.
      NativeAOT net10.0-android failed with 1 warning(s) (0.2s) → bin/Release/net10.0-android/NativeAOT.dll
        /home/sven/android/bin/Debug/lib/packs/Microsoft.Android.Sdk.Linux/36.1.99/tools/Xamarin.Android.Common.targets(570,3): warning XA1040: The NativeAOT runtime on Android is an experimental feature and not yet suitable for production use. File issues at: https://github.com/dotnet/android/issues
    
    Build failed with 6 error(s) and 1 warning(s) in 1.3s
    

    which seems to be caused by the compiler emitting some symbol definitions for these into the application object file (and these conflict with the definitions in libRuntime.WorkStationGC.a):

    > nm obj/Release/net10.0-android/android-arm64/native/NativeAOT.o | grep -E "(RhCollect|RhGetGcTotalMemory|RhStartNoGCRegion|RhEndNoGCRegion)"
    00000000000e5300 T RhCollect
    00000000000e5460 T RhEndNoGCRegion
    00000000000e5370 T RhGetGcTotalMemory
    00000000000e53d0 T RhStartNoGCRegion
    00000000000e5300 T S_P_CoreLib_System_Runtime_InternalCalls__RhCollect
    00000000000e5460 T S_P_CoreLib_System_Runtime_InternalCalls__RhEndNoGCRegion
    00000000000e5370 T S_P_CoreLib_System_Runtime_InternalCalls__RhGetGcTotalMemory
    00000000000e53d0 T S_P_CoreLib_System_Runtime_InternalCalls__RhStartNoGCRegion
    000000000001336f R _lsda0S_P_CoreLib_System_Runtime_InternalCalls__RhStartNoGCRegion
    

    Is that expected @MichalStrehovsky? It looks like a bug to me.

  14. MichalStrehovsky commented on Jan 7, 2026

    @MichalStrehovsky
    Member

    which seems to be caused by the compiler emitting some symbol definitions for these into the application object file (and these conflict with the definitions in libRuntime.WorkStationGC.a):

    Is it possible you're using CoreLib from .NET 10, and libRuntime.WorkStationGC.a from .NET 11?

    RhCollect is implemented in managed code in .NET 10 and part of NativeAOT.o, but it's in native code in .NET 11 and part of libRuntime.WorkStationGC.a.

    (I don't see a definition for the symbol in the object files generated from ILC in main, but do see them for .NET 10. Turns out the reason is #118970.)

  15. sbomer commented on Jan 7, 2026

    @sbomer
    Member

    Thanks @MichalStrehovsky, that was the issue. After ensuring all bits come from .NET 11, I no longer get that failure. My workaround was using objcopy --localize-hidden, but that now hits undefined symbols (logDWARF is extern, so --localize-hidden breaks the extern reference at link time). I can work around this by localizing individual symbols with --localize-symbols: https://github.com/dotnet/runtime/compare/main...sbomer:runtime:localizeLibunwind?expand=1

    The increasingly fragile workarounds reflect the fact that libunwind was not meant to be statically linked multiple times into the same binary. I don't think we should go this route - the fix should be to remove the libc++ dependency from the android nativeaot runtime.

  16. sbomer commented on May 19, 2026

    @sbomer
    Member

    While android is working on removing the libc++ dependency, I now think we should also prevent the conflict on our side. We're not really using libunwind as an unwinder - we're really just using its internals for limited DWARF debug info parsing. Ideally this dependency wouldn't block the final application link from including a libc++ dependency.

    I explored a few approaches:

    1. --localize-hidden on the entire libRuntime.WorkstationGC.a archive - this localizes too much (including symbols that need to be global at application link time.
    2. --localize-symbols on a specific set of symbols (see earlier comment) - works, but is fragile.
    3. --localize-hidden on the libunwind object before it goes into the runtime archive - initially I hit some problems with the logDWARF symbol, but that's not present in release builds. I think this should work to fix the android problem.

    I think we should do 3) for now. We might also be able to reduce the number of conflicts by removing some dead code that's in the runtime copy of libunwind but not actually used at link time. But I don't think that gets rid of all of the symbols, so we would still need some other workaround.

    We also talked about rewriting this dependency in managed code. I intend to keep exploring that in a follow-up but don't want to block on that.

  17. sbomer commented on May 20, 2026

    @sbomer
    Member

    Nope, still doesn't work - the problem is that some symbols need to be global so they can be referenced from runtime code, but are also provided by the NDK's libunwind. I think the ELF linking model fundamentally doesn't support this (a runtime static archive with cross-object references to symbols that are also provided by a separate library at application link time).

    So I believe we can only fix this on our side by introducing a separate namespace for the symbols or avoiding the dependency entirely.

  18. am11 commented on May 21, 2026

    @am11
    Member

    Alternatively, we could align NativeAOT with the coreclr/pal plan in #128422 by adopting HP-libunwind and removing llvm-libunwind. Since @jkotas previously suggested converging these paths, using this issue as a precursor makes it a great time to do so.

  19. reopened this on Jun 2, 2026
  20. locked and limited conversation to collaborators on Jul 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

  • Status
    No status

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions