zig icon indicating copy to clipboard operation
zig copied to clipboard

zig c++ fails to compile for x86_64-linux-gnu: "Undefined symbol: __cxa_thread_atexit_impl"

Open TheSpydog opened this issue 4 years ago • 8 comments
trafficstars

I'm attempting to build a .NET NativeAOT project using zig c++ -target x86_linux-gnu as the system compiler/linker, so that it can build with a low glibc (2.17) instead of my Linux machine's actual libc. So far it seems to be working surprisingly well, but there is one build error that I'm not sure how to properly fix:

ld.lld : error : undefined symbol: __cxa_thread_atexit_impl [/home/calebcornett/dev/flotilla/SpaceShooter/Flotilla.SDL2.Core.csproj]
  >>> referenced by cxa_thread_atexit.cpp
  >>>               /home/calebcornett/.cache/zig/o/9ce5e078b5e6082394b4d462fd77ab1c/cxa_thread_atexit.o:(__cxa_thread_atexit) in archive /home/calebcornett/.cache/zig/o/71710f341d4f94a1eba2a0e167fafa1e/libc++abi.a
  >>> did you mean: __cxa_thread_atexit_impl@GLIBC_2.18
  >>> defined in: /home/calebcornett/.cache/zig/o/fdbd9d71c34a562c7053a91a4cec8d55/libc.so.6

I was able to work around this by #undef'ing HAVE___CXA_THREAD_ATEXIT_IMPL in cxa_thread_atexit.cpp and then removing lines 115-117, but that seems like a terrible hack.

I'm not sure why HAVE__CXA_THREAD_ATEXIT_IMPL is getting defined in the first place, since the targeted glibc doesn't support it. But even if it's not defined, it still tries to use the dynamic __weak__ linkage for the function, which is (apparently?) not supported by clang, so it fails anyway.

TheSpydog avatar Jul 18 '21 17:07 TheSpydog

set your target to be x86_64-linux-gnu.2.18

nektro avatar Jul 18 '21 23:07 nektro

set your target to be x86_64-linux-gnu.2.18

I am intentionally targeting 2.17. This project builds (without zig) on CentOS 7, which has glibc 2.17. The main reason I am interested in compiling with zig in the first place is to avoid having to install a CentOS VM for building this project.

TheSpydog avatar Jul 19 '21 00:07 TheSpydog

then you have to backport the function from glibc 2.18

  >>> did you mean: __cxa_thread_atexit_impl@GLIBC_2.18
  >>> defined in: /home/calebcornett/.cache/zig/o/fdbd9d71c34a562c7053a91a4cec8d55/libc.so.6

https://sourceware.org/git/?p=glibc.git;a=blob;f=stdlib/cxa_thread_atexit_impl.c;h=577ed30931d95a42b5cec67050f90b42e2aecb71;hb=HEAD

nektro avatar Jul 19 '21 01:07 nektro

My understanding is that libcxxabi is supposed to polyfill this function for lower glibc versions, by having a backup implementation in case no actual __cxa_thread_atexit_impl function is found. But the polyfill seems to assume that the __weak__ attribute here allows this function to link dynamically at runtime (based on the null check here), which Clang apparently does not support. So instead of checking for the function's existence when the app is run, it checks during the linking process instead and always fails as a result.

Backporting the function doesn't seem like it should be necessary if the polyfill could work as expected.

TheSpydog avatar Jul 19 '21 01:07 TheSpydog

https://bugs.chromium.org/p/chromium/issues/detail?id=749077 seems very related

howardjohn avatar Jul 24 '21 02:07 howardjohn

I ran into the same issue. In my case I didn't really care what version of glibc was used, I just want it to work. I was cross compiling amd64 -> arm64, so it was defaulting to glibc 2.17. When I explicitly used linux-gnu.2.18 target, it worked fine.

I realize the original issue wants to use glibc 2.17 explicitly, just noting that it can be worked around by explicitly using 2.18 (or higher, I assume?)

howardjohn avatar Jul 26 '21 18:07 howardjohn

I got the same issue, albeit with the musl target:

zig c++ -target x86_64-linux-musl -static -lc++abi test.cc -o test
ld.lld: error: undefined symbol: __cxa_thread_atexit_impl
>>> referenced by cxa_thread_atexit.cpp
>>>               /home/felix/.cache/zig/o/e64c2abe2575938d980d5fd24527ffd4/cxa_thread_atexit.o:(__cxa_thread_atexit) in archive /home/felix/.cache/zig/o/6ea5fd27b3e32b43954dac6a856968cb/libc++abi.a

Minimal example to reproduce:

#include <thread>
#include <iostream>

struct yeah { ~yeah() { std::cout << "destruct thread local\n"; } };
thread_local yeah x;
int main() { std::thread{ []() { std::cout << "hello\n"; } }.join(); }

The above solution does work for glibc. However, is there also a solution for the musl target?

felixguendling avatar Aug 09 '21 16:08 felixguendling

I've noticed that the build of libcxxabi on my system always reports LIBCXXABI_HAS_CXA_THREAD_ATEXIT_IMPL:INTERNAL=1 even though this isn't present. The cmake --debug-trycompile option (of the libcxxabi build) shows it produces an archive file which nm reports an U (undefined) reference to __cxa_thread_atexit_impl, but it succeeds in creating this file, and check_library_exists then thinks the symbol is available and subsequently sets HAVE___CXA_THREAD_ATEXIT_IMPL. https://github.com/llvm/llvm-project/blob/d3f5f330671e718a0e28598c412d09e9a3b54273/libcxxabi/cmake/config-ix.cmake#L102 I conclude the detection logic is broken, it looks like check_library_exists may be the wrong cmake function for this.

pwaller avatar Oct 21 '22 21:10 pwaller