search
LoginSignup
11
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

Ubuntu で clang + libc++ を使う

これはなに

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 も使う: (これは今回試してない)

とします。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
11
Help us understand the problem. What are the problem?