llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
29.25k stars 12.07k forks source link

libc++.dylib crash on MacOS when using LIBCXX_ENABLE_STATIC_ABI_LIBRARY=ON #111570

Open hlewin opened 1 month ago

hlewin commented 1 month ago

Hello! I think I have run into this problem: https://github.com/llvm/llvm-project/issues/109782

The libc++ generated by these settings crashes on load.

Source: llvm-19.1.0 release

> uname -a
Darwin BYO-MBP-hlewin 23.6.0 Darwin Kernel Version 23.6.0: Wed Jul 31 20:49:46 PDT 2024; root:xnu-10063.141.1.700.5~1/RELEASE_ARM64_T8103 arm64
cmake 
        -DCMAKE_INSTALL_PREFIX=$HOME/llvm-inst  \
        -DCMAKE_MACOSX_RPATH=ON \
        -DCMAKE_BUILD_TYPE=RelWithDebInfo \
        -G Ninja \
        -DLLVM_ENABLE_PROJECTS=clang \
        -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind" \
        -DLLVM_TARGETS_TO_BUILD=Native \
        -DLIBCXXABI_USE_LLVM_UNWINDER=ON \
        -DLIBUNWIND_ENABLE_SHARED=OFF \
        -DLIBCXXABI_ENABLE_STATIC_UNWINDER=ON \
        -DLIBCXXABI_ENABLE_SHARED=OFF \
        -DLIBCXX_ENABLE_STATIC_ABI_LIBRARY=ON \
       <path to llvm 19.1.0>
> ninja runtimes    # Compile runtimes
> lldb $HOME/load lib/libc++.dylib   # try to load the resulting libc++

(lldb) target create "/Users/hlewin/load"
Current executable set to '/Users/hlewin/load' (arm64).
(lldb) settings set -- target.run-args  "lib/libc++.dylib"
(lldb) run
Process 76725 launched: '/Users/hlewin/load' (arm64)
Process 76725 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
    frame #0: 0x0000000000000000
error: memory read failed for 0x0
Target 0: (load) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
  * frame #0: 0x0000000000000000
    frame #1: 0x00000001001e3c74 libc++.1.0.dylib`std::__1::__stdinbuf<char>::imbue(std::__1::locale const&) [inlined] std::__1::codecvt<char, char, __mbstate_t>::encoding[abi:ne190100](this=<unavailable>) const at __locale:781:65 [opt]
    frame #2: 0x00000001001e3c68 libc++.1.0.dylib`std::__1::__stdinbuf<char>::imbue(this=0x0000000100262a38, __loc=<unavailable>) at std_stream.h:86:29 [opt]
    frame #3: 0x00000001001e36b4 libc++.1.0.dylib`std::__1::DoIOSInit::DoIOSInit() [inlined] std::__1::__stdinbuf<char>::__stdinbuf(this=0x0000000100262a38, __fp=0x00000001fc4a4318, __st=<unavailable>) at std_stream.h:74:3 [opt]
    frame #4: 0x00000001001e365c libc++.1.0.dylib`std::__1::DoIOSInit::DoIOSInit() [inlined] std::__1::__stdinbuf<char>::__stdinbuf(this=0x0000000100262a38, __fp=0x00000001fc4a4318, __st=<unavailable>) at std_stream.h:73:105 [opt]
    frame #5: 0x00000001001e365c libc++.1.0.dylib`std::__1::DoIOSInit::DoIOSInit(this=0x0000000100262f88) at iostream.cpp:131:57 [opt]
    frame #6: 0x00000001001e4c0c libc++.1.0.dylib`_GLOBAL__I_000100 + 68
    frame #7: 0x00000001942d6608 dyld`invocation function for block in dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const::$_0::operator()() const + 168
    frame #8: 0x0000000194314d6c dyld`invocation function for block in dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 340
    frame #9: 0x0000000194308400 dyld`invocation function for block in dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 496
    frame #10: 0x00000001942b72fc dyld`dyld3::MachOFile::forEachLoadCommand(Diagnostics&, void (load_command const*, bool&) block_pointer) const + 300
    frame #11: 0x0000000194307394 dyld`dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 192
    frame #12: 0x0000000194314880 dyld`dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 516
    frame #13: 0x00000001942d253c dyld`dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const + 564
    frame #14: 0x00000001942d8bc0 dyld`dyld4::JustInTimeLoader::runInitializers(dyld4::RuntimeState&) const + 36
    frame #15: 0x00000001942d2984 dyld`dyld4::Loader::runInitializersBottomUp(dyld4::RuntimeState&, dyld3::Array<dyld4::Loader const*>&) const + 220
    frame #16: 0x00000001942d6698 dyld`dyld4::Loader::runInitializersBottomUpPlusUpwardLinks(dyld4::RuntimeState&) const::$_1::operator()() const + 116
    frame #17: 0x00000001942d2b78 dyld`dyld4::Loader::runInitializersBottomUpPlusUpwardLinks(dyld4::RuntimeState&) const + 420
    frame #18: 0x00000001942eed30 dyld`dyld4::APIs::dlopen_from(char const*, int, void*) + 1876
    frame #19: 0x0000000100003ed8 load`main + 120
    frame #20: 0x00000001942bb154 dyld`start + 2476
