2011-09-10 6 views
15

Necesito obtener la dirección de inicio y final de la sección de texto de un ejecutable. ¿Cómo puedo obtenerlo?Obtener la dirección de inicio y final de la sección de texto en un ejecutable

Puedo obtener la dirección inicial desde el símbolo _init o el símbolo _start, pero ¿qué ocurre con la dirección final? ¿Debería considerar la dirección final de la sección text como la última dirección antes de comenzar la sección .rodata?

¿O debo editar la secuencia de comandos ld predeterminada y agregar mis propios símbolos para indicar el inicio y el final de la sección de texto, y pasarla a GCC al compilar? En este caso, ¿dónde colocaré los nuevos símbolos, consideraré la sección init y fini?

¿Cuál es una buena forma de obtener la dirección inicial y final de la sección de texto?

+0

prueba 'readelf -h' para ver la información del encabezado del duende, donde se proporciona el desplazamiento del archivo de encabezado del programa. –

+0

Lo mismo para la sección de datos: http://stackoverflow.com/questions/1765969/unable-to-locate-definition-of-etext-edata-end –

Respuesta

19

Los scripts de linkut predeterminados de binutils de GNU para plataformas basadas en ELF normalmente definen un buen número de símbolos diferentes que se pueden usar para buscar el inicio y el final de varias secciones.

El final de la sección de texto generalmente se referencia mediante una selección de tres símbolos diferentes: etext, _etext o __etext; el comienzo se puede encontrar como __executable_start. (Tenga en cuenta que estos símbolos son generalmente exportados utilizando el mecanismo de PROVIDE(), lo que significa que van a ser anulados si algo más en su ejecutable define ellos en lugar de simplemente referencia ellos. En particular, esto significa que _etext o __etext es probable que sean opciones más seguras que etext)

Ejemplo:.

$ cat etext.c 
#include <stdio.h> 

extern char __executable_start; 
extern char __etext; 

int main(void) 
{ 
    printf("0x%lx\n", (unsigned long)&__executable_start); 
    printf("0x%lx\n", (unsigned long)&__etext); 
    return 0; 
} 
$ gcc -Wall -o etext etext.c 
$ ./etext 
0x8048000 
0x80484a0 
$ 

no creo que ninguno de estos símbolos se especifican cualquier punto de vista, por lo que no se debe asumir para ser portátil (no tengo ni idea si incluso el bin de GNU utils los proporciona para todos plataformas basadas en ELF, o si el conjunto de símbolos proporcionados ha cambiado en diferentes versiones de binutils), aunque supongo que si a) usted está haciendo algo que necesita esta información, yb) usted está considerando piratear scripts de enlazadores como una opción, ¡entonces la portabilidad no es una gran preocupación!

Para ver el conjunto exacto de los símbolos que se obtiene cuando la construcción de una cosa en particular en una plataforma en particular, dar a la bandera --verbose a ld (o -Wl,--verbose a gcc) para imprimir el guión enlazador que elige utilizar (en realidad hay varios diferentes scripts de enlazador predeterminados, que varían según las opciones del enlazador y el tipo de objeto que está creando).

+0

¿cuál sería una mejor opción entonces? piratear el script del enlazador e insertar mis propios símbolos? – phoxis

+1

No, si estos símbolos funcionan en su plataforma, también podría usarlos. El código de ejemplo anterior funciona al menos en Linux x86, Linux ppc y NetBSD x86. Simplemente no sé si hay otras plataformas en las que no funcionará. (Una secuencia de comandos de enlazador hackeada es * menos * portátil: una secuencia de comandos de enlazador de Linux x86 hackeada casi con certeza no funcionará en Linux ppc, por ejemplo). –

2

.rodata no se garantiza que siempre venga directamente después de .text. Puede usar objdump -h file y readelf --sections file para obtener más información. Con objdump, obtienes tanto el tamaño como el desplazamiento en el archivo.

2

Para Linux, considere utilizar la herramienta nm(1) para inspeccionar los símbolos que proporciona el archivo objeto. Puede elegir entre este conjunto de símbolos, donde puede aprender los dos símbolos que Matthew Slattery proporcionó en su respuesta.

7

Es incorrecto hablar de "el" segmento de texto, ya que puede haber más de uno (garantizado para el caso habitual cuando tiene bibliotecas compartidas, pero aún es posible que un solo binario ELF tenga múltiples PT_LOAD secciones con el las mismas banderas de todos modos).

El siguiente programa de ejemplo vacía toda la información devuelta por dl_iterate_phr. Está interesado en cualquier segmento del tipo PT_LOAD con el indicador PF_X (tenga en cuenta que PT_GNU_STACK incluirá la marca si -z execstack se pasa al vinculador, por lo que realmente debe verificar ambos).

