[Linux][C/C++]関数の復帰アドレス値と呼び出し元の関数名を取得する方法

# TL;DR

いろんな箇所から呼ばれる関数のデバッグをしたいので、呼び出し元の関数名を取得したい。

`__builtin_return_address(0)` と `dladdr` を使うと良さそう。

# 実装サンプル

```c:test.c(呼び出し元の関数名と復帰アドレスを取得する)
#include <stdio.h>
#define __USE_GNU
#include <dlfcn.h>

void hoge() {
    Dl_info info;
    dladdr(__builtin_return_address(0), &info);
    printf("[%s] parent func name => %p [%s]\n",
           __func__,
           __builtin_return_address(0),
           info.dli_sname);
}

void foo() {
    hoge();

    Dl_info info;
    dladdr(__builtin_return_address(0), &info);
    printf("[%s] parent func name => %p [%s]\n",
           __func__,
           __builtin_return_address(0),
           info.dli_sname);
}

int main(int argc, char const* argv[])
{
    hoge();

    foo();

    return 0;
}
```

実行した結果は以下のようになる。dladdrからシンボル名が取得できる。

```実行結果
$ gcc test.c -ldl -rdynamic
$ ./a.out
[hoge] parent func name => 0x4008ee [main]
[hoge] parent func name => 0x40089e [foo]
[foo] parent func name => 0x4008f8 [main]
```

ただし、この方法には制限があって、`-rdynamic` をつけてビルドしないといけない。
(`-rdynamic` => すべてのシンボルを動的シンボルテーブルに追加)

動的シンボルテーブルにシンボルが存在しない場合は `dladdr` により名前の取得はできない。

```bash:実行結果
$ gcc test.c -ldl
$ ./a.out
[hoge] parent func name => 0x4006de [(null)]
[hoge] parent func name => 0x40068e [(null)]
[foo] parent func name => 0x4006e8 [(null)]
```

`__builtin_return_address(0)` により復帰アドレスはわかっているため、例えば `objdump` 等を使えば判別することはできる

```
$ objdump -d ./a.out | view -
000000000040067c <foo>: #呼び出し元
  40067c:	55                   	push   %rbp
  40067d:	48 89 e5             	mov    %rsp,%rbp
  400680:	48 83 ec 20          	sub    $0x20,%rsp
  400684:	b8 00 00 00 00       	mov    $0x0,%eax
  400689:	e8 af ff ff ff       	callq  40063d <hoge>
  40068e:	48 8b 45 08          	mov    0x8(%rbp),%rax   # hogeから抜けるとここに戻る
  400692:	48 8d 55 e0          	lea    -0x20(%rbp),%rdx
  400696:	48 89 d6             	mov    %rdx,%rsi
  400699:	48 89 c7             	mov    %rax,%rdi
  40069c:	e8 7f fe ff ff       	callq  400520 <dladdr@plt>
  4006a1:	48 8b 55 f0          	mov    -0x10(%rbp),%rdx
  4006a5:	48 8b 45 08          	mov    0x8(%rbp),%rax
  4006a9:	48 89 d1             	mov    %rdx,%rcx
  4006ac:	48 89 c2             	mov    %rax,%rdx
  4006af:	be 9f 07 40 00       	mov    $0x40079f,%esi
  4006b4:	bf 78 07 40 00       	mov    $0x400778,%edi
  4006b9:	b8 00 00 00 00       	mov    $0x0,%eax
  4006be:	e8 4d fe ff ff       	callq  400510 <printf@plt>
  4006c3:	c9                   	leaveq 
  4006c4:	c3                   	retq   
~中略~
00000000004006c5 <main>: #呼び出し元
  4006c5:	55                   	push   %rbp
  4006c6:	48 89 e5             	mov    %rsp,%rbp
  4006c9:	48 83 ec 10          	sub    $0x10,%rsp
  4006cd:	89 7d fc             	mov    %edi,-0x4(%rbp)
  4006d0:	48 89 75 f0          	mov    %rsi,-0x10(%rbp)
  4006d4:	b8 00 00 00 00       	mov    $0x0,%eax
  4006d9:	e8 5f ff ff ff       	callq  40063d <hoge>
  4006de:	b8 00 00 00 00       	mov    $0x0,%eax     # hogeから抜けるとここに戻る
  4006e3:	e8 94 ff ff ff       	callq  40067c <foo>
  4006e8:	b8 00 00 00 00       	mov    $0x0,%eax     # fooから抜けるとここに戻る
  4006ed:	c9                   	leaveq 
  4006ee:	c3                   	retq   
  4006ef:	90                   	nop
```


# 関連

以前調べたLD_PRELOADでのフックと組み合わせるとデバッグが捗りそう。

[LD_PRELOADでprintfを後から差し替える - Qiita](http://qiita.com/koara-local/items/d5205f94decade3ffbf1)

# 参考
[C言語で関数名の文字列を取得する方法 - たいしょー(miettal)の日記](http://miettal.hatenablog.com/entry/20120624/1340533907)
[Function caller in linux kernel - Stack Overflow](http://stackoverflow.com/questions/4141324/function-caller-in-linux-kernel)
[How to get function's name from function's pointer in C? - Stack Overflow](http://stackoverflow.com/questions/351134/how-to-get-functions-name-from-functions-pointer-in-c)
[Return Address - Using the GNU Compiler Collection (GCC)](https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html)
[GCCのコンパイルオプションで関数トレーサ - torutkの日記](http://d.hatena.ne.jp/torutk/20090126/p1)
[__builtin_return_addressについて - syohex’s diary](http://syohex.hatenablog.com/entry/20110923/1316784987)