Come ottenere il nome della funzione dal puntatore della funzione in C?
-
20-08-2019 - |
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?
Soluzione
Questo non è direttamente possibile senza ulteriore assistenza.
Potresti:
-
mantiene una tabella nei puntatori della funzione di mappatura del programma ai nomi
-
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 il0x
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.
-
Usa
kallsyms_lookup_name()
per trovare l'indirizzo dikallsyms_lookup
. -
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 #.