web-dev-qa-db-ja.com

Cの関数ポインタから関数の名前を取得する方法は?

Cで 関数のポインター から関数の名前を取得する方法は?

編集:実際のケースは次のとおりです。私はLinuxカーネルモジュールを書いており、カーネル関数を呼び出しています。これらの関数の一部はポインターであり、カーネルソースでその関数のコードを調べたいと思います。しかし、どの関数が指しているのかわかりません。システムに障害が発生すると(カーネルパニック)、画面に関数の名前で現在のコールスタックが出力されるため、それができると考えました。しかし、私は間違っていたと思います...私ですか?

53
Daniel Silveira

それは、追加の支援なしでは直接不可能です。

あなたは出来る:

  1. プログラムマッピングテーブルの名前への関数ポインターを維持します。

  2. 実行可能ファイルのシンボルテーブルがある場合は、それを調べます。

ただし、後者は難しく、移植性がありません。この方法は、オペレーティングシステムのバイナリ形式(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に送信します。または、%pFフォーマット指定子でprintkを使用できます。

30
Alnitak

私は誰もがそれが不可能だと言う理由に驚いています。 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を使用してデバッグ情報を使用しますが、ストリップされていないバイナリが必要であり、あまり簡単ではないため、お勧めしません。

63
qrdl

Linuxカーネルでは、直接"%pF" printkの形式を使用できます!

void *func = &foo;
printk("func: %pF at address: %p\n", func, func);
16
Zskdan

以下はLinuxで動作します:

  • %pを使用して関数のアドレスをprintf
  • 次に、nm <program_path> | grep <address>を実行します(0xプレフィックスなし)
  • 関数名が表示されるはずです。

問題の関数が同じプログラム内にある場合にのみ機能します(動的にリンクされたライブラリなどにはありません)。

ロードされた共有ライブラリのロードアドレスを確認できる場合は、印刷された数値からアドレスを減算し、ライブラリでnmを使用して関数名を確認できます。

5
Calmarius

指すことができる関数のリストが大きすぎない場合、または関数の小さなグループが既に疑われる場合は、アドレスを出力して、実行中に使用されたものと比較できます。例:

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

このアプローチは、静的関数でも機能します。

2
givanse

直接指示することはできませんが、必要に応じてこの問題に別のアプローチを実装できます。構造体ポインタを作成して、代わりに関数と任意の値に設定できる説明文字列を指すようにすることができます。これらの変数を永久に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
2
eaanon01
  1. kallsyms_lookup_name()を使用して、kallsyms_lookupのアドレスを見つけます。

  2. kallsyms_lookupを指す関数ポインターを使用して呼び出します。

1
WindChaser

一般的にそれを行う方法はありません。

対応するコードをDLL /共有ライブラリにコンパイルすると、すべてのエントリポイントを登録して、取得したポインターと比較できるはずです。まだ試していませんが、DLL /共有ライブラリの使用経験があり、動作するはずです。これは、クロスプラットフォームで動作するように実装することもできます。

デバッグシンボルを使用してコンパイルすることについて他の誰かが既に言及している場合、デバッガーが行うことと同様に、実行中のアプリケーションからこれらを分析する方法を見つけることができます。しかし、これは完全にプロプライエタリであり、ポータブルではありません。

1
mh.

質問がまさに求めているものではありませんが、ここで答えを読んだ後、私の同じ問題のこの解決策について:

_/**
* search methods */
static int starts(const char *str, const char *c);
static int fuzzy(const char *str, const char *c);

int (*search_method)(const char *, const char *);

/* asign the search_method and do other stuff */
[...]

printf("The search method is %s\n", search_method == starts ? "starts" : "fuzzy")
_

プログラムでこれが必要な場合は、XMacroで文字列とともにメソッド名を定義し、コードで#define X(name, str) ... #undef Xを使用して、関数名から対応する文字列を取得できます。

0
The Gramm

Visual Leak Detector をチェックして、コールスタック印刷がどのように機能するかを確認してください。ただし、これはWindowsを使用していることを前提としています。

0
Jim Buck