dlopenした共有ライブラリーのValgrind検出結果が???になる現象の対策

dlopenした共有ライブラリーの中でメモリーリークしていると、Valgrindで検出した結果のソースコード情報が???になり、どこでリークしたのかが追えません。

# 環境
- Ubuntu 20.04.5 LTS
- gcc 9.4.0
- Valgrind 3.15.0

# 再現コード
まずはメモリーリークする共有ライブラリー側のソースコードです。
```lib_func.c
#include <stdlib.h>
void lib_func() {
	char* data = (char*)malloc(100);
	return;
}
```
`gcc -shared -fPIC -o libsample.so -g lib_func.c`でsoファイルを作成します。

次に、共有ライブラリーをdlopenして呼び出すmainプログラム側のソースコードです。
```main.c
#include <dlfcn.h>

int main() {
	void* handle = dlopen("./libsample.so", RTLD_NOW);
	void (*lib_func)(void) = (void (*)(void))dlsym(handle, "lib_func");
	(*lib_func)();
	dlclose(handle);
	return 0;
}
//※エラー処理を省いています。
```
`gcc -I./ -o a.out -g main.c -ldl`で実行ファイルを作成します。

# 実行結果
a.outとlibsample.soを同じディレクトリに配置し、Valgrindを使ってa.outを実行してみます。
```
$ valgrind --leak-check=full ./a.out
==6129== Memcheck, a memory error detector
==6129== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==6129== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==6129== Command: ./a.out
==6129== 
==6129== 
==6129== HEAP SUMMARY:
==6129==     in use at exit: 100 bytes in 1 blocks
==6129==   total heap usage: 6 allocs, 5 frees, 1,585 bytes allocated
==6129== 
==6129== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1
==6129==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==6129==    by 0x484B12E: ???
==6129==    by 0x1091C6: main (main.c:6)
==6129== 
==6129== LEAK SUMMARY:
==6129==    definitely lost: 100 bytes in 1 blocks
==6129==    indirectly lost: 0 bytes in 0 blocks
==6129==      possibly lost: 0 bytes in 0 blocks
==6129==    still reachable: 0 bytes in 0 blocks
==6129==         suppressed: 0 bytes in 0 blocks
==6129== 
==6129== For lists of detected and suppressed errors, rerun with: -s
==6129== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
```

メモリーリークする外部関数lib_func()を呼び出している行番号`main.c:6`は出ますが、その先が`???`になってしまいます。

# Valgrindの--keep-debuginfo=yesオプションを使う
--keep-debuginfo=yesオプションとは。
>--keep-debuginfo=<yes|no> [default: no]
When enabled, keep ("archive") symbols and all other debuginfo for unloaded code. This allows saved stack traces to include file/line info for code that has been dlclose'd (or similar). Be careful with this, since it can lead to unbounded memory use for programs which repeatedly load and unload shared objects.
[Google翻訳]
有効にすると、アンロードされたコードのシンボルとその他すべてのデバッグ情報を保持 (「アーカイブ」) します。これにより、保存されたスタック トレースに、dlclose された (または同様の) コードのファイル/行情報を含めることができます。共有オブジェクトのロードとアンロードを繰り返すプログラムでは、無制限のメモリ使用につながる可能性があるため、これには注意してください。

```
$ valgrind --leak-check=full --keep-debuginfo=yes ./a.out
==6145== Memcheck, a memory error detector
==6145== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==6145== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==6145== Command: ./a.out
==6145== 
==6145== 
==6145== HEAP SUMMARY:
==6145==     in use at exit: 100 bytes in 1 blocks
==6145==   total heap usage: 6 allocs, 5 frees, 1,585 bytes allocated
==6145== 
==6145== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1
==6145==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==6145==    by 0x484B12E: lib_func (lib_func.c:4)
==6145==    by 0x1091C6: main (main.c:6)
==6145== 
==6145== LEAK SUMMARY:
==6145==    definitely lost: 100 bytes in 1 blocks
==6145==    indirectly lost: 0 bytes in 0 blocks
==6145==      possibly lost: 0 bytes in 0 blocks
==6145==    still reachable: 0 bytes in 0 blocks
==6145==         suppressed: 0 bytes in 0 blocks
==6145== 
==6145== For lists of detected and suppressed errors, rerun with: -s
==6145== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
```
これでリークしているソースファイル名・行番号まで特定できました。