Pobranie adresu początkowego i końcowego sekcji tekstowej w pliku wykonywalnym

Potrzebuję adresu początkowego i końcowego sekcji tekstowej pliku wykonywalnego. Jak mogę go zdobyć?

Mogę uzyskać adres początkowy z symbolu _init lub symbolu _start, ale co z adresem końcowym? Czy powinienem uznać adres końcowy sekcji text za ostatni adres przed rozpoczęciem sekcji .rodata?

Czy mam edytować domyślny skrypt ld i dodawać własne symbole, aby wskazać początek i koniec sekcji tekstowej i przekazać go do GCC podczas kompilacji? W w tym przypadku, gdzie mam umieścić nowe symbole, czy powinienem rozważyć sekcję init i fini?

Jaki jest dobry sposób na uzyskanie adresu początkowego i końcowego sekcji tekstowej?

 15
Author: Peter Mortensen, 2011-09-10

4 answers

Domyślne Skrypty linkera GNU binutils dla platform opartych na ELF Zwykle definiują sporo różnych symboli, które mogą być użyte do znalezienia początku i końca różnych sekcji.

Koniec sekcji tekstowej jest zwykle określany przez wybór trzech różnych symboli: etext, _etext lub __etext; początek można znaleźć jako __executable_start. (Zauważ, że te symbole są zwykle eksportowane za pomocą mechanizmu PROVIDE(), co oznacza, że zostaną nadpisane, jeśli coś innego w Twoim pliku wykonywalnym definiuje nie tylko odwołując się do nich . W szczególności oznacza to, że _etext lub __etext mogą być bezpieczniejszymi wyborami niż etext.)

Przykład:

$ 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
$

Nie sądzę, aby któryś z tych symboli był określony przez jakikolwiek standard, więc nie należy zakładać, że jest to przenośne (nie mam pojęcia, czy nawet GNU binutils udostępnia je dla wszystkich platform opartych na ELFIE, czy też zestaw dostarczonych symboli zmienił się na inne. różne wersje binutils), chociaż myślę, że jeśli a) robisz coś, co potrzebuje tych informacji, i b) rozważasz zhakowane Skrypty linkera jako opcję, to przenośność nie jest zbyt dużym problemem!

Aby zobaczyć dokładny zestaw symboli otrzymany podczas budowania konkretnej rzeczy na określonej platformie, podaj flagę --verbose ld (lub -Wl,--verbose gcc), aby wydrukować skrypt linkera, którego używa (istnieje naprawdę kilka różnych domyślnych skryptów linkera, które różnią się zgodnie z opcjami linkera i typem budowanego obiektu).

 19
Author: Matthew Slattery,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-10 17:18:42

Niepoprawne jest mówienie o "segmencie" tekstu, ponieważ może być więcej niż jeden (gwarantowane w przypadku zwykłych bibliotek współdzielonych, ale nadal możliwe jest, aby pojedynczy plik binarny elfa miał wiele PT_LOAD sekcji z tymi samymi flagami).

Poniższy przykładowy program wyrzuca wszystkie informacje zwrócone przez dl_iterate_phr. Jesteś zainteresowany dowolnym segmentem typu PT_LOAD z flagą PF_X (zauważ, że PT_GNU_STACK będzie zawierać flagę, Jeśli -z execstack zostanie przekazana do linkera, więc naprawdę trzeba sprawdzić oba).

#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);
}
 7
Author: o11c,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-23 07:14:14

.rodata nie ma gwarancji, że zawsze przyjdzie bezpośrednio po .text. Możesz użyć objdump -h file i readelf --sections file, aby uzyskać więcej informacji. Z objdump otrzymujesz zarówno rozmiar, jak i przesunięcie do pliku.

 2
Author: Emil Romanus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-10 08:03:59

Dla Linuksa rozważ użycie narzędzia nm(1) do sprawdzenia, jakie symbole zawiera plik obiektowy. Możesz wybrać ten zestaw symboli, w którym możesz nauczyć się obu symboli, które Matthew Slattery podał w swojej odpowiedzi.

 2
Author: sholsapp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-18 22:54:27