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

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

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

`gcc(g++)`を`Ubuntu20.04`にインストールする
```shell-session: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++)`をU`buntu20.04`にインストールする
```shell-session: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++ でコンパイルを試してみる
```c++:main.cc
#include <cstdio>
#include <string>

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

## g++ でコンパイル

```shell-session: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`を指定しない場合は`gcc`の`libstdc++`がリンクされる
```shell-session: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++`がリンクされる 
```shell-session: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` の３ファイルを用意
![スクリーンショット 2023-02-17 23.52.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1017489/d2bad165-ca20-0e26-fc10-a823a5b4bb55.png)



```c++: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); 
}
```

```c++:hello_gcc.cc
#include <string>

void hello_by_gcc(std::string& str) { 
    str = "GCC: Hello " + str;
    puts(str.c_str()); 
}
```

```c++: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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1017489/2bab9370-82d8-83f0-17da-6e30c0cbe04e.png)


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

```shell-session
$ 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++`でコンパイルした場合のそれぞれでのシンボルは同一

```shell-session:シンボル確認
$ 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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1017489/786348d2-4f4a-3551-9da7-cb805a2c96e3.png)

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

```shell-session
$ 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`が変更になる。

:::note info
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
:::

:::note warn
所見
全ての機能の`mangled names`の結果が変わるのであれば`libstd++`と`libc++`を共存させても動きそうな気はする
:::


| stdlib | std::string |
|:-:|:-:|
| libstdc++ | std::__cxx11::basic_string  |
| libc++  | std::__1::basic_string  |

```shell-session:シンボル確認
$ 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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1017489/66688675-4d2c-8658-eca2-9f266e479c33.png)



### サンプルコード
```c++: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
}
```

```c++:hello_gcc.cc
#include <string>

void hello_by_gcc(std::string& str) { 
    str = "GCC: Hello " + str;
    puts(str.c_str()); 
}
```

```c++: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++  |

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

```shell-session
$ 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  |

```shell-session: 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]
...
```

# 参考資料

https://qiita.com/kojiohta/items/fb6c307365d1db388acc