Wie bekomme ich den Namen der Funktion aus dem Zeiger der Funktion im Linux-Kernel?

Lesezeit: 7 Minuten

Benutzeravatar von Daniel Silveira
Daniel Silveira

So erhalten Sie den Namen der Funktion Zeiger der Funktion in C?

Bearbeiten: Der eigentliche 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 untersuchen. Aber ich weiß nicht, auf welche Funktion es zeigt. Ich dachte, es könnte getan werden, weil es bei einem Systemausfall (Kernel-Panik) den aktuellen Callstack mit den Namen der Funktionen auf dem Bildschirm ausgibt. Aber ich schätze, ich habe mich geirrt … oder?

  • Vielleicht, wenn Sie erklären, warum Sie den Namen der Funktion brauchen, könnte jemand einen alternativen Weg vorschlagen, um das zu bekommen, was Sie brauchen.

    – Schlag

    8. Dezember 2008 um 23:01 Uhr

  • Etwas, das erwähnt und nicht weiter ausgeführt wird, ist die Verwendung von Debug-Symbolen. Wie auch immer Sie dies zum Laufen bringen, das Ergebnis wird mit ziemlicher Sicherheit Suchvorgänge in einer Quelle von Debug-Symbolen durchführen … wie in einer Antwort erwähnt, ist libdwarf wahrscheinlich der richtige Weg für Linux-Kernel-Dinge.

    – jheriko

    22. Juni 2012 um 14:21 Uhr

  • C++ Userland: stackoverflow.com/questions/40706805/…

    – Ciro Santilli OurBigBook.com

    2. März 2020 um 14:57 Uhr


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

Ich kenne mindestens zwei Möglichkeiten, dies zu erreichen.

Es gibt GNU-Funktionen für das Backtrace-Drucken: backtrace() und backtrace_symbols() (Sehen man). In Ihrem Fall brauchen Sie nicht backtrace() Da Sie bereits einen Funktionszeiger haben, ü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 den Binärnamen, den Funktionsnamen, den Zeiger-Offset vom Funktionsstart und den Zeigerwert, damit Sie ihn analysieren können.

Eine andere Möglichkeit ist die Verwendung dladdr() (eine andere Erweiterung), denke ich print_backtrace() Verwendet dladdr(). dladdr() kehrt zurück Dl_info Struktur, die den Funktionsnamen enthält dli_sname aufstellen. Ich gebe hier kein Codebeispiel an, aber es ist offensichtlich – siehe man dladdr für Details.

ACHTUNG! Beide Ansätze erfordern, dass die Funktion nicht statisch ist!

Nun, es gibt noch einen weiteren Weg – Verwenden Sie Debug-Informationen mit libdwarf aber es würde eine nicht gestrippte Binärdatei erfordern und ist nicht sehr einfach, daher empfehle ich es nicht.

  • Für den Nicht-Kernel-Fall sollte die dladdr-Methode die akzeptierte Antwort sein. Sie sollten es von den Backtrace-Sachen trennen.

    – Todd befreit

    15. Oktober 2016 um 20:13 Uhr

Benutzeravatar von Alnitak
Alnitak

Ohne zusätzliche Hilfsmittel ist das nicht direkt möglich.

Sie könnten:

  1. Pflegen Sie eine Tabelle in Ihrem Programm, die Funktionszeiger auf Namen abbildet

  2. Untersuchen Sie die Symboltabelle der ausführbaren Datei, falls vorhanden.

Letzteres ist jedoch hart und nicht tragbar. Die Methode hängt vom Binärformat des Betriebssystems (ELF, a.out, .exe usw.) und auch von jeder vom 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 verfügbar in /proc/kallsymsund 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 Debug-Zwecke wird letzteres wahrscheinlich genau das tun, was Sie brauchen – es nimmt die Adresse, formatiert sie und sendet sie an printkoder Sie können verwenden printk mit dem %pF Formatbezeichner.

  • Ich glaube nicht, dass ich kallsyms_lookup von einem Kernelmodul aufrufen kann. Beim Kompilieren bekomme ich “kallsyms_lookup undefined”

    – Daniel Silveira

    9. Dezember 2008 um 15:03 Uhr

  • Wenn Sie einen Kompilierzeitfehler erhalten, müssen Sie sicherstellen, dass Sie die Kernel-Header verfügbar und in Ihrem Include-Pfad haben.

    – Alnitak

    9. Dezember 2008 um 17:44 Uhr

  • Ich erhalte einen Verbindungszeitfehler. Die Überschriften sind in Ordnung. #include

    – Daniel Silveira

    9. Dezember 2008 um 18:24 Uhr

  • ok, das deutet darauf hin, dass Ihre Modulkompilierung irgendwo falsch ist. Module müssen Symbole aufrufen, die sich im Kernel befinden, daher kann das Symbol per Definition nicht vollständig zur Verbindungszeit aufgelöst werden.

    – Alnitak

    9. Dezember 2008 um 19:30 Uhr

  • ps-Beispiel-Makefiles zum Kompilieren von Linux-Kernel-Modulen ist wahrscheinlich ein guter Kandidat für eine andere Frage. Mein Referenzmaterial dafür ist bei der Arbeit, wo ich es im Moment nicht bekommen kann.

    – Alnitak

    9. Dezember 2008 um 19:35 Uhr

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

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

  • Was bedeutet %pF?

    – Qi Zhang

    6. Juli 2016 um 18:28 Uhr

  • @QiZhang es ist eine Erweiterung der %p Formatbezeichner, der zum Drucken von Rohzeigeradressen verwendet wird. Dieser Bezeichner gibt Funktionsnamen aus. sehen die printk-Kernel-Dokumentation Für mehr Information.

    – Woodrow Barlow

    12. Juli 2016 um 19:35 Uhr

  • Siehe auch: stackoverflow.com/questions/3899870/print-call-stack-in-c-or-c

    – Ciro Santilli OurBigBook.com

    21. November 2019 um 17:19 Uhr

Benutzeravatar von Calmarius
Calmarius

Folgendes funktioniert bei mir unter Linux:

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

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

Wenn Sie die Ladeadressen der geladenen gemeinsam genutzten Bibliotheken herausfinden können, können Sie die Adresse von der gedruckten Zahl subtrahieren und nm für die Bibliothek verwenden, um den Funktionsnamen herauszufinden.

Benutzeravatar von eaanon01
eaanon01

Sie können 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, sowie eine beschreibende Zeichenfolge, die Sie beliebig festlegen können. Ich habe auch eine Debugging-Möglichkeit hinzugefügt, da Sie wahrscheinlich nicht wollen, dass diese Variablen für immer gedruckt werden.

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

Wenn die Liste der Funktionen, auf die gezeigt werden kann, nicht zu groß ist oder Sie bereits eine kleine Gruppe von Funktionen vermuten, können Sie die Adressen ausdrucken und mit der während der Ausführung verwendeten 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.

Benutzeravatar von mh
mh.

Es gibt keine Möglichkeit, wie man es allgemein macht.

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

Jemand anderes hat bereits erwähnt, mit Debug-Symbolen zu kompilieren, dann könnten Sie versuchen, einen Weg zu finden, diese aus der laufenden Anwendung zu analysieren, ähnlich wie es ein Debugger tun würde. Aber das ist absolut proprietär und nicht portabel.

1414560cookie-checkWie bekomme ich den Namen der Funktion aus dem Zeiger der Funktion im Linux-Kernel?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy