Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

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

とします。

kojiohta
某電機メーカーで組み込み系エンジニアやっています。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away