webentwicklung-frage-antwort-db.com.de

Wie bekomme ich den Funktionsnamen vom Funktionszeiger in C?

Wie bekomme ich den Funktionsnamen von Funktionszeiger in C?

Edit: Der wahre Fall ist: Ich schreibe ein Linux-Kernel-Modul und rufe Kernel-Funktionen auf. Einige dieser Funktionen sind Zeiger, und ich möchte den Code dieser Funktion in der Kernelquelle überprüfen. Aber ich weiß nicht, auf welche Funktion es zeigt. Ich dachte, es könnte gemacht werden, weil, wenn das System ausfällt (Kernel Panic), es den aktuellen Callstack mit Funktionsnamen auf dem Bildschirm ausgibt. Aber ich schätze, ich habe mich geirrt ... oder?

52
Daniel Silveira

Das ist ohne zusätzliche Hilfe nicht direkt möglich.

Du könntest:

  1. pflegen Sie eine Tabelle in Ihrem Programm, die Funktionszeiger auf Namen zeigt

  2. Überprüfen Sie die Symboltabelle der ausführbaren Datei, falls vorhanden.

Letzteres ist jedoch hart und nicht tragbar. Die Methode hängt vom binären Format des Betriebssystems (ELF, a.out, .exe usw.) und auch von jeder durch den Linker vorgenommenen Verschiebung ab.

EDIT: Da Sie jetzt erklärt haben, was Ihr wirklicher Anwendungsfall ist, ist die Antwort eigentlich nicht so schwer. Die Kernel-Symboltabelle ist in /proc/kallsyms verfügbar, und es gibt eine API für den Zugriff darauf:

#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)

Für einfache Debugging-Zwecke wird Letzteres wahrscheinlich genau das tun, was Sie benötigen - er nimmt die Adresse, formatiert sie und sendet sie an printk, oder Sie können printk mit dem Formatbezeichner %pF verwenden.

29
Alnitak

Ich bin überrascht, warum alle sagen, dass dies nicht möglich ist. Es ist unter Linux für nicht statische Funktionen möglich.

Ich kenne mindestens zwei Wege, um dies zu erreichen.

Es gibt GNU - Funktionen für den Rückverfolgungsdruck: backtrace() und backtrace_symbols() (siehe man). In Ihrem Fall benötigen Sie backtrace() nicht, da Sie bereits über einen Funktionszeiger verfügen. Übergeben Sie ihn einfach an backtrace_symbols().

Beispiel (Arbeitscode):

#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;
}

Kompilieren mit gcc test.c -rdynamic

Ausgabe: ./a.out(foo+0x0)[0x8048634]

Es gibt Ihnen binären Namen, Funktionsnamen, Zeigerversatz vom Funktionsstart und Zeigerwert, damit Sie ihn analysieren können.

Eine andere Möglichkeit ist die Verwendung von dladdr() (einer anderen Erweiterung). Ich denke, print_backtrace() verwendet dladdr(). dladdr() gibt eine Dl_info-Struktur mit einem Funktionsnamen im dli_sname-Feld zurück. Ich gebe hier kein Codebeispiel, aber es ist offensichtlich - siehe man dladdr für Details.

NB! Bei beiden Ansätzen muss die Funktion nicht statisch sein!

Nun, es gibt noch eine weitere Möglichkeit: Verwenden Sie Debug-Informationen mit libdwarf. Dies würde jedoch eine nicht gestrippte Binärdatei erfordern, die nicht sehr einfach ist. Ich empfehle es nicht.

62
qrdl

Im Linux-Kernel können Sie direkt das "% pF" Format von printk verwenden!

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

Folgendes funktioniert mir unter Linux:

  • printf die Adresse der Funktion mit %p
  • Dann mache einen nm <program_path> | grep <address> (ohne das 0x-Präfix)
  • Es sollte Ihnen den Funktionsnamen zeigen. 

Sie funktioniert nur, wenn sich die betreffende Funktion im selben Programm befindet (nicht in einer dynamisch verknüpften Bibliothek oder in etwas anderem).

Wenn Sie die Ladeadressen der geladenen gemeinsam genutzten Bibliotheken herausfinden können, können Sie die Adresse von der gedruckten Nummer subtrahieren und mit nm in der Bibliothek den Funktionsnamen ermitteln.

5
Calmarius

Wenn die Liste der Funktionen, auf die verwiesen werden kann, nicht zu groß ist oder wenn Sie bereits eine kleine Gruppe von Funktionen vermuten, können Sie die Adressen drucken und mit der während der Ausführung verwendeten Adresse vergleichen. Ex:

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);
    }
}

Ausgabe:

f1 0x40051b
f2 0x400521
f3 0x400527
0x400521
function name: f2

Dieser Ansatz funktioniert auch für statische Funktionen.

2
givanse

Sie können es nicht direkt, aber Sie können einen anderen Ansatz für dieses Problem implementieren, wenn Sie möchten. Sie können einen Strukturzeiger erstellen, der stattdessen auf eine Funktion zeigt, und auch eine beschreibende Zeichenfolge, die Sie beliebig setzen können .. __ Ich habe auch eine Debugging-Option hinzugefügt, da diese Variablen wahrscheinlich nicht für immer gedruckt werden sollen.

// 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

Im Allgemeinen gibt es keine Möglichkeit, dies zu tun.

Wenn Sie den entsprechenden Code in eine DLL/Shared Library kompilieren, sollten Sie in der Lage sein, alle Einstiegspunkte zu registrieren und mit dem vorhandenen Zeiger zu vergleichen. Ich habe es noch nicht ausprobiert, aber ich habe etwas Erfahrung mit DLLs/Shared Libs und würde erwarten, dass es funktioniert. Dies könnte sogar implementiert werden, um plattformübergreifend zu arbeiten.

Jemand, der bereits mit Debug-Symbolen kompiliert wurde, könnte versuchen, diese aus der laufenden Anwendung heraus zu analysieren, ähnlich wie ein Debugger dies tun würde .. __ Dies ist jedoch absolut proprietär und nicht portabel.

1
mh.
  1. Verwenden Sie kallsyms_lookup_name(), um die Adresse von kallsyms_lookup zu finden.

  2. Verwenden Sie einen Funktionszeiger, der auf kallsyms_lookup zeigt, um ihn aufzurufen.

0
WindChaser

Nicht genau das, wonach die Frage fragt, aber nachdem ich die Antworten hier gelesen habe, habe ich über diese Lösung für ein ähnliches Problem nachgedacht:

/**
* 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")

Wenn Ihr Programm dies häufig benötigt, können Sie die Methodennamen zusammen mit einer Zeichenfolge in einem XMacro definieren und #define X(name, str) ... #undef X im Code verwenden, um die entsprechende Zeichenfolge aus dem Funktionsnamen abzurufen.

0
The Gramm

Check out Visual Leak Detector , um zu sehen, wie sie ihren Callstack zum Drucken bringen. Dies setzt jedoch voraus, dass Sie Windows verwenden.

0
Jim Buck