LoginSignup
44
39

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-04-26

TL;DR

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

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

実装サンプル

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 により名前の取得はできない。

実行結果
$ 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

44
39
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
44
39