2009-07-01 11 views
7

Cuando uso gdb para depurar un programa escrito en C, el comando desmontar muestra los códigos y sus direcciones en la segmentación de la memoria de código. ¿Es posible conocer esas direcciones de memoria en tiempo de ejecución? Estoy usando Ubuntu OS. Gracias.¿Encontrar la dirección de código del programa en tiempo de ejecución?

[edit] Para ser más específicos, lo demostraré con el siguiente ejemplo.

#include <stdio.h> 

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

Ahora me gustaría tener la dirección de mifuncion() en la segmentación de memoria de código cuando corro mi programa.

+0

¿Está preguntando acerca de un desensamblador en tiempo de ejecución integrado en su programa? –

+0

¿Qué es lo que realmente quieres? –

+0

Gracias a todos. Tengo una buena comprensión de este asunto: D – wakandan

Respuesta

16

Por encima de respuesta es muy complicada demasiado. Si la referencia de funciones es estático, ya que está por encima , la dirección es simplemente el valor del nombre del símbolo en el contexto del puntero:

void* myfunction_address = myfunction; 

Si está agarrando la función dinámica de una biblioteca compartida, entonces el valor devuelto por dlsym() (POSIX) o G etProcAddress() (windows) es también la dirección de la función.

Tenga en cuenta que es probable que el código anterior genere una advertencia con algunos compiladores, ya que ISO C prohíbe técnicamente la asignación entre códigos y punteros de datos (algunas arquitecturas los colocan en espacios de direcciones físicamente distintos).

Y algunos pedantes se señalan que la dirección devuelta no es realmente garantiza que sea la dirección de memoria de la función, es sólo un valor único que se puede comparar a la igualdad con otros punteros de función y actos, cuando se le llama , para transferir el control a la función cuyo puntero contiene. Obviamente, todos los compiladores conocidos implementan esto con una dirección de destino de sucursal.

Y, por último, tenga en cuenta que la "dirección" de una función es un poco ambigua. Si la función se cargó dinámicamente o es una referencia externa a un símbolo exportado, lo que realmente se obtiene es generalmente un puntero a algún código de corrección en el "PLT" (un término Unix/ELF, aunque el mecanismo PE/COFF en Windows es similar) que luego salta a la función.

+0

Muchas gracias por su fantástica respuesta. ¿Puedo hacer esta pregunta con anticipación? ¿Qué tal obtener la dirección de una línea de código específica? – wakandan

+2

No hubo suerte allí. Los compiladores pueden volver a generar y optimizar el código, de modo que no hay una sola región de memoria que corresponda a una línea o expresión determinada. Los depuradores pueden hacer un trabajo bastante razonable al reconstruir esto desde la tabla de símbolos y depurar la información en el ejecutable, pero desafortunadamente ahí se está metiendo en algún vudú profundo que no conozco de manera directa. –

+0

Buena respuesta, bien explicada. También puede evitar el hecho de que la conversión entre el código y los punteros de datos no está definida, al aplicar este truco (como se hace en "man dlsym" en la dirección inversa): void * p; * (void (**)()) & p = (void (*)()) & myfunction; o al usar una unión. –

5

Para obtener una traza inversa, use execinfo.h según lo documentado in the GNU libc manual.

Por ejemplo:

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

compilado

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

De acuerdo con la página web mencionada:

la actualidad, el nombre de la función y el desplazamiento sólo puede ser obtenido en sistemas que utilizan la ELF formato binario para programas y bibliotecas. En otros sistemas, solo estará presente la dirección de retorno hexadecimal . Además, es posible que deba pasar indicadores adicionales al vinculador al para que los nombres de las funciones estén disponibles para el programa. (Por ejemplo, en sistemas que utilizan GNU ld, debe pasar (-rdynamic.)

salida

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

¿Hay alguna manera de que pueda extraer la dirección de la función DepositMoney sola, significando que 0x8048872 debería ser la única salida en lugar de imprimir todo el backtrace? – wakandan

+1

Ah, lo siento, pensé que querías un rastreo completo. –

8

Si conoce el nombre de la función antes de que acabe el programa, sólo tiene que utilizar

void * addr = myfunction; 

Si el nombre de la función se da en tiempo de ejecución, una vez escribí una función para averiguar la dirección símbolo dinámicamente utilizando la librería BFD. Aquí está el código x86_64, puede obtener la dirección a través de find_symbol ("a.out", "myfunction") en el ejemplo.

#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

Gracias, esto es lo que estaba buscando – Ulterior

+0

Perdón por la nigromancia; sin embargo, encontré esto realmente útil. ¿Conoces un tutorial para ayudar con bfd? – SailorCire

3

Sobre un comentario en una respuesta (obtener la dirección de una instrucción), puede utilizar esta muy feo truco

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

} 

Debe ser env [12] (EIP), pero tenga cuidadoso ya que parece depender de la máquina, así que compruebe mi palabra triple. Esta es la salida

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 

have fun!

Cuestiones relacionadas