Linux では、次を使用dladdr()
して、呼び出し元の関数を解決するために使用できます。
#define _GNU_SOURCE
#include <dlfcn.h>
...
void *retAddr = __builtin_extract_return_addr(__builtin_return_address(0));
Dl_info d;
(void)dladdr(retAddr, &d);
printf("%s called from %s + 0x%p\n",
__FUNC__,
d.dli_sname,
(retAddr - d.dli_saddr));
詳細については、GCC のドキュメント__builtin_return_address()
と Linux のマンページdladdr(3)
を参照してください。
この関数dladdr()
は、Solaris/MacOSX/*BSD でも使用できますが、表示されるようにする以外のプリプロセッサ定義が必要_GNU_SOURCE
です。それぞれのオペレーティング システムのマンページを参照してください ...
編集:これはシンボル テーブルの存在に依存しているため、削除されたバイナリでは正常に解決されない可能性があることに注意してください。上記にエラー処理を追加しようとはしていません。一般に、どのタイプの自動バックトレース (関数名解決を伴う) サポートも、シンボル テーブルが取り除かれることを好みません。
非常に簡単なものとして、私は時々単純に使用します:
#include <execinfo.h>
...
void *retAddr[10];
backtrace_symbols_fd(retAddr, backtrace(retaddr, 10), STDERR_FILENO);
10 エントリの深いスタック トレースを取得するためです。繰り返しますが、symtab が削除されていないことに依存しています。複数のアドレスを解決しているため、これにはパフォーマンスのペナルティがあります。
Edit2: シンボル テーブル (とりわけ、実行可能ファイル/ライブラリ内の関数の開始アドレスとサイズを含む) がなければ、「開始アドレス」とは何かという情報はかなり無意味です。CPU 自体に関する限り、命令ポインターが特定の時点で現在の場所にどのように到着したかについての記録は実際にはありません。適切に構造化され、コンパイラによって生成されたコードとして、CPU に対して「有効」です。x86 命令は可変サイズであり、オペコード マップは高密度です。goto
jmp
ほぼすべてのランダムなバイトシーケンスが「有効な」命令ストリームを構成するのに十分です。したがって、バイナリ コードのヒューリスティック逆アセンブルは、100% 安全な方法ではありません。
その意味で、シンボル テーブルは、デバッガーの "マーカー" も確立します。シンボル テーブルに記録されている関数の開始アドレスで逆アセンブルを開始すると、有効な命令ストリームを見つけることが期待できます。また、バックトレースで見つかった戻りアドレスが実際にcall
命令の前にあることを検証することにより、クロス検証できます。