获取可执行文件中文本部分的开始和结束地址

pho*_*xis 15 c gcc ld

我需要获取可执行文本部分的开始和结束地址.我怎么才能得到它?

我可以从_init符号或_start符号中获取起始地址,但是结束地址呢?text在开始这.rodata部分之前,我应该考虑该部分的结束地址是最后一个地址吗?

或者我应该编辑默认的ld脚本并添加我自己的符号来指示文本部分的开头和结尾,并在编译时将其传递给GCC?在这种情况下,我应该在哪里放置新符号,我应该考虑init和fini部分吗?

获取文本部分的开始和结束地址有什么好方法?

Mat*_*ery 21

基于ELF的平台的GNU binutils默认链接描述文件通常定义了许多不同的符号,可用于查找各个部分的开头和结尾.

文本部分的结尾通常由三个不同符号的选择引用:etext,_etext__etext; 开始可以找到__executable_start.(请注意,这些符号通常使用PROVIDE()机制导出,这意味着如果可执行文件中的其他内容定义它们而不是仅仅引用它们,它们将被覆盖.特别是这意味着_etext或者__etext可能是比它更安全的选择etext. )

例:

$ 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
$
Run Code Online (Sandbox Code Playgroud)

我不认为这些符号中的任何符号都是由任何标准指定的,因此不应该认为这是可移植的(我不知道甚至GNU binutils是否为所有基于ELF的平台提供它们,或者是否符号集已经改变了不同的binutils版本),虽然我想如果a)你正在做一些需要这些信息的东西,并且b)你正在考虑被黑客的链接器脚本作为一个选项,那么可移植性并不是太过关注!

要查看符号的确切集合在特定平台上建立一个特定的事情,当你得到,给--verbose标志ld(或-Wl,--verbosegcc)打印它选择要使用的链接脚本(实际上有几种不同的默认链接描述文件,根据该变化链接器选项和您正在构建的对象类型.

  • 不,如果这些符号在您的平台上工作,您不妨使用它们。上面的示例代码至少适用于 Linux x86、Linux ppc 和 NetBSD x86 - 我只是不知道是否还有其他平台无法使用。(被黑的链接器脚本*不太*可移植:例如,被黑的 Linux x86 链接器脚本几乎肯定不会在 Linux ppc 上运行。) (2认同)

o11*_*11c 7

说"文本"段是不正确的,因为可能有多个(保证通常情况下你有共享库,但是单个ELF二进制文件仍然可能有多个PT_LOAD具有相同标志的部分).

以下示例程序转储了返回的所有信息dl_iterate_phr.你对PT_LOAD带有PF_X标志的任何类型的段感兴趣(注意PT_GNU_STACK如果-z execstack传递给链接器将包含标志,所以你真的必须检查两者).

#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);
}
Run Code Online (Sandbox Code Playgroud)