2009-07-01 16 views
7

Quando uso gdb per eseguire il debug di un programma scritto in C, il comando disassemble mostra i codici e i relativi indirizzi nella segmentazione della memoria del codice. È possibile conoscere gli indirizzi di memoria in fase di runtime? Sto usando Ubuntu OS. Grazie.Trova l'indirizzo del codice del programma in fase di runtime?

[modifica] Per essere più specifico, lo dimostrerò con il seguente esempio.

#include <stdio.h> 

int main(int argc,char *argv[]){ 
    myfunction(); 
    exit(0); 
} 

Ora vorrei avere l'indirizzo del myFunction() nella segmentazione della memoria di codice quando eseguo il mio programma.

+0

Stai chiedendo di un disassemblatore di runtime incorporato nel tuo programma? –

+0

Cosa vuoi veramente? –

+0

Grazie a tutti. Ho una buona comprensione di questo argomento: D – wakandan

risposta

16

Sopra risposta è di gran lunga troppo complicata. Se il riferimento alla funzione è statico, in quanto è al di sopra , l'indirizzo è semplicemente il valore del nome del simbolo in un contesto puntatore:

void* myfunction_address = myfunction; 

Se si sta afferrando la funzione in modo dinamico da una libreria condivisa, allora il valore restituito da dlsym() (POSIX) o G etProcAddress() (windows) è anche l'indirizzo della funzione.

Si noti che il codice sopra può generare un avviso con alcuni compilatori, in quanto ISO C impedisce tecnicamente l'assegnazione tra codice e puntatori di dati (alcune architetture li mettono in spazi di indirizzi fisicamente distinti).

E alcuni pedanti si sottolineano che l'indirizzo restituito non è davvero garantito per essere l'indirizzo di memoria della funzione, è solo un valore unico che può essere paragonato per l'uguaglianza con gli altri puntatori a funzione e gli atti, quando viene chiamato , per trasferire il controllo alla funzione di cui tiene il puntatore. Ovviamente tutti i compilatori conosciuti lo implementano con un indirizzo di destinazione del ramo.

Infine, notare che "l'indirizzo" di una funzione è un po 'ambiguo. Se la funzione è stata caricata dinamicamente o è un riferimento esterno a un simbolo esportato, ciò che ottieni è generalmente un puntatore a qualche codice di correzione nel "PLT" (un termine Unix/ELF, sebbene il meccanismo PE/COFF su Windows sia simile) che poi salta alla funzione.

+0

Grazie mille per la tua fantastica risposta. Posso fare questa domanda in anticipo: come ottenere l'indirizzo di una specifica riga di codice? – wakandan

+2

Non c'è fortuna lì. I compilatori sono liberi di riordinare e ottimizzare il codice, quindi non esiste una singola regione di memoria che corrisponda a una determinata linea o espressione. I debugger possono fare un lavoro abbastanza ragionevole di ricostruzione di questo dalla tabella dei simboli e delle informazioni di debug nell'eseguibile, ma sfortunatamente si sta entrando in qualche voodoo profondo che non conosco a memoria. –

+0

Bella risposta, ben spiegato. Puoi anche aggirare il fatto che la conversione tra codice e puntatori di dati non è definita, applicando questo trucco (come fatto in "man dlsym" nella direzione opposta): void * p; * (void (**)()) & p = (void (*)()) & myfunction; o usando un sindacato. –

5

Per ottenere un backtrace, utilizzare execinfo.h come documentato in the GNU libc manual.

Ad esempio:

#include <execinfo.h> 
#include <stdio.h> 
#include <unistd.h> 


void trace_pom() 
{ 
    const int sz = 15; 
    void *buf[sz]; 

    // get at most sz entries 
    int n = backtrace(buf, sz); 

    // output them right to stderr 
    backtrace_symbols_fd(buf, n, fileno(stderr)); 

    // but if you want to output the strings yourself 
    // you may use char ** backtrace_symbols (void *const *buffer, int size) 
    write(fileno(stderr), "\n", 1); 
} 


void TransferFunds(int n); 

void DepositMoney(int n) 
{ 
    if (n <= 0) 
     trace_pom(); 
    else TransferFunds(n-1); 
} 


void TransferFunds(int n) 
{ 
    DepositMoney(n); 
} 


int main() 
{ 
    DepositMoney(3); 

    return 0; 
} 

compilato

 
gcc a.c -o a -g -Wall -Werror -rdynamic 