#define _GNU_SOURCE 
#include <link.h> 
#include <stddef.h> 
#include <stdio.h> 
#include <stdlib.h> 

const char *type_str(ElfW(Word) type) 
{ 
    switch (type) 
    { 
    case PT_NULL: 
     return "PT_NULL"; // should not be seen at runtime, only in the file! 
    case PT_LOAD: 
     return "PT_LOAD"; 
    case PT_DYNAMIC: 
     return "PT_DYNAMIC"; 
    case PT_INTERP: 
     return "PT_INTERP"; 
    case PT_NOTE: 
     return "PT_NOTE"; 
    case PT_SHLIB: 
     return "PT_SHLIB"; 
    case PT_PHDR: 
     return "PT_PHDR"; 
    case PT_TLS: 
     return "PT_TLS"; 
    case PT_GNU_EH_FRAME: 
     return "PT_GNU_EH_FRAME"; 
    case PT_GNU_STACK: 
     return "PT_GNU_STACK"; 
    case PT_GNU_RELRO: 
     return "PT_GNU_RELRO"; 
    case PT_SUNWBSS: 
     return "PT_SUNWBSS"; 
    case PT_SUNWSTACK: 
     return "PT_SUNWSTACK"; 
    default: 
     if (PT_LOOS <= type && type <= PT_HIOS) 
     { 
      return "Unknown OS-specific"; 
     } 
     if (PT_LOPROC <= type && type <= PT_HIPROC) 
     { 
      return "Unknown processor-specific"; 
     } 
     return "Unknown"; 
    } 
} 

const char *flags_str(ElfW(Word) flags) 
{ 
    switch (flags & (PF_R | PF_W | PF_X)) 
    { 
    case 0 | 0 | 0: 
     return "none"; 
    case 0 | 0 | PF_X: 
     return "x"; 
    case 0 | PF_W | 0: 
     return "w"; 
    case 0 | PF_W | PF_X: 
     return "wx"; 
    case PF_R | 0 | 0: 
     return "r"; 
    case PF_R | 0 | PF_X: 
     return "rx"; 
    case PF_R | PF_W | 0: 
     return "rw"; 
    case PF_R | PF_W | PF_X: 
     return "rwx"; 
    } 
    __builtin_unreachable(); 
} 

static int callback(struct dl_phdr_info *info, size_t size, void *data) 
{ 
    int j; 
    (void)data; 

    printf("object \"%s\"\n", info->dlpi_name); 
    printf(" base address: %p\n", (void *)info->dlpi_addr); 
    if (size > offsetof(struct dl_phdr_info, dlpi_adds)) 
    { 
     printf(" adds: %lld\n", info->dlpi_adds); 
    } 
    if (size > offsetof(struct dl_phdr_info, dlpi_subs)) 
    { 
     printf(" subs: %lld\n", info->dlpi_subs); 
    } 
    if (size > offsetof(struct dl_phdr_info, dlpi_tls_modid)) 
    { 
     printf(" tls modid: %zu\n", info->dlpi_tls_modid); 
    } 
    if (size > offsetof(struct dl_phdr_info, dlpi_tls_data)) 
    { 
     printf(" tls data: %p\n", info->dlpi_tls_data); 
    } 
    printf(" segments: %d\n", info->dlpi_phnum); 

    for (j = 0; j < info->dlpi_phnum; j++) 
    { 
     const ElfW(Phdr) *hdr = &info->dlpi_phdr[j]; 
     printf(" segment %2d\n", j); 
     printf("  type: 0x%08X (%s)\n", hdr->p_type, type_str(hdr->p_type)); 
     printf("  file offset: 0x%08zX\n", hdr->p_offset); 
     printf("  virtual addr: %p\n", (void *)hdr->p_vaddr); 
     printf("  physical addr: %p\n", (void *)hdr->p_paddr); 
     printf("  file size: 0x%08zX\n", hdr->p_filesz); 
     printf("  memory size: 0x%08zX\n", hdr->p_memsz); 
     printf("  flags: 0x%08X (%s)\n", hdr->p_flags, flags_str(hdr->p_flags)); 
     printf("  align: %zd\n", hdr->p_align); 
     if (hdr->p_memsz) 
     { 
      printf("  derived address range: %p to %p\n", 
       (void *) (info->dlpi_addr + hdr->p_vaddr), 
       (void *) (info->dlpi_addr + hdr->p_vaddr + hdr->p_memsz)); 
     } 
    } 
    return 0; 
} 

int main(void) 
{ 
    dl_iterate_phdr(callback, NULL); 

    exit(EXIT_SUCCESS); 
} 
Cuestiones relacionadas