Cで関数のポインタから関数の名前を取得する方法は?
編集:実際のケースは次のとおりです。Linuxカーネルモジュールを作成していて、カーネル関数を呼び出しています。これらの関数の一部はポインターであり、カーネル ソースでその関数のコードを検査したいと考えています。しかし、それがどの関数を指しているのかわかりません。システムが失敗すると(カーネルパニック)、現在のコールスタックと関数の名前が画面に出力されるため、それができると思いました。でも、私は間違っていたと思います... 私ですか?
Cで関数のポインタから関数の名前を取得する方法は?
編集:実際のケースは次のとおりです。Linuxカーネルモジュールを作成していて、カーネル関数を呼び出しています。これらの関数の一部はポインターであり、カーネル ソースでその関数のコードを検査したいと考えています。しかし、それがどの関数を指しているのかわかりません。システムが失敗すると(カーネルパニック)、現在のコールスタックと関数の名前が画面に出力されるため、それができると思いました。でも、私は間違っていたと思います... 私ですか?
誰もがそれが不可能だと言う理由に私は驚いています。Linuxでは非静的機能が可能です。
私はこれを達成するために少なくとも2つの方法を知っています。
バックトレース印刷用のGNU関数があります:backtrace()
およびbacktrace_symbols()
(を参照man
)。あなたの場合、あなたはbacktrace()
すでに関数ポインタを持っているので必要ありません、あなたはそれをに渡すだけbacktrace_symbols()
です。
例(作業コード):
#include <stdio.h>
#include <execinfo.h>
void foo(void) {
printf("foo\n");
}
int main(int argc, char *argv[]) {
void *funptr = &foo;
backtrace_symbols_fd(&funptr, 1, 1);
return 0;
}
でコンパイルgcc test.c -rdynamic
出力:./a.out(foo+0x0)[0x8048634]
バイナリ名、関数名、関数開始からのポインタオフセット、およびポインタ値が表示されるため、解析できます。
別の方法はdladdr()
(別の拡張機能)を使用することです、私はprint_backtrace()
使用すると思いますdladdr()
。フィールドに関数名を持つ構造をdladdr()
返します。ここではコード例を提供しませんが、それは明らかです。詳細についてはを参照してください。Dl_info
dli_sname
man dladdr
NB!どちらのアプローチでも、関数が非静的である必要があります。
もう1つの方法があります。を使用してデバッグ情報を使用しますlibdwarf
が、ストリップされていないバイナリが必要であり、簡単に実行できないため、お勧めしません。
これは、追加の支援なしでは直接不可能です。
あなたは出来る:
関数ポインタを名前にマッピングするプログラム内のテーブルを維持する
実行可能ファイルのシンボル テーブルがある場合は、それを調べます。
ただし、後者は難しく、移植性がありません。この方法は、オペレーティング システムのバイナリ形式 (ELF、a.out、.exe など) と、リンカーによって行われる再配置によって異なります。
編集:実際のユースケースが何であるかを説明したので、答えは実際にはそれほど難しくありません。カーネル シンボル テーブルは で利用でき/proc/kallsyms
、それにアクセスするための API があります。
#include <linux/kallsyms.h>
const char *kallsyms_lookup(unsigned long addr, unsigned long *symbolsize,
unsigned long *ofset, char **modname, char *namebuf)
void print_symbol(const char *fmt, unsigned long addr)
単純なデバッグの目的では、後者はおそらく必要なことを正確に実行します-アドレスを取得し、フォーマットしてに送信するか、フォーマット指定子と一緒にprintk
使用できます。printk
%pF
Linuxでは次のように動作します:
%p
nm <program_path> | grep <address>
(0x
プレフィックスなしで)実行します問題の関数が同じプログラムにある場合にのみ機能します(動的にリンクされたライブラリなどではありません)。
ロードされた共有ライブラリのロードアドレスを見つけることができれば、出力された数値からアドレスを差し引き、ライブラリで nm を使用して関数名を見つけることができます。
直接的にはできませんが、必要に応じて、この問題に対して別のアプローチを実装できます。関数を指す代わりに構造体ポインターを作成したり、必要に応じて設定できる説明的な文字列を作成したりできます。おそらくこれらの変数を永久にprintetにしたくないので、デバッグポーズも追加しました。
// Define it like this
typedef struct
{
char *dec_text;
#ifdef _DEBUG_FUNC
void (*action)(char);
#endif
} func_Struct;
// Initialize it like this
func_Struct func[3]= {
#ifdef _DEBUG_FUNC
{"my_Set(char input)",&my_Set}};
{"my_Get(char input)",&my_Get}};
{"my_Clr(char input)",&my_Clr}};
#else
{&my_Set}};
{&my_Get}};
{&my_Clr}};
#endif
// And finally you can use it like this
func[0].action( 0x45 );
#ifdef _DEBUG_FUNC
printf("%s",func.dec_text);
#endif
指し示すことができる関数のリストがあまり大きくない場合、または関数の小さなグループがすでに疑わしい場合は、アドレスを出力して、実行中に使用されたものと比較できます。元:
typedef void (*simpleFP)();
typedef struct functionMETA {
simpleFP funcPtr;
char * funcName;
} functionMETA;
void f1() {/*do something*/}
void f2() {/*do something*/}
void f3() {/*do something*/}
int main()
{
void (*funPointer)() = f2; // you ignore this
funPointer(); // this is all you see
printf("f1 %p\n", f1);
printf("f2 %p\n", f2);
printf("f3 %p\n", f3);
printf("%p\n", funPointer);
// if you want to print the name
struct functionMETA arrFuncPtrs[3] = {{f1, "f1"}, {f2, "f2"} , {f3, "f3"}};
int i;
for(i=0; i<3; i++) {
if( funPointer == arrFuncPtrs[i].funcPtr )
printf("function name: %s\n", arrFuncPtrs[i].funcName);
}
}
出力:
f1 0x40051b
f2 0x400521
f3 0x400527
0x400521
function name: f2
このアプローチは、静的関数でも機能します。
一般的にそれを行う方法はありません。
対応するコードをDLL/共有ライブラリにコンパイルすると、すべてのエントリポイントを登録して、取得したポインタと比較できるようになります。まだ試していませんが、DLL /共有ライブラリの経験があり、動作することを期待しています。これは、クロスプラットフォームで動作するように実装することもできます。
他の誰かがすでにデバッグシンボルを使用してコンパイルすることについて言及している場合は、実行中のアプリケーションから、デバッガーが実行するのと同様に、これらを分析する方法を見つけることができます。しかし、これは完全に独占的であり、移植性はありません。
kallsyms_lookup_name()
のアドレスを検索するために使用しますkallsyms_lookup
。
を指す関数ポインタを使用しkallsyms_lookup
て呼び出します。
Visual Leak Detectorをチェックして、コールスタックの印刷がどのように機能するかを確認してください。ただし、これは Windows を使用していることを前提としています。
できません。関数がコンパイルおよびリンクされるまで、関数名は関数に関連付けられていません。名前ではなく、その時点でのすべてのメモリアドレスによるものです。
反射鏡がなければ、自分がどのように見えるかわかりません。C# などのリフレクション対応言語を使用する必要があります。