2013-09-08 14 views
5

Nella pagina man, la funzione backtrace() su Linux dice:Alternativa a backtrace() su Linux che può trovare i simboli per le funzioni statiche

Nota che i nomi delle funzioni "statiche" non sono esposti, e ha vinto essere disponibile nel backtrace.

Tuttavia, con simboli di debug abilitati (-g), programmi come addr2line e gdb possono ancora ottenere i nomi delle funzioni statiche. C'è un modo per ottenere i nomi delle funzioni statiche a livello di codice all'interno del processo stesso?

risposta

2

Sì, esaminando il proprio eseguibile (/proc/self/exe) utilizzando ad es. libbfd o una libreria di analisi dei file ELF, per analizzare i simboli stessi. In sostanza, devi scrivere codice C che fa l'equivalente di qualcosa come

env LANG=C LC_ALL=C readelf -s executable | awk '($5 == "LOCAL" && $8 ~ /^[^_]/ && $8 !~ /\./)' 

Per quanto ne so, l'interfaccia linker dinamico in Linux (<dlfcn.h>) non restituisce gli indirizzi per la statica simboli (locali).

Un approccio semplice e piuttosto robusto è quello di eseguire readelf o objdump dal programma. Si noti che non è possibile assegnare il percorso dello pseudo file a /proc/self/exe, poiché si riferisce sempre al proprio eseguibile del processo. Invece, devi usare ad es. realpath("/proc/self/exe", NULL) per ottenere un percorso assoluto assegnato dinamicamente all'eseguibile corrente che è possibile fornire al comando. Inoltre, si desidera garantire che l'ambiente contenga LANG=C e LC_ALL=C, in modo che l'output del comando sia facilmente analizzabile (e non localizzato in qualsiasi lingua l'utente corrente preferisca). Questo potrebbe sembrare un po 'complicato, ma richiede solo il pacchetto binutils da installare, e non è necessario aggiornare il programma o la libreria per stare al passo con gli ultimi sviluppi, quindi penso che sia nel complesso un buon approccio .

Vuoi un esempio?

Un modo per semplificare è generare array separati con le informazioni sui simboli in fase di compilazione. In sostanza, dopo che i file oggetto vengono generati, un file di origine separato viene generato dinamicamente eseguendo objdump o readelf sui file oggetto correlato, generando una serie di nomi e puntatori simili a

const struct { 
    const char *const name; 
    const void *const addr; 
} local_symbol_names[] = { 
    /* Filled in using objdump or readelf and awk, for example */ 
    { NULL, NULL } 
}; 

magari con una semplice funzione di ricerca esportata in un file di intestazione, in modo che quando l'eseguibile finale è collegato, può accedere facilmente ed efficientemente alla matrice di simboli locali.

Duplica alcuni dati, poiché le stesse informazioni sono già nel file eseguibile e, se non ricordo male, devi prima collegare l'eseguibile finale con una matrice stub per ottenere gli indirizzi effettivi per i simboli, e poi ricollegarsi con l'array di simboli, rendendolo un po 'complicato in fase di compilazione .. Ma evita di avere una dipendenza da run-time su binutils.

3

Se il file eseguibile (e le librerie collegate) sono compilati con informazioni di debug (ad es.con -g bandiera per gcc o g++) allora si potrebbe utilizzare di Ian Taylor libbacktrace (annunciato here) dall'interno GCC - vedere il suo codice here

quella libreria (BSD licenza software libero) sta usando DWARF informazioni di debug da eseguibili e librerie condivise collegato dal processo. Vedi il suo file README.

Attenzione che se si compila con ottimizzazioni, alcune funzioni potrebbero essere inline (anche senza essere esplicitamente contrassegnate con inline nel codice sorgente e le funzioni inline static potrebbero non avere un proprio codice proprio). Quindi il backtracing non dirà molto su di loro.