在 Ubuntu 上使用 clang + libc++
这是什么?
在使用Ubuntu上的clang编译器进行C++开发时,我们常常使用的是(GNU工具链的)libstdc++而不是(LLVM的)libc++。也许你可能认为这是理所当然的,但实际上情况并非如此。
顺便说一下,我还调查了其他运行库的切换方法。
这里有关于这个领域的官方文件。请点击以下链接查看。
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文件,因此将尝试创建符号链接。
$ 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
哦,确实看起来是在使用libc++而不是libstdc++。
– 编译器运行时库的选项是 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= Unwind library to use)
既然有机会,我想要使用LLVM的libunwind,但是在看起来LLVM的apt存储库似乎没有提供这个。
无奈的是,我决定在这里使用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
把所有都放进去吧。
总结
-
- (少なくとも 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 も使う: (これは今回試してない)
我将这样假设。