6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

g++ と clang++ の混在させた場合の挙動を確認する

Posted at

はじめに

clang++で作成したライブラリ群をg++でコンパイルしたプログラムから呼び出す事が可能なのかを確認する

開発ツールのインストール

gcc(g++)Ubuntu20.04にインストールする

install gcc
$ 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にインストールする

install clang
$ 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++ でコンパイルを試してみる

main.cc
#include <cstdio>
#include <string>

int main() { 
    puts(std::string("hello").c_str()); 
}

g++ でコンパイル

libstdc++を利用
$ 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を指定しない場合はgcclibstdc++がリンクされる

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)

stdliblibc++を明示的に指定してコンパイルした場合はllvmlibc++がリンクされる

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ファイルを用意
スクリーンショット 2023-02-17 23.52.53.png

main.cc
#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); 
}
hello_gcc.cc
#include <string>

void hello_by_gcc(std::string& str) { 
    str = "GCC: Hello " + str;
    puts(str.c_str()); 
}
hello_clang.cc
#include <string>

void hello_by_clang(std::string& str) { 
    str = "Clang: Hello " + str;
    puts(str.c_str()); 
}

libstdc++のみを利用してコンパイルする場合

コンパイラ 使用する stdlib
g++ libstdc++
clang++ libstdc++

スクリーンショット 2023-02-18 0.00.20.png

コンパイル、リンク、実行に成功

$ 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++

スクリーンショット 2023-02-18 0.00.47.png

シンボルが解決できずリンクに失敗

$ 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言語経由で呼び出すことでシンボル問題を回避する

スクリーンショット 2023-02-18 16.36.13.png

サンプルコード

main.cc
#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
}
hello_gcc.cc
#include <string>

void hello_by_gcc(std::string& str) { 
    str = "GCC: Hello " + str;
    puts(str.c_str()); 
}
hello_clang.cc
#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::stringlibc++std::stringのシンボルをそれぞれ解決している

指定したライブラリ std::stringが見つかった共有ライブラリ
libstdc++ /lib/aarch64-linux-gnu/libstdc++.so.6
libc++ /lib/aarch64-linux-gnu/libc++.so.1
std::string の検索
$ 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]
...

参考資料

6
2
0

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
  3. You can use dark theme
What you can do with signing up
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?