'How to manually disable libstdc++ in CMake for Clang

Recently I'm configuring a new project in language C and C++ using both CMake and llvm Clang with Visual Studio Code for a program where EVERY exact object or library it generates or depends needs specifying explicitly to make or link against. The program by design should feature high RAM efficency, security for the least vulnerabilities induced by the unused data segments and cross-compilation.

That being said, the provision libraries for architecture, application binary interface (aka ABIs), standard libraries functions from C and C++ standard and executable binary need manual setup for toolchain program to process in appropriate manner.

Well, the libstdc++ substitution with libc++ library is what I think a good "entry point" to kick-off, but definitely not straight forward, which you might have learned enough from documented painful practices elsewhere and think of that as a bad idea...but I'm sorry, this has become my obsession. I really appreciate your help before it drives me nuts here.

Anyway, here is a list of information about my local machine in below:

  • Ubuntu 20.04 @ v5.13.0-40-generic kernel, SMP, x86_64 instruction-set
  • CMake 3.23.1, built from source
  • gcc-10:amd64
  • clang-12:amd64
  • libstdc++-10-dev:amd64
  • libc++-12-dev:amd64

Clang works fine to return from command:

jacob.nielson@ubuntu~$: clang++-12 -v -c -xc /dev/null

CLANG

to keep the new project from any future unexpected and irrelevant issue as much as possible, I leave the project directory layout as simple as it could be before making a CMakeLists.txt:

CLANG

To begin with, I looked up in CMake's document about either compiling (preprocessing) or linking options:

-"-nostdinc"
-"-nostdinc++"
-"-nobuiltininc"
-"-nostdlib"
-"-nolibc"
-"-nostdlib++"
-"-nodefaultlibs"

Then I come up with a few simple lines in the CMakeLists.txt:

  cmake_minimum_required(VERSION 3.23.0)
  project(program VERSION 0.0.1)
  set(TARGET program)

  add_compile_options(-std=c++17)

  # I'm not sure about semantics of "-nostdinc++", "-nostdinc" and "-nobuiltininc". 
  # Should I add them all in case to substitute libstdc++ with libc++ or not in below?
  add_compile_options($<$<COMPILE_LANG_AND_ID:CXX,Clang>:-nobuiltininc>)
  add_compile_options($<$<COMPILE_LANG_AND_ID:CXX,Clang>:-nostdinc++>)
  add_compile_options($<$<COMPILE_LANG_AND_ID:CXX,Clang>:-nostdinc>)

  # Should I add path to system headers in "-isystem" option? 
  # Attention: cmake-generator-expression does not recognize white-space separated options group to be string. It is option "-isystem" where the path to directory as value follows without white-space in this example.
  add_compile_options(
      $<$<COMPILE_LANG_AND_ID:CXX,Clang>:-isystem/usr/include/x86_64-linux-gnu>
      $<$<COMPILE_LANG_AND_ID:CXX,Clang>:-isystem-after/usr/include>
      # "-cxx-isystem" option is legacy and is not appropriate for standard headers. Add path to the subdirectories where you put your own c++-only headers in your project.
      $<$<COMPILE_LANG_AND_ID:CXX,Clang>:-cxx-isystem ${...}>
  )

  # If C or/and C++ standard library is turned off like in above, Should I add path to libc++ headers in "-stdlib++-isystem" option?
  # Attention: cmake-generator-expression does not recognize white-space separated options group to be string. It is option "-stdlib++-isystem" where the path to directory as value follows without white-space in this example.
  add_compile_options($<$<COMPILE_LANG_AND_ID:CXX,Clang>:-stdlib++-isystem/usr/lib/llvm-12/include/c++/v1>) 

  # Replace default C++ standard with libc++.
  add_link_options(
      $<$<LINK_LANG_AND_ID:CXX,Clang>:-stdlib=libc++>
  )

  add_executable(${TARGET} "${CMAKE_CURRENT_SOURCE_DIR}/source/${TARGET}.cpp")
  target_link_libraries(${TARGET} PRIVATE
      $<$<LINK_LANG_AND_ID:CXX,Clang>:c++> 
      $<$<LINK_LANG_AND_ID:CXX,Clang>:c++abi>
      $<$<LINK_LANG_AND_ID:CXX,Clang>:unwind>
      $<$<LINK_LANG_AND_ID:CXX,Clang>:ssl>
      $<$<LINK_LANG_AND_ID:CXX,Clang>:crypto>
      $<$<LINK_LANG_AND_ID:CXX,Clang>:pthread>
      )

