TL;DR
いろんな箇所から呼ばれる関数のデバッグをしたいので、呼び出し元の関数名を取得したい。
__builtin_return_address(0)
と dladdr
を使うと良さそう。
実装サンプル
#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
により名前の取得はできない。
$ 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
参考
C言語で関数名の文字列を取得する方法 - たいしょー(miettal)の日記
Function caller in linux kernel - Stack Overflow
How to get function's name from function's pointer in C? - Stack Overflow
Return Address - Using the GNU Compiler Collection (GCC)
GCCのコンパイルオプションで関数トレーサ - torutkの日記
__builtin_return_addressについて - syohex’s diary