Can different versions of libc++ coexist in a program at runtime?

I’m working on a macOS app, and I use the git HEAD of Clang and libc++ so I can play with C++ modules and other new stuff. I’ve built up some CMake files and they appear to work, but it also looks like it loads multiple versions of libc++ – the new one I compiled, and the one installed by default on macOS.

I realize I don’t understand linking and loading very well. It seems like a dark art. If there’s a good book or website explaining it, please let me know. :slight_smile:

I set the env var DYLD_PRINT_LIBRARIES and start my executable, and I can see it appears to link to multiple copies of libc++, along with various system libs from Apple. Example:

...
dyld[98783]: <DA42E8C2-95EE-31B8-9058-5523BA0E326E> /Users/rob/Dev/llvm-project/build/lib/libc++.1.0.dylib
dyld[98783]: <786B9A5A-5E72-3E5F-81BA-DBC69BE60780> /Users/rob/Dev/llvm-project/build/lib/libc++abi.1.0.dylib
dyld[98783]: <0225C4B6-DAA0-3BB4-9A95-0B57A5023EED> /usr/lib/libSystem.B.dylib
dyld[98783]: <540CEDFD-5A35-3F35-8953-DCB7C4834EB5> /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit
...
dyld[98783]: <AF1F6DD6-2182-37B5-9E27-0CC62C440696> /usr/lib/libc++abi.dylib
dyld[98783]: <EAB4B9FE-7636-3817-9E54-72C115A5493B> /usr/lib/libobjc.A.dylib
dyld[98783]: <BE8B9C58-66B9-3AA7-A617-DB47EEA224D4> /usr/lib/liboah.dylib
dyld[98783]: <B8339FB2-CBAE-3D84-B080-BD19DDB2981C> /usr/lib/libc++.1.dylib

Is that bad? Or is libc++ designed to do this in a robust way, perhaps with some changes to my CMake file and compiler and linker flags? I don’t know if the runtime has a way of keep these libraries separate or if all the symbol exist in a global namespace and step on each other.

Rob

This can all be a bit complicated. It depends very much on what you are trying to achieve. It looks like you currently link against two libc++ dylibs which have the same symbols. That’s definitely not a good idea. Even worse, you have two libc++abi dylibs. Could you elaborate a bit on what you are trying to achieve? E.g. do you plan to use other system-provided functionality?

I’m trying to create a macOS app and use the latest C++, not the one provided with Xcode.

If there are linker flags I can give to CMake to make this work, I just don’t know them. Currently I have:

set(MY_CLANG_DIR /Users/rob/Dev/llvm-project/build)
set(CMAKE_CXX_COMPILER ${MY_CLANG_DIR}/bin/clang++)
set(CMAKE_CXX_STANDARD 23)
...
set(CMAKE_EXE_LINKER_FLAGS "-L ${MY_CLANG_DIR}/lib -rpath ${MY_CLANG_DIR}/lib")

I thought that last one would cause it to load out of the new clang dir, but apparently it doesn’t stop it from also loading the system one.

System libraries also link against libc++. Because of that, the other library also gets loaded. The interesting question is how you configure your custom libc++. To avoid having multiple libc++abi libraries you can set LIBCXX_CXX_ABI to system-libcxxabi. To avoid having the same symbols in the libc++ dylib you can set LIBCXX_ABI_NAMESPACE to something else, like __robnik_libcxx. If you want you can also set LIBCXX_ENABLE_SHARED to False, so you link against your libc++ statically (you should still change the ABI namespace).

1 Like

Thanks. I will try to rebuild using these definitions and see how it goes.

I’m hitting some error with libunwind now. Maybe I don’t need it. I followed the “bootstrapping build” instructions here: Building libc++ — libc++ documentation.

It built clang, but then it seems to think exception support is disabled.

%  cmake -S llvm -B build \
        -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" \
        -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind" \
        -DLIBCXX_CXX_ABI=system-libcxxabi \
        -DLIBCXX_ABI_NAMESPACE=__robnik_libcxx \
        -DCMAKE_BUILD_TYPE=Release \
        -G Ninja

% ninja -C build runtimes
... [lots of building, forgot to limit the targets] ...
[3783/3787] Performing configure step for 'runtimes'
...
-- Looking for pthread_once in pthread
-- Looking for pthread_once in pthread - found
-- Performing Test CXX_SUPPORTS_WNO_UNUSED_FUNCTION_FLAG
-- Performing Test CXX_SUPPORTS_WNO_UNUSED_FUNCTION_FLAG - Failed
-- Performing Test CXX_SUPPORTS_FUNWIND_TABLES_FLAG
-- Performing Test CXX_SUPPORTS_FUNWIND_TABLES_FLAG - Success
-- Performing Test CXX_SUPPORTS_FNO_EXCEPTIONS_FLAG
-- Performing Test CXX_SUPPORTS_FNO_EXCEPTIONS_FLAG - Failed
-- Performing Test CXX_SUPPORTS_FNO_RTTI_FLAG
-- Performing Test CXX_SUPPORTS_FNO_RTTI_FLAG - Failed
CMake Error at /Users/rob/Dev/llvm-project/libunwind/src/CMakeLists.txt:109 (message):
  Compiler doesn't support generation of unwind tables if exception support
  is disabled.  Building libunwind DSO with runtime dependency on C++ ABI
  library is not supported.


