複数の動的共有ライブラリ内でシンボルの衝突が発生する場合の挙動


# はじめに
複数の動的共有ライブラリ内でシンボルの衝突が発生した場合の挙動を確認する

# ２つの共有ライブラリ内のクラス名が衝突する場合

`myclass`という同一の名前のクラスが`liba.so`と`libb.so`に存在する場合を考える

![スクリーンショット 2023-02-01 23.42.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1017489/5c8d8e5b-12a5-9f91-9eff-05458c0dca58.png)

## サンプルソースコード

```c++:a.hh
class myclass{
public:
    myclass(){};
    int test();
};
```

```c++:a.cpp
#include <iostream>
#include "a.hh"

int myclass::test(){
    std::cout << "a.cpp: myclass.test()" << "\n";
    return 0;
}
```

```c++:b.cpp
#include <iostream>
#include "a.hh"

int myclass::test(){
    std::cout << "b.cpp: myclass.test()" << "\n";
    return 0;
}
```

`main0.cpp`の`myclass`は実行時まで`a.cpp`の`myclass`の実装が実行されるのか`b.cpp`の`myclass`の実装が実行されるのか決定していない。
```c++:main0.cpp
#include <iostream>
#include "a.hh"

int main(){
    std::cout << "main0.cpp: main()" << "\n";

    myclass myklass;
    myklass.test();

    return 0;
}
```

## 挙動の確認

`a.cpp`と`b.cpp`から共有ライブラリを作成する
```console
$ clang++ -shared -fPIC a.cpp -o liba.so
$ clang++ -shared -fPIC b.cpp -o libb.so
```

コンパイル時に `liba.so` を先にリンクした場合は`a.cpp`の実装が実行される
```console
$ clang++ main0.cpp -L. -la -lb
$ LD_LIBRARY_PATH=. ./a.out
main0.cpp: main()
a.cpp: myclass.test()
```

コンパイル時に `libb.so` を先にリンクした場合は`b.cpp`の実装が実行される
```console
$ clang++ main0.cpp -L. -lb -la
$ LD_LIBRARY_PATH=. ./a.out
main0.cpp: main()
b.cpp: myclass.test()
```


実行中のシンボル解決の状況を確認すると先にリンクしたファイル優先で検索されていることが確認できる
```console
$ clang++ main0.cpp -L. -la -lb
$ LD_DEBUG=symbols,bindings LD_LIBRARY_PATH=. ./a.out
...
main0.cpp: main()
    119778:	symbol=_ZN7myclass4testEv;  lookup in file=./a.out [0]
    119778:	symbol=_ZN7myclass4testEv;  lookup in file=./liba.so [0]
    119778:	binding file ./a.out [0] to ./liba.so [0]: normal symbol `_ZN7myclass4testEv'
...

$ clang++ main0.cpp -L. -lb -la
$ LD_DEBUG=symbols,bindings LD_LIBRARY_PATH=. ./a.out
...
main0.cpp: main()
    120482:	symbol=_ZN7myclass4testEv;  lookup in file=./a.out [0]
    120482:	symbol=_ZN7myclass4testEv;  lookup in file=./libb.so [0] 
    120482:	binding file ./a.out [0] to ./libb.so [0]: normal symbol `_ZN7myclass4testEv'
...
```


# 間接的に利用するクラス名が衝突する場合
実行ファイルから間接的に `myclass` を呼び出す場合の挙動を確認する
感覚的には、下図のように実行ファイルから `suba()` を呼び出すと `libsuba.so` の `suba()` が呼び出されて `libsuba.so` 内部の `myclass` が呼び出され、`subb()` を呼び出すと `libsubb.so` の `subb()` が呼び出されて `libsubb.so` 内部の `myclass` が呼び出されることを期待する。
しかし、そのような挙動にはならない。

![スクリーンショット 2023-02-01 23.45.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1017489/b3d94a2d-00da-bcf2-9431-62e4140af6c4.png)

## サンプルソースコード

```c++:suba.cpp
#include <iostream>
#include "a.hh"

extern int suba(){
    std::cout << "suba()" << "\n";
    myclass myklass;
    myklass.test();

    return 0;
}
```

```c++:subb.cpp
#include <iostream>
#include "a.hh"

extern int subb(){
    std::cout << "subb()" << "\n";
    myclass myklass;
    myklass.test();

    return 0;
}
```

```c++:main1.cpp
#include <iostream>
#include "a.hh"

int suba();
int subb();

int main(){
    std::cout << "main1.cc: main()" << "\n";
    myclass myklass;
    myklass.test();

    suba();
    subb();

    return 0;
}

```

## 挙動の確認

`libsuba.so`を先にリンクした場合、必ず`libsuba.so`の`myclass`が実行される

```console
$ clang++ -shared -fPIC a.cpp suba.cpp -o libsuba.so
$ clang++ -shared -fPIC b.cpp subb.cpp -o libsubb.so
$ clang++ main1.cpp -L. -lsuba -lsubb
$ LD_LIBRARY_PATH=. ./a.out
main1.cc: main()
a.cpp: myclass.test()
suba()
a.cpp: myclass.test()
subb()
a.cpp: myclass.test()
```

`subb()`内での `myclass` の解決時に `libsuba.so` 内の `myclass` が先に検索されている

```console
$ LD_DEBUG=symbols,bindings LD_LIBRARY_PATH=. ./a.out
...
subb()
    122784:	symbol=_ZN7myclassC2Ev;  lookup in file=./a.out [0]
    122784:	binding file ./libsubb.so [0] to ./a.out [0]: normal symbol `_ZN7myclassC2Ev'
    122784:	symbol=_ZN7myclass4testEv;  lookup in file=./a.out [0]
    122784:	symbol=_ZN7myclass4testEv;  lookup in file=./libsuba.so [0]
    122784:	binding file ./libsubb.so [0] to ./libsuba.so [0]: normal symbol `_ZN7myclass4testEv'
...
```

![スクリーンショット 2023-02-01 23.47.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1017489/b8e6ad15-01d5-742d-4844-81a908af843b.png)


`libsubb.so`を先にリンクした場合、必ず`libsubb.so`の`myclass`が実行される

```console
$ clang++ main1.cpp -L. -lsubb -lsuba
$ LD_LIBRARY_PATH=. ./a.out
main1.cc: main()
b.cpp: myclass.test()
suba()
b.cpp: myclass.test()
subb()
b.cpp: myclass.test()
```

`suba()`内での `myclass` の解決時に `libsubb.so` 内の `myclass` が先に検索されている

```console
$ LD_DEBUG=symbols,bindings LD_LIBRARY_PATH=. ./a.out
...
suba()
    122661:	symbol=_ZN7myclassC2Ev;  lookup in file=./a.out [0]
    122661:	binding file ./libsuba.so [0] to ./a.out [0]: normal symbol `_ZN7myclassC2Ev'
    122661:	symbol=_ZN7myclass4testEv;  lookup in file=./a.out [0]
    122661:	symbol=_ZN7myclass4testEv;  lookup in file=./libsubb.so [0]
    122661:	binding file ./libsuba.so [0] to ./libsubb.so [0]: normal symbol `_ZN7myclass4testEv'
...
```

![スクリーンショット 2023-02-01 23.50.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1017489/d8fd5c01-c472-dea5-7231-8edf30b184b8.png)


## 共有ライブラリ内の `myclass` を利用したい場合

`-Wl,-Bsymbolic`オプションでリンカに共有ライブラリ内でのシンボル名解決を事前に行わせる。
これにより最初の感覚通りの挙動となる


```console
$ clang++ -Wl,-Bsymbolic -shared -fPIC a.cpp suba.cpp -o libsuba.so
$ clang++ -Wl,-Bsymbolic -shared -fPIC b.cpp subb.cpp -o libsubb.so
$ clang++ main1.cpp -L. -lsuba -lsubb
$ LD_LIBRARY_PATH=. ./a.out
main1.cc: main()
a.cpp: myclass.test()
suba()
a.cpp: myclass.test()
subb()
b.cpp: myclass.test()
```

```console
$ lang++ main1.cpp -L. -lsubb -lsuba
$ LD_LIBRARY_PATH=. ./a.out
main1.cc: main()
b.cpp: myclass.test()
suba()
a.cpp: myclass.test()
subb()
b.cpp: myclass.test()
```

![スクリーンショット 2023-02-01 23.45.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1017489/b3d94a2d-00da-bcf2-9431-62e4140af6c4.png)


# 参考文献
- [Linux の共有ライブラリの挙動について](https://dayflower.hatenablog.com/entry/20080522/1211531665?utm_source=pocket_saves)
- [ld 2.18 の -Bsymbolic オプションを使うと共有ライブラリ内でシンボルをローカルバインドできる](https://dayflower.hatenablog.com/entry/20080523/1211531833?utm_source=pocket_saves)
- [-Bsymbolic の使用](https://docs.oracle.com/cd/E19455-01/806-2734/chapter4-16/index.html)