はじめに
clang++
で作成したライブラリ群をg++
でコンパイルしたプログラムから呼び出す事が可能なのかを確認する
開発ツールのインストール
gcc(g++)
をUbuntu20.04
にインストールする
$ sudo apt install build-essential
$ gcc --version
gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
clang(clang++)
をUbuntu20.04
にインストールする
$ sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)"
$ sudo apt install -y libc++-15-dev
$ clang-15 --version
Ubuntu clang version 15.0.7
Target: aarch64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
g++ と clang++ でコンパイルを試してみる
#include <cstdio>
#include <string>
int main() {
puts(std::string("hello").c_str());
}
g++ でコンパイル
$ g++ main.cc
$ ./a.out
hello
$ ldd a.out
linux-vdso.so.1 (0x0000ffffbefd9000)
libstdc++.so.6 => /lib/aarch64-linux-gnu/libstdc++.so.6 (0x0000ffffbeda0000)
libgcc_s.so.1 => /lib/aarch64-linux-gnu/libgcc_s.so.1 (0x0000ffffbed7c000)
libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000ffffbec09000)
/lib/ld-linux-aarch64.so.1 (0x0000ffffbefa9000)
libm.so.6 => /lib/aarch64-linux-gnu/libm.so.6 (0x0000ffffbeb5e000)
clang++ でコンパイル
stdlib
を指定しない場合はgcc
のlibstdc++
がリンクされる
$ clang++-15 main.cc
$ ./a.out
hello
$ ldd a.out
linux-vdso.so.1 (0x0000ffffb316d000)
libstdc++.so.6 => /lib/aarch64-linux-gnu/libstdc++.so.6 (0x0000ffffb2f33000)
libm.so.6 => /lib/aarch64-linux-gnu/libm.so.6 (0x0000ffffb2e88000)
libgcc_s.so.1 => /lib/aarch64-linux-gnu/libgcc_s.so.1 (0x0000ffffb2e64000)
libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000ffffb2cf1000)
/lib/ld-linux-aarch64.so.1 (0x0000ffffb313d000)
stdlib
にlibc++
を明示的に指定してコンパイルした場合はllvm
のlibc++
がリンクされる
$ clang++-15 main.cc -stdlib=libc++
$ ./a.out
hello
$ ldd a.out
linux-vdso.so.1 (0x0000ffff84542000)
libc++.so.1 => /lib/aarch64-linux-gnu/libc++.so.1 (0x0000ffff843e5000)
libc++abi.so.1 => /lib/aarch64-linux-gnu/libc++abi.so.1 (0x0000ffff84394000)
libunwind.so.1 => /lib/aarch64-linux-gnu/libunwind.so.1 (0x0000ffff84373000)
libm.so.6 => /lib/aarch64-linux-gnu/libm.so.6 (0x0000ffff842c8000)
libgcc_s.so.1 => /lib/aarch64-linux-gnu/libgcc_s.so.1 (0x0000ffff842a4000)
libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000ffff84131000)
libpthread.so.0 => /lib/aarch64-linux-gnu/libpthread.so.0 (0x0000ffff84100000)
/lib/ld-linux-aarch64.so.1 (0x0000ffff84512000)
librt.so.1 => /lib/aarch64-linux-gnu/librt.so.1 (0x0000ffff840e8000)
libdl.so.2 => /lib/aarch64-linux-gnu/libdl.so.2 (0x0000ffff840d4000)
g++ と clang++ を混在してコンパイル
g++
とclang++
それぞれでコンパイルして作成した動的共有ライブラリを混在して利用可能かを確認する
サンプルコード
main.cc
, hello_gcc
, hello_clang.cc
の3ファイルを用意
#include <cstdio>
#include <string>
void hello_by_gcc(std::string& str);
void hello_by_clang(std::string& str);
int main() {
puts(std::string("hello").c_str());
std::string str_gcc("Michel");
hello_by_gcc(str_gcc);
std::string str_clang("Tanaka");
hello_by_clang(str_clang);
}
#include <string>
void hello_by_gcc(std::string& str) {
str = "GCC: Hello " + str;
puts(str.c_str());
}
#include <string>
void hello_by_clang(std::string& str) {
str = "Clang: Hello " + str;
puts(str.c_str());
}
libstdc++のみを利用してコンパイルする場合
コンパイラ | 使用する stdlib |
---|---|
g++ | libstdc++ |
clang++ | libstdc++ |
コンパイル、リンク、実行に成功
$ g++ -shared -fPIC hello_gcc.cc -o libhello_gcc.so
$ clang++-15 -shared -fPIC hello_clang.cc -o libhello_clang.so
$ g++ main.cc -L. -lhello_gcc -lhello_clang
$ LD_LIBRARY_PATH=. ldd a.out
linux-vdso.so.1 (0x0000ffffb8cf7000)
libhello_gcc.so => ./libhello_gcc.so (0x0000ffffb8ca1000)
libhello_clang.so => ./libhello_clang.so (0x0000ffffb8c8e000)
libstdc++.so.6 => /lib/aarch64-linux-gnu/libstdc++.so.6 (0x0000ffffb8a97000)
libgcc_s.so.1 => /lib/aarch64-linux-gnu/libgcc_s.so.1 (0x0000ffffb8a73000)
libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000ffffb8900000)
/lib/ld-linux-aarch64.so.1 (0x0000ffffb8cc7000)
libm.so.6 => /lib/aarch64-linux-gnu/libm.so.6 (0x0000ffffb8855000)
$ LD_LIBRARY_PATH=. ./a.out
hello
GCC: Hello Michel
Clang: Hello Tanaka
シンボルは同一
g++
でコンパイルした場合とclang++
でコンパイルした場合のそれぞれでのシンボルは同一
$ nm a.out | c++filt
0000000000012010 V DW.ref.__gxx_personality_v0
0000000000011ce8 a _DYNAMIC
0000000000011fb8 a _GLOBAL_OFFSET_TABLE_
0000000000000f08 R _IO_stdin_used
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U _Unwind_Resume@@GCC_3.0
U hello_by_gcc(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&)
U hello_by_clang(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&)
...
$ nm libhello_gcc.so | c++filt
...
0000000000000f04 T hello_by_gcc(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&)
U std::__cxx11::basic_string<char, std::char_traits<char>,
...
$ nm libhello_clang.so | c++filt
...
0000000000000e44 T hello_by_clang(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&)
...
clang++ でコンパイルする際には libc++ を指定した場合
コンパイラ | 使用する stdlib |
---|---|
g++ | libstdc++ |
clang++ | libc++ |
シンボルが解決できずリンクに失敗
$ g++ -shared -fPIC hello_gcc.cc -o libhello_gcc.so
$ clang++-15 -shared -fPIC -stdlib=libc++ hello_clang.cc -o libhello_clang.so
$ g++ main.cc -L. -lhello_gcc -lhello_clang
$ LD_LIBRARY_PATH=. ldd a.out
$ g++ main.cc -L. -lhello_gcc -lhello_clang
/usr/bin/ld: /tmp/ccOOxe2H.o: in function `main':
main.cc:(.text+0xd0): undefined reference to `hello_by_clang(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&)'
collect2: error: ld returned 1 exit status
シンボルが異なる
clang++
で -stdlib=libc++
を指定してコンパイルした場合は std::string
のシンボルが変化しておりシンボル解決に失敗する。libstdc++
から libc++
に変更することで全ての関数のmangled names
が変更になる。
C++ABI実装
libstdc++
はlibsupc++
を利用しlibc++
はlibc++abi
を利用し互換性がない。libstdc++
からlibc++
に切り替えた際には全ての機能のmangled names
の結果が変わる
https://releases.llvm.org/15.0.0/projects/libcxx/docs/BuildingLibcxx.html#using-alternate-abi-libraries
https://clang.llvm.org/docs/UsersManual.html#profile-remapping
所見
全ての機能のmangled names
の結果が変わるのであればlibstd++
とlibc++
を共存させても動きそうな気はする
stdlib | std::string |
---|---|
libstdc++ | std::__cxx11::basic_string |
libc++ | std::__1::basic_string |
$ nm a.out | c++filt
0000000000012010 V DW.ref.__gxx_personality_v0
0000000000011ce8 a _DYNAMIC
0000000000011fb8 a _GLOBAL_OFFSET_TABLE_
0000000000000f08 R _IO_stdin_used
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U _Unwind_Resume@@GCC_3.0
U hello_by_gcc(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&)
U hello_by_clang(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&)
...
$ nm libhello_gcc.so | c++filt
...
0000000000000f04 T hello_by_gcc(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&)
U std::__cxx11::basic_string<char, std::char_traits<char>,
...
nm libhello_clang.so | c++filt
...
0000000000000d14 T hello_by_clang(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&)
...
C言語を経由して libstdc++ と libc++ を混在させた場合
C言語経由で呼び出すことでシンボル問題を回避する
サンプルコード
#include <cstdio>
#include <string>
void hello_by_gcc(std::string& str);
void hello_by_clang(std::string& str);
extern "C" void chello_by_clang(const char* s);
int main() {
puts(std::string("hello").c_str());
std::string str_gcc("Michel");
hello_by_gcc(str_gcc);
std::string str_clang("Tanaka");
chello_by_clang(str_clang.c_str()); // c wrapper
}
#include <string>
void hello_by_gcc(std::string& str) {
str = "GCC: Hello " + str;
puts(str.c_str());
}
#include <string>
void hello_by_clang(std::string& str) {
str = "Clang: Hello " + str;
puts(str.c_str());
}
extern "C" void chello_by_clang(const char* s){
std::string str(s);
hello_by_clang(str);
}
C言語経由の結果
コンパイラ | 使用する stdlib |
---|---|
g++ | libstdc++ |
clang++ | libc++ |
コンパイル、リンク、実行に成功
$ g++ -shared -fPIC hello_gcc.cc -o libhello_gcc.so
$ clang++-15 -shared -fPIC -stdlib=libc++ hello_clang.cc -o libhello_clang.so
$ g++ main.cc -L. -lhello_gcc -lhello_clang
$ LD_LIBRARY_PATH=. ./a.out
hello
GCC: Hello Michel
Clang: Hello Tanaka
$ LD_LIBRARY_PATH=. ldd ./a.out
linux-vdso.so.1 (0x0000ffff84a1c000)
libhello_gcc.so => ./libhello_gcc.so (0x0000ffff849c6000)
libhello_clang.so => ./libhello_clang.so (0x0000ffff849b3000)
libstdc++.so.6 => /lib/aarch64-linux-gnu/libstdc++.so.6 (0x0000ffff847bc000)
libgcc_s.so.1 => /lib/aarch64-linux-gnu/libgcc_s.so.1 (0x0000ffff84798000)
libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000ffff84625000)
/lib/ld-linux-aarch64.so.1 (0x0000ffff849ec000)
libc++.so.1 => /lib/aarch64-linux-gnu/libc++.so.1 (0x0000ffff8451d000)
libc++abi.so.1 => /lib/aarch64-linux-gnu/libc++abi.so.1 (0x0000ffff844cc000)
libunwind.so.1 => /lib/aarch64-linux-gnu/libunwind.so.1 (0x0000ffff844ab000)
libm.so.6 => /lib/aarch64-linux-gnu/libm.so.6 (0x0000ffff84400000)
libpthread.so.0 => /lib/aarch64-linux-gnu/libpthread.so.0 (0x0000ffff843cf000)
librt.so.1 => /lib/aarch64-linux-gnu/librt.so.1 (0x0000ffff843b7000)
libdl.so.2 => /lib/aarch64-linux-gnu/libdl.so.2 (0x0000ffff843a3000)
libstdc++
のstd::string
とlibc++
のstd::string
のシンボルをそれぞれ解決している
指定したライブラリ | std::stringが見つかった共有ライブラリ |
---|---|
libstdc++ | /lib/aarch64-linux-gnu/libstdc++.so.6 |
libc++ | /lib/aarch64-linux-gnu/libc++.so.1 |
$ LD_DEBUG=symbols LD_LIBRARY_PATH=. ./a.out
...
58206: symbol=_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE6appendEPKcm; lookup in file=./a.out [0]
58206: symbol=_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE6appendEPKcm; lookup in file=./libhello_gcc.so [0]
58206: symbol=_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE6appendEPKcm; lookup in file=./libhello_clang.so [0]
58206: symbol=_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE6appendEPKcm; lookup in file=/lib/aarch64-linux-gnu/libstdc++.so.6 [0]
...
58206: symbol=_ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED1Ev; lookup in file=./libhello_gcc.so [0]
58206: symbol=_ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED1Ev; lookup in file=./libhello_clang.so [0]
58206: symbol=_ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED1Ev; lookup in file=/lib/aarch64-linux-gnu/libstdc++.so.6 [0]
58206: symbol=_ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED1Ev; lookup in file=/lib/aarch64-linux-gnu/libgcc_s.so.1 [0]
58206: symbol=_ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED1Ev; lookup in file=/lib/aarch64-linux-gnu/libc.so.6 [0]
58206: symbol=_ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED1Ev; lookup in file=/lib/ld-linux-aarch64.so.1 [0]
58206: symbol=_ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED1Ev; lookup in file=/lib/aarch64-linux-gnu/libc++.so.1 [0]
...
参考資料