-- Configuring incomplete, errors occurred!
FAILED: runtimes/runtimes-stamps/runtimes-configure /Users/rob/Dev/llvm-project/build/runtimes/runtimes-stamps/runtimes-configure 
cd /Users/rob/Dev/llvm-project/build/runtimes/runtimes-bins && /usr/local/Cellar/cmake/HEAD-3d6075d/bin/cmake --no-warn-unused-cli -DCMAKE_C_COMPILER=/Users/rob/Dev/llvm-project/build/./bin/clang -DCMAKE_CXX_COMPILER=/Users/rob/Dev/llvm-project/build/./bin/clang++ -DCMAKE_ASM_COMPILER=/Users/rob/Dev/llvm-project/build/./bin/clang -DCMAKE_AR=/Users/rob/Dev/llvm-project/build/./bin/llvm-ar -DCMAKE_LIBTOOL=/Users/rob/Dev/llvm-project/build/./bin/llvm-libtool-darwin -DCMAKE_LIPO=/Users/rob/Dev/llvm-project/build/./bin/llvm-lipo -DCMAKE_RANLIB=/Users/rob/Dev/llvm-project/build/./bin/llvm-ranlib -DCMAKE_NM=/Users/rob/Dev/llvm-project/build/./bin/llvm-nm -DCMAKE_OBJDUMP=/Users/rob/Dev/llvm-project/build/./bin/llvm-objdump -DCMAKE_C_COMPILER_TARGET=x86_64-apple-darwin22.2.0 -DCMAKE_CXX_COMPILER_TARGET=x86_64-apple-darwin22.2.0 -DCMAKE_ASM_COMPILER_TARGET=x86_64-apple-darwin22.2.0 -DCMAKE_INSTALL_PREFIX=/usr/local -DLLVM_BINARY_DIR=/Users/rob/Dev/llvm-project/build -DLLVM_CONFIG_PATH=/Users/rob/Dev/llvm-project/build/bin/llvm-config -DLLVM_ENABLE_WERROR=OFF -DLLVM_HOST_TRIPLE=x86_64-apple-darwin22.2.0 -DLLVM_HAVE_LINK_VERSION_SCRIPT=0 -DLLVM_USE_RELATIVE_PATHS_IN_DEBUG_INFO=OFF -DLLVM_USE_RELATIVE_PATHS_IN_FILES=OFF -DLLVM_LIT_ARGS=-sv -DLLVM_SOURCE_PREFIX= -DPACKAGE_VERSION=17.0.0git -DCMAKE_BUILD_TYPE=Release -DCMAKE_MAKE_PROGRAM=/usr/local/bin/ninja -DCMAKE_C_COMPILER_LAUNCHER= -DCMAKE_CXX_COMPILER_LAUNCHER= -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DCOMPILER_RT_BUILD_BUILTINS=Off -DLLVM_INCLUDE_TESTS=ON -DLLVM_DEFAULT_TARGET_TRIPLE=x86_64-apple-darwin22.2.0 -DLLVM_ENABLE_PROJECTS_USED=ON -DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF -DLLVM_BUILD_TOOLS=ON -DCMAKE_C_COMPILER_WORKS=ON -DCMAKE_CXX_COMPILER_WORKS=ON -DCMAKE_ASM_COMPILER_WORKS=ON -DHAVE_LLVM_LIT=ON "-DLLVM_ENABLE_RUNTIMES=libcxx;libcxxabi;libunwind" -DLIBCXX_ABI_NAMESPACE=__robnik_libcxx -DLIBCXX_CXX_ABI=system-libcxxabi -GNinja -S /Users/rob/Dev/llvm-project/llvm/runtimes/../../runtimes -B /Users/rob/Dev/llvm-project/build/runtimes/runtimes-bins && /usr/local/Cellar/cmake/HEAD-3d6075d/bin/cmake -E touch /Users/rob/Dev/llvm-project/build/runtimes/runtimes-stamps/runtimes-configure
ninja: build stopped: subcommand failed.

You don’t have to build libc++abi or libunwind. These should be provided by the OS. There is also not a lot gained compiling it yourself, so it’s probably easier to just use the already built ones.

1 Like

Ah, okay.

When you said this:

That controls what it links to as I build libc++, right? Shouldn’t it also be possible to control that later when I’m building my app binary? If I had a new libc++abi, and the system’s libc++abi, I should be able to use linker flags to statically or dynamically link to either one, when I build my app, no?

I wanted to look at the symbols in the system’s libunwind and libc++abi, just to see what they are, but I can’t find the binaries. On macOS Ventura it appears everything has disappeared into some files in:

ls /System/Cryptexes/OS/System/Library/dyld/                                  
dyld_shared_cache_x86_64h	dyld_shared_cache_x86_64h.02	....

As opposed to the old .dylib files that I could look at.