Domanda

Come ottenere il nome della funzione da puntatore della funzione in C?

Modificare:Il caso reale è:Sto scrivendo un modulo del kernel Linux e sto chiamando le funzioni del kernel.Alcune di queste funzioni sono puntatori e voglio controllare il codice di quella funzione nel sorgente del kernel.Ma non so a quale funzione stia puntando.Ho pensato che si potesse fare perché, quando il sistema fallisce (panico del kernel) stampa sullo schermo lo stack di chiamate corrente con i nomi delle funzioni.Ma immagino di essermi sbagliato...lo sono?

È stato utile?

Soluzione

Questo non è direttamente possibile senza ulteriore assistenza.

Potresti:

  1. mantiene una tabella nei puntatori della funzione di mappatura del programma ai nomi

  2. esamina la tabella dei simboli dell'eseguibile, se ne ha una.

Quest'ultimo, tuttavia, è difficile e non è portatile. Il metodo dipenderà dal formato binario del sistema operativo (ELF, a.out, .exe, ecc.) E anche da qualsiasi trasferimento effettuato dal linker.

EDIT: poiché ora hai spiegato qual è il tuo caso d'uso reale, la risposta non è poi così difficile. La tabella dei simboli del kernel è disponibile in /proc/kallsyms e c'è un'API per accedervi:

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

Per semplici scopi di debug, quest'ultimo probabilmente farà esattamente ciò di cui hai bisogno: prende l'indirizzo, lo formatta e lo invia a printk oppure puoi utilizzare %pF con l'identificatore di formato <=>.

Altri suggerimenti

Sono sorpreso dal motivo per cui tutti dicono che non è possibile. È possibile su Linux per funzioni non statiche.

Conosco almeno due modi per raggiungere questo obiettivo.

Esistono funzioni GNU per la stampa backtrace: backtrace() e backtrace_symbols() (Vedi man). Nel tuo caso non hai bisogno di gcc test.c -rdynamic poiché hai già il puntatore a funzione, lo passi semplicemente a ./a.out(foo+0x0)[0x8048634].

Esempio (codice di lavoro):

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

Compila con dladdr()

Output: print_backtrace()

Ti dà il nome binario, il nome della funzione, l'offset del puntatore dall'inizio della funzione e il valore del puntatore in modo da poterlo analizzare.

Un altro modo è usare Dl_info (un'altra estensione), immagino che dli_sname usi man dladdr. libdwarf restituisce <=> la struttura che ha il nome della funzione nel campo <=>. Non fornisco un esempio di codice qui, ma è ovvio - vedi <=> per dettagli.

NB! Entrambi gli approcci richiedono che la funzione non sia statica!

Bene, c'è un altro modo: usa le informazioni di debug usando <=> ma richiederebbe binari non spogliati e non molto facili da fare, quindi non lo consiglio.

Nel kernel di Linux, puoi usare direttamente "% pF " formato di printk!

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

Quanto segue mi funziona su Linux:

  • printf l'indirizzo della funzione utilizzando %p
  • Quindi fai un nm <program_path> | grep <address> (senza il 0x prefisso)
  • Dovrebbe mostrarti il ​​nome della funzione.

Funziona solo se la funzione in questione si trova nello stesso programma (non in una libreria collegata dinamicamente o qualcosa del genere).

Se riesci a trovare gli indirizzi di caricamento delle librerie condivise caricate, puoi sottrarre l'indirizzo dal numero stampato e utilizzare nm sulla libreria per scoprire il nome della funzione.

Non puoi diete, ma puoi implementare un approccio diverso a questo problema, se lo desideri. Puoi invece creare un puntatore a una struttura che punta a una funzione e a una stringa descrittiva che puoi impostare su ciò che desideri. Ho anche aggiunto una posa di debug poiché probabilmente non vuoi che questi vars vengano stampati per sempre.

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

Se l'elenco delle funzioni che è possibile indicare non è troppo grande o se si sospetta già un piccolo gruppo di funzioni, è possibile stampare gli indirizzi e confrontarli con quelli utilizzati durante l'esecuzione. Es:

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

Output:

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

Questo approccio funzionerà anche per le funzioni statiche.

Non c'è modo di farlo in generale.

Se compili il codice corrispondente in una DLL / Libreria condivisa, dovresti essere in grado di elencare tutti i punti di ingresso e confrontarli con il puntatore che hai. Non l'ho ancora provato, ma ho qualche esperienza con DLL / Lib condivise e mi aspetto che funzioni. Questo potrebbe persino essere implementato per funzionare in modo multipiattaforma.

Qualcun altro ha già detto di compilare con i simboli di debug, quindi potresti provare a trovare un modo per analizzarli dall'applicazione in esecuzione, simile a quello che farebbe un debugger. Ma questo è assolutamente proprietario e non portatile.

  1. Usa kallsyms_lookup_name() per trovare l'indirizzo di kallsyms_lookup.

  2. Utilizza un puntatore a funzione che punta a <=>, per chiamarlo.

Dai un'occhiata a Visual Leak Detector per vedere come ottengono la stampa del loro callstack lavoro. Ciò presuppone che tu stia utilizzando Windows.

Non è esattamente quello che la domanda chiede ma dopo aver letto le risposte qui Ho pensato a questa soluzione a un mio problema simile:

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

Se il tuo programma ne ha molto bisogno, puoi definire i nomi dei metodi insieme a una stringa in un XMacro e usare #define X(name, str) ... #undef X nel codice per ottenere la stringa corrispondente dal nome della funzione.

Non puoi. Il nome della funzione non è associato alla funzione quando viene compilato e collegato. È tutto per indirizzo di memoria a quel punto, non per nome.

Non sapresti come sembri senza uno specchio riflettente. Dovrai usare un linguaggio capace di riflettere come C #.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top