これはなに
Ubuntu 上の clang で C++ を使うときに (GNU Toolchain の) libstdc++ ではなく (LLVM の) libc++ を使う、という話です。
「それって当たり前だよね」と思うかもしれませんが、実はそうでもないのです。
ついでに、その他の runtime library の切り替えについても調べたことを書いておきます。
このあたりに関する公式ドキュメントはこちら。
https://clang.llvm.org/docs/Toolchain.html#runtime-libraries
環境
Ubuntu 18.04 に LLVM の標準の手順 でインストールしたものです。
bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)"
$ clang++-10 --version
clang version 10.0.1-++20200507062652+bab8d1790a3-1~exp1~20200507163249.158
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
指定なし
簡単なサンプルプログラムをコンパイルします。(例外が発生しうるコードにしてあります)
#include <cstdio>
#include <string>
int main() { puts(std::string("hello").c_str()); }
コンパイル・実行します。
$ clang++-10 test.cc
$ ./a.out
hello
何も不思議はないですね。 -v
をつけてみます。
$ clang++-10 -v test.cc
clang version 10.0.1-++20200507062652+bab8d1790a3-1~exp1~20200507163249.158
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/7
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/8
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7.5.0
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/8
Selected GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0
Candidate multilib: .;@m64
Selected multilib: .;@m64
"/usr/lib/llvm-10/bin/clang" -cc1 -triple x86_64-pc-linux-gnu -emit-obj -mrelax-all -disable-free -disable-llvm-verifier -discard-value-names -main-file-name test.cc -mrelocation-model static -mthread-model posix -mframe-pointer=all -fmath-errno -fno-rounding-math -masm-verbose -mconstructor-aliases -munwind-tables -target-cpu x86-64 -dwarf-column-info -fno-split-dwarf-inlining -debugger-tuning=gdb -v -resource-dir /usr/lib/llvm-10/lib/clang/10.0.1 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../../include/c++/7.5.0 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../../include/x86_64-linux-gnu/c++/7.5.0 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../../include/x86_64-linux-gnu/c++/7.5.0 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../../include/c++/7.5.0/backward -internal-isystem /usr/local/include -internal-isystem /usr/lib/llvm-10/lib/clang/10.0.1/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fdeprecated-macro -fdebug-compilation-dir /home/ohta/projects/hagoromo/hgrm.tools.ci/generic/config/docker -ferror-limit 19 -fmessage-length 0 -fgnuc-version=4.2.1 -fobjc-runtime=gcc -fcxx-exceptions -fexceptions -fdiagnostics-show-option -fcolor-diagnostics -faddrsig -o /tmp/test-593447.o -x c++ test.cc
clang -cc1 version 10.0.1 based upon LLVM 10.0.1 default target x86_64-pc-linux-gnu
ignoring nonexistent directory "/include"
ignoring duplicate directory "/usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../../include/x86_64-linux-gnu/c++/7.5.0"
#include "..." search starts here:
#include <...> search starts here:
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../../include/c++/7.5.0
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../../include/x86_64-linux-gnu/c++/7.5.0
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../../include/c++/7.5.0/backward
/usr/local/include
/usr/lib/llvm-10/lib/clang/10.0.1/include
/usr/include/x86_64-linux-gnu
/usr/include
End of search list.
"/usr/bin/ld" -z relro --hash-style=gnu --build-id --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o a.out /usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../x86_64-linux-gnu/crt1.o /usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../x86_64-linux-gnu/crti.o /usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/crtbegin.o -L/usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0 -L/usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../x86_64-linux-gnu -L/lib/x86_64-linux-gnu -L/lib/../lib64 -L/usr/lib/x86_64-linux-gnu -L/usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../.. -L/usr/lib/llvm-10/bin/../lib -L/lib -L/usr/lib /tmp/test-593447.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/crtend.o /usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../x86_64-linux-gnu/crtn.o
注目は最後のリンカを起動している行です。
-lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc
と、 libc++ ではなく libstdc++ がリンクされていることがわかります。
-stdlib=libc++
-stdlib=libc++
をつけると libc++ を使ってくれる、はずです。
$ clang++-10 -stdlib=libc++ test.cc
test.cc:1:10: fatal error: 'cstdio' file not found
#include <cstdio>
^~~~~~~~
1 error generated.
が、エラーになってしまいました。どうやら、標準のインストール方法では libc++ はインストールされないようです。
インストールします。
# apt install -y libc++-10-dev
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
libc++1-10 libc++abi1-10
Suggested packages:
clang
The following NEW packages will be installed:
libc++-10-dev libc++1-10 libc++abi1-10
0 upgraded, 3 newly installed, 0 to remove and 9 not upgraded.
libc++abi1-10
というパッケージも入ったようです。
コンパイルをやりなおしてみます。
$ clang++-10 -stdlib=libc++ test.cc
/usr/bin/ld: cannot find -lc++abi
clang: error: linker command failed with exit code 1 (use -v to see invocation)
エラーが変わりました。 libc++abi が見つけられていない。インストールされてたのに。
$ ls -l /usr/lib/x86_64-linux-gnu/libc++*
lrwxrwxrwx 1 root root 23 May 7 16:32 /usr/lib/x86_64-linux-gnu/libc++.a -> ../llvm-10/lib/libc++.a
lrwxrwxrwx 1 root root 24 May 7 16:32 /usr/lib/x86_64-linux-gnu/libc++.so -> ../llvm-10/lib/libc++.so
lrwxrwxrwx 1 root root 13 May 7 16:32 /usr/lib/x86_64-linux-gnu/libc++.so.1 -> libc++.so.1.0
lrwxrwxrwx 1 root root 28 May 7 16:32 /usr/lib/x86_64-linux-gnu/libc++.so.1.0 -> ../llvm-10/lib/libc++.so.1.0
lrwxrwxrwx 1 root root 16 May 7 16:32 /usr/lib/x86_64-linux-gnu/libc++abi.so.1 -> libc++abi.so.1.0
lrwxrwxrwx 1 root root 31 May 7 16:32 /usr/lib/x86_64-linux-gnu/libc++abi.so.1.0 -> ../llvm-10/lib/libc++abi.so.1.0
libc++abi.so が存在していないので、 symlink してみます。
$ cd usr/lib/x86_64-linux-gnu
$ sudo ln -s libc++abi.so.1 libc++abi.so
これで通るようになりました。(これでいいのかなぁ?)
$ clang++-10 -stdlib=libc++ test.cc
$ ./a.out
hello
-v
をつけてリンク部分を見てみます。
-lc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc
なるほど、確かに libstdc++ ではなく libc++ を使っているようです。
-rtlib=compiler-rt
しかし、 compiler runtime library として LLVM の compiler-rt は使われていないようです。せっかくなのでこれも LLVM のものを使いたい。
$ clang++-10 -v -stdlib=libc++ -rtlib=compiler-rt test.cc
すると確かにリンクオプションが
-lc++ -lm /usr/lib/llvm-10/lib/clang/10.0.1/lib/linux/libclang_rt.builtins-x86_64.a -lc /usr/lib/llvm-10/lib/clang/10.0.1/lib/linux/libclang_rt.builtins-x86_64.a /usr/lib/llvm-10/lib/clang/10.0.1/lib/linux/clang_rt.crtend-x86_64.o
のように変わり、 compiler-rt を使ってくれているようです。
ところが以下のようなリンクエラーとなります。
/tmp/test-2ede37.o: In function `main':
test.cc:(.text+0x59): undefined reference to `_Unwind_Resume'
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../x86_64-linux-gnu/libc++abi.so: undefined reference to `_Unwind_RaiseException'
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../x86_64-linux-gnu/libc++abi.so: undefined reference to `_Unwind_GetLanguageSpecificData'
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../x86_64-linux-gnu/libc++abi.so: undefined reference to `_Unwind_DeleteException'
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../x86_64-linux-gnu/libc++abi.so: undefined reference to `_Unwind_SetIP'
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../x86_64-linux-gnu/libc++abi.so: undefined reference to `_Unwind_GetRegionStart'
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../x86_64-linux-gnu/libc++abi.so: undefined reference to `_Unwind_SetGR'
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../x86_64-linux-gnu/libc++abi.so: undefined reference to `_Unwind_GetIP'
-unwindlib=libgcc
どうやら、unwind library (https://clang.llvm.org/docs/Toolchain.html#unwind-library に説明がある ) がリンクできていないようです。確かにリンクオプションにもそれっぽい指定がない。
上記ドキュメントにも指定の方法の説明がなく、ここでハマったので LLVM のソースコードを見てみました。
https://github.com/llvm/llvm-project/blob/17e13da29de48e10fa4ae50831e781d9c44edde9/clang/lib/Driver/ToolChain.cpp#L791-L816
https://github.com/llvm/llvm-project/blob/2da89df4e8d240160d5f265ecac4b0c6a0705d65/clang/lib/Driver/ToolChains/CommonArgs.cpp#L1218-L1234
どうやら、 -unwindlib
というオプションに libgcc
とか libunwind
を指定すればよいようです。
(clang++ --help
を見てみたらあっさりとした説明がありました: -unwindlib=<value> Unwind library to use
)
せっかくなので LLVM な libunwind を使いたいところですが、どうも LLVM な apt repository では提供されていないように見えます。
仕方がないのでここでは libgcc_s (GNU) を使うことにしました。
$ clang++-10 -stdlib=libc++ -rtlib=compiler-rt -unwindlib=libgcc test.cc
$ ./a.out
hello
ようやく通りました。
リンクオプションはこんな感じ (一部抜粋)。 -lgcc_s
が増えています。
-lc++ -lm /usr/lib/llvm-10/lib/clang/10.0.1/lib/linux/libclang_rt.builtins-x86_64.a -lgcc_s -lc /usr/lib/llvm-10/lib/clang/10.0.1/lib/linux/libclang_rt.builtins-x86_64.a -lgcc_s /usr/lib/llvm-10/lib/clang/10.0.1/lib/linux/clang_rt.crtend-x86_64.o
全体も載せておこう。
"/usr/bin/ld" -z relro --hash-style=gnu --build-id --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o a.out /usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../x86_64-linux-gnu/crt1.o /usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../x86_64-linux-gnu/crti.o /usr/lib/llvm-10/lib/clang/10.0.1/lib/linux/clang_rt.crtbegin-x86_64.o -L/usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0 -L/usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../x86_64-linux-gnu -L/lib/x86_64-linux-gnu -L/lib/../lib64 -L/usr/lib/x86_64-linux-gnu -L/usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../.. -L/usr/lib/llvm-10/bin/../lib -L/lib -L/usr/lib /tmp/test-91de86.o -lc++ -lm /usr/lib/llvm-10/lib/clang/10.0.1/lib/linux/libclang_rt.builtins-x86_64.a -lgcc_s -lc /usr/lib/llvm-10/lib/clang/10.0.1/lib/linux/libclang_rt.builtins-x86_64.a -lgcc_s /usr/lib/llvm-10/lib/clang/10.0.1/lib/linux/clang_rt.crtend-x86_64.o /usr/bin/../lib/gcc/x86_64-linux-gnu/7.5.0/../../../x86_64-linux-gnu/crtn.o
まとめ
- (少なくとも LLVM 公式手順で Ubuntu にインストールした) clang++ はデフォルトでは libc++ ではなく libstdc++ を使う
- runtime library の切り替えは
-stdlib
/-rtlib
/-unwindlib
オプションで行う - LLVM 公式手順で Ubuntu にインストールするだけだと libc++ を使うのは意外にてこずります
Ubuntu で clang と libc++ を使う手順
- LLVM の標準の手順 でインストール
apt install -y libc++-10-dev
cd usr/lib/x86_64-linux-gnu && sudo ln -s libc++abi.so.1 libc++abi.so
としたうえで、
- libc++ だけを使う:
-stdlib=libc++
- compiler-rt も使う:
-stdlib=libc++ -rtlib=compiler-rt -unwindlib=libgcc
- LLVM libunwind も使う: (これは今回試してない)
とします。