(lldb) 
load.c

#include <dlfcn.h>
#include <stdio.h>

int
main(int argc, const char *argv[])
{
    if (argc != 2) {
        fprintf(stderr, "usage: %s dynamic-library\n", argv[0]);
        return 1;
    }
    const char *lib = argv[1];
    int failed = dlopen(lib, RTLD_LOCAL | RTLD_LAZY) == 0;
    printf("dlopen %s: %s\n", lib, failed ? "failed" : "succeeded");
    return failed;
}

Without using a static libc++abi the error seems to disappear.

hlewin commented 1 month ago

I have just confirmed the issue still persists in llvm-19.1.1

error: memory read failed for 0x0
Target 0: (load) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
  * frame #0: 0x0000000000000000
    frame #1: 0x00000001001e3de4 libc++.1.0.dylib`std::__1::__stdinbuf<char>::imbue(std::__1::locale const&) [inlined] std::__1::codecvt<char, char, __mbstate_t>::encoding[abi:ne190101](this=<unavailable>) const at __locale:781:65 [opt]
    frame #2: 0x00000001001e3dd8 libc++.1.0.dylib`std::__1::__stdinbuf<char>::imbue(this=0x0000000100262a38, __loc=<unavailable>) at std_stream.h:86:29 [opt]
    frame #3: 0x00000001001e3824 libc++.1.0.dylib`std::__1::DoIOSInit::DoIOSInit() [inlined] std::__1::__stdinbuf<char>::__stdinbuf(this=0x0000000100262a38, __fp=0x00000001fc4a4318, __st=<unavailable>) at std_stream.h:74:3 [opt]
    frame #4: 0x00000001001e37cc libc++.1.0.dylib`std::__1::DoIOSInit::DoIOSInit() [inlined] std::__1::__stdinbuf<char>::__stdinbuf(this=0x0000000100262a38, __fp=0x00000001fc4a4318, __st=<unavailable>) at std_stream.h:73:105 [opt]
    frame #5: 0x00000001001e37cc libc++.1.0.dylib`std::__1::DoIOSInit::DoIOSInit(this=0x0000000100262f88) at iostream.cpp:131:57 [opt]
    frame #6: 0x00000001001e4d7c libc++.1.0.dylib`_GLOBAL__I_000100 + 68
    frame #7: 0x00000001942d6608 dyld`invocation function for block in dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const::$_0::operator()() const + 168
    frame #8: 0x0000000194314d6c dyld`invocation function for block in dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 340
    frame #9: 0x0000000194308400 dyld`invocation function for block in dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 496
    frame #10: 0x00000001942b72fc dyld`dyld3::MachOFile::forEachLoadCommand(Diagnostics&, void (load_command const*, bool&) block_pointer) const + 300
    frame #11: 0x0000000194307394 dyld`dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 192
    frame #12: 0x0000000194314880 dyld`dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 516
    frame #13: 0x00000001942d253c dyld`dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const + 564
    frame #14: 0x00000001942d8bc0 dyld`dyld4::JustInTimeLoader::runInitializers(dyld4::RuntimeState&) const + 36
    frame #15: 0x00000001942d2984 dyld`dyld4::Loader::runInitializersBottomUp(dyld4::RuntimeState&, dyld3::Array<dyld4::Loader const*>&) const + 220
    frame #16: 0x00000001942d6698 dyld`dyld4::Loader::runInitializersBottomUpPlusUpwardLinks(dyld4::RuntimeState&) const::$_1::operator()() const + 116
    frame #17: 0x00000001942d2b78 dyld`dyld4::Loader::runInitializersBottomUpPlusUpwardLinks(dyld4::RuntimeState&) const + 420
    frame #18: 0x00000001942eed30 dyld`dyld4::APIs::dlopen_from(char const*, int, void*) + 1876
    frame #19: 0x0000000100003ed8 load`main + 120
    frame #20: 0x00000001942bb154 dyld`start + 2476
egilll commented 1 month ago

I've also been seeing errors relating to std::streambuf on MacOS 14.6 with LLVM-19.1, with that same rare ABI code you're seeing (std::basic_streambuf::[...]::encoding[abi:ne190101]).

Upgrading from MacOS 14.6 to MacOS 15 (= Darwin Kernel Version 24.0) and upgrading Command Line Tools fixed the issue on my end.

gwllx commented 6 days ago

Hi, @hlewin are you using a 19.1.x version of LLD for your libcxx build?

I have also run into the same seg fault with llvm-19.1.4 on macOS 15.1 (although not a static libcxxabi/libunwind build). After bisecting, I tracked it down to #99255. This enables MachO fixup chains in LLD by default. I confirmed this was the problem by adding -no_fixup_chains to the libcxx/libcxxabi/libunwind link flags, which allows me to load the newly built libc++.dylib.

My config:

cmake
  -G Ninja -S . -B build \
  -DCMAKE_BUILD_TYPE=RelWithDebInfo \
  -DLLVM_TARGETS_TO_BUILD=Native \
  -DLLVM_ENABLE_PROJECTS="clang;lld" \
  -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind" \
  -DLLVM_ENABLE_LLD=ON

If you are not using LLD, this is potentially a separate problem and I can open a new issue.