Next, I tried to generate "Unix Makefiles" for project:

CMake Prompt

CMake parsed successfully:

CMake Output

Meanwhile it generates CMakeCCompiler.cmake and CMakeCXXCompiler.cmake in ./build/CMakeFiles/3.23.1/ temporary folder, the later contains a few lines:

    set(CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES "/usr/include/c++/10;/usr/include/x86_64-linux-gnu/c++/10;/usr/include/c++/10/backward;/usr/local/include;/usr/lib/llvm-12/lib/clang/12.0.0/include;/usr/include/x86_64-linux-gnu;/usr/include")
    set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "stdc++;m;gcc_s;gcc;c;gcc_s;gcc")
    set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "/usr/lib/gcc/x86_64-linux-gnu/10;/usr/lib/x86_64-linux-gnu;/usr/lib64;/lib/x86_64-linux-gnu;/lib64;/usr/lib;/usr/lib/llvm-12/lib;/lib")
    set(CMAKE_CXX_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "")

Note: Clang uses heuristic discovery to search headers in directories which are reported by front-end. This is inappropriate because I just don't want either headers or libraries of libstdc++ to be implicitly used within the project.

Also, it exports compile commands into compile_commands.json as below:

/usr/bin/clang++-12 -DLIBCXXABI_USE_COMPILER_RT=YES -DLIBCXX_USE_COMPILER_RT=YES -I/data/solution/projects/program/include -std=c++17 -stdlib=libc++ -isystem /usr/include/x86_64-linux-gnu -stdlib++-isystem /usr/lib/llvm-12/include/c++/v1 -o CMakeFiles/program.dir/source/program.cpp.o -c /data/solution/projects/program/source/program.cpp

It all looks good. But when I use ldd to check:

jacob.nielson@ubuntu:/data/solution/projects/program/build/debug$ ldd program

    linux-vdso.so.1 (0x00007ffee7190000)
    libc++.so.1 => /lib/x86_64-linux-gnu/libc++.so.1 (0x00007f69164a5000)
    libc++abi.so.1 => /lib/x86_64-linux-gnu/libc++abi.so.1 (0x00007f691646d000)
    libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f691628b000)     /* LIBSTDC++ in here! */
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f691613c000)               /* LIBM in here */
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f6916121000)       /* gcc startup file in here! */
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6915f2f000)               /* LIBC in here! */
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f6915f0a000)
    librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f6915f00000)
    libatomic.so.1 => /lib/x86_64-linux-gnu/libatomic.so.1 (0x00007f6915ef6000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f691658d000)

It is frustrating to see all libstdc++, libc, libm or libgcc_s remain. Is there any option I missed to link them statically such like -static-libc in GNU CC or do I have to give up my ambition early at this point?



Solution 1:[1]

The answer to the question is hereby noted.

There is a mistake about missing definitions to instruct Clang to use compiler_rt. The problem is solved once I had add to relevant options.

As to replace libstsdc++ with libc++, one shall not forget to add the following options:

add_compile_definitions(
    $<$<COMPILE_LANG_AND_ID:CXX,Clang>:LLVM_ENABLE_RUNTIMES=libunwind>
    $<$<COMPILE_LANG_AND_ID:CXX,Clang>:LIBCXX_USE_COMPILER_RT=YES>
    $<$<COMPILE_LANG_AND_ID:CXX,Clang>:LIBCXXABI_USE_COMPILER_RT=YES>
    $<$<COMPILE_LANG_AND_ID:CXX,Clang>:LIBUNWIND_USE_COMPILER_RT=YES>
    )

add_link_opptions(
    $<$<LINK_LANG_AND_ID:CXX,Clang>:-stdlib=libc++>
    $<$<LINK_LANG_AND_ID:CXX,Clang>:-rtlib=compiler-rt>
    )

AFAK, definitions above help to instruct clang "not to link against default libraries", but one also has to use them in conjunction with compile option "-stdlib=libc++" and especially NO "-nostdlib" for the replace to take effect. Part of the document covers in below:

Compiler runtime

The default runtime library is target-specific. For targets where GCC is the dominant compiler, Clang currently defaults to using libgcc_s. On most other targets, compiler-rt is used by default.

However, it is much unclear to me what target refers to here and how comes to tell a compiler is dominant for a given target.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1