Secondo il sito citato:

Attualmente, il nome della funzione e l'offset essere ottenuti solo su sistemi che utilizzano l'ELF formato binario per programmi e librerie. Su altri sistemi, sarà presente solo l'indirizzo esadecimale restituito . Inoltre, potrebbe essere necessario passare ulteriori flag al linker su rendere i nomi delle funzioni disponibili per il programma. (Ad esempio, su sistemi che utilizzano GNU ld, è necessario passare (-rdynamic.)

uscita

 
./a(trace_pom+0xc9)[0x80487fd] 
./a(DepositMoney+0x11)[0x8048862] 
./a(TransferFunds+0x11)[0x8048885] 
./a(DepositMoney+0x21)[0x8048872] 
./a(TransferFunds+0x11)[0x8048885] 
./a(DepositMoney+0x21)[0x8048872] 
./a(TransferFunds+0x11)[0x8048885] 
./a(DepositMoney+0x21)[0x8048872] 
./a(main+0x1d)[0x80488a4] 
/lib/i686/cmov/libc.so.6(__libc_start_main+0xe5)[0xb7e16775] 
./a[0x80486a1] 
+0

Esiste un modo per estrarre la funzione solo dall'indirizzo di DepositMoney, il significato 0x8048872 dovrebbe essere l'unico output invece di stampare l'intero backtrace? – wakandan

+1

Ah, scusa, pensavo volessi un backtrace completo. –

8

Se si conosce il nome della funzione, prima esecuzione del programma, è sufficiente utilizzare

void * addr = myfunction; 

Se il nome della funzione viene dato in fase di esecuzione, una volta ho scritto una funzione per trovare l'indirizzo simbolo dinamicamente utilizzando la libreria BFD. Ecco il codice x86_64, è possibile ottenere l'indirizzo tramite find_symbol ("a.out", "myfunction") nell'esempio.

#include <bfd.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <type.h> 
#include <string.h> 

long find_symbol(char *filename, char *symname) 
{ 
    bfd *ibfd; 
    asymbol **symtab; 
    long nsize, nsyms, i; 
    symbol_info syminfo; 
    char **matching; 

    bfd_init(); 
    ibfd = bfd_openr(filename, NULL); 

    if (ibfd == NULL) { 
     printf("bfd_openr error\n"); 
    } 

    if (!bfd_check_format_matches(ibfd, bfd_object, &matching)) { 
     printf("format_matches\n"); 
    } 

    nsize = bfd_get_symtab_upper_bound (ibfd); 
    symtab = malloc(nsize); 
    nsyms = bfd_canonicalize_symtab(ibfd, symtab); 

    for (i = 0; i < nsyms; i++) { 
     if (strcmp(symtab[i]->name, symname) == 0) { 
      bfd_symbol_info(symtab[i], &syminfo); 
      return (long) syminfo.value; 
     } 
    } 

    bfd_close(ibfd); 
    printf("cannot find symbol\n"); 
} 
+0

Grazie, questo è quello che stavo cercando – Ulterior

+0

Scusate per il negromante; tuttavia, ho trovato questo veramente utile. Sai di un tutorial per aiutare con bfd? – SailorCire

3

A proposito di un commento in una risposta (ottenendo l'indirizzo di un'istruzione), si può usare questo molto brutto trucco

#include <setjmp.h> 

void function() { 
    printf("in function\n"); 
    printf("%d\n",__LINE__); 
    printf("exiting function\n"); 

} 

int main() { 
    jmp_buf env; 
    int i; 

    printf("in main\n"); 
    printf("%d\n",__LINE__); 
    printf("calling function\n"); 
    setjmp(env); 
    for (i=0; i < 18; ++i) { 
     printf("%p\n",env[i]); 
    }  
    function(); 
    printf("in main again\n"); 
    printf("%d\n",__LINE__); 

} 

Dovrebbe essere env [12] (EIP), ma essere attento come sembra dipendente dalla macchina, quindi controlla la mia parola. Questa è l'uscita

in main 
13 
calling function 
0xbfff037f 
0x0 
0x1f80 
0x1dcb 
0x4 
0x8fe2f50c 
0x0 
0x0 
0xbffff2a8 
0xbffff240 
0x1f 
0x292 
0x1e09 
0x17 
0x8fe0001f 
0x1f 
0x0 
0x37 
in function 
4 
exiting function 
in main again 
37 

buon divertimento!

Problemi correlati