Obtenga la dirección inicial y final de la sección de texto en un ejecutable

6 minutos de lectura

avatar de usuario
fóxis

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

Puedo obtener la dirección inicial de la _init símbolo o el _start símbolo, pero ¿qué pasa con la dirección final? ¿Debo considerar la dirección final del text sección para ser la última dirección antes de comenzar el .rodata ¿sección?

¿O debo editar el script ld predeterminado y agregar mis propios símbolos para indicar el inicio y el final de la sección de texto, y pasarlo 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 manera de obtener la dirección inicial y final de la sección de texto?

  • intente ‘readelf -h’ para ver la información del encabezado elf, donde se proporciona el desplazamiento del archivo del encabezado del programa.

    – Ak

    10 de septiembre de 2011 a las 8:10

  • Lo mismo para la sección de datos: stackoverflow.com/questions/1765969/…

    – Ciro Santilli Путлер Капут 六四事

    29 mayo 2015 a las 15:23


Los scripts de vinculación predeterminados de GNU binutils para plataformas basadas en ELF normalmente definen una gran cantidad de símbolos diferentes que se pueden usar para encontrar el inicio y el final de varias secciones.

El final de la sección de texto suele estar referenciado por 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 generalmente se exportan usando el PROVEER() mecanismo, lo que significa que se anularán si algo más en su ejecutable define ellos en lugar de simplemente referenciando ellos. En particular, eso 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 esté especificado por ningún estándar, por lo que no se debe suponer que esto sea portátil (no tengo idea de si incluso GNU binutils los proporciona para todos plataformas basadas en ELF, o si el conjunto de símbolos provisto ha cambiado en diferentes versiones de binutils), aunque supongo que si a) está haciendo algo que necesita esta información, yb) está considerando los scripts de enlace pirateados como una opción, entonces ¡la portabilidad no es una gran preocupación!

Para ver el conjunto exacto de símbolos que obtienes cuando construyes algo en particular en una plataforma en particular, dale al --verbose bandera a ld (o -Wl,--verbose para gcc) para imprimir la secuencia de comandos del enlazador que elige usar (realmente hay varias secuencias de comandos del enlazador predeterminadas diferentes, que varían según las opciones del enlazador y el tipo de objeto que está creando).

  • cual seria mejor opcion entonces? piratear el script del enlazador e insertar mis propios símbolos?

    – fóxis

    11 de septiembre de 2011 a las 5:55

  • 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á. (Un script de enlazador pirateado es menos portátil: un script de enlazador x86 de Linux pirateado casi seguramente no funcionará en Linux ppc, por ejemplo).

    –Matthew Slattery

    11 de septiembre de 2011 a las 14:01

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 las mismas banderas de todos modos).

El siguiente programa de ejemplo descarga toda la información devuelta por dl_iterate_phr. Estás interesado en cualquier segmento de tipo PT_LOAD con el PF_X bandera (tenga en cuenta que PT_GNU_STACK incluirá la bandera si -z execstack se pasa al enlazador, 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);
}

Para Linux, considere usar nm(1) herramienta para inspeccionar qué símbolos proporciona el archivo de objeto. Puede seleccionar este conjunto de símbolos, donde podría aprender los dos símbolos que proporcionó Matthew Slattery en su respuesta.

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

¿Ha sido útil esta solución?