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

时间:2011-09-10 07:56:43

标签: c gcc ld

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

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

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

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

4 个答案:

答案 0 :(得分:20)

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

我不相信这些符号中的任何符号都是由任何标准指定的,因此不应该认为这是可移植的(我不知道甚至GNU binutils是否为所有 ELF提供它们基于平台,或者提供的符号集是否已经在不同的binutils版本上发生了变化),尽管我猜是否a)你正在做一些需要这些信息的事情,并且b)你正在考虑将被黑客的链接器脚本作为选项,然后可移植性不是太在意了!

要查看在特定平台上构建特定内容时获得的确切符号集,请将--verbose标记提供给ld(或-Wl,--verbosegcc)打印它选择使用的链接描述文件(实际上有几个不同的默认链接描述文件,根据链接器选项和你正在构建的对象类型而不同)。

答案 1 :(得分:7)

说&#34;&#34;&#34;文本段,因为可能有多个(保证通常情况下,当您有共享库时,但单个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);
}

答案 2 :(得分:2)

.rodata并不保证始终直接在.text之后。您可以使用objdump -h filereadelf --sections file获取更多信息。使用objdump,您可以将大小和偏移量都记录到文件中。

答案 3 :(得分:2)

对于Linux,请考虑使用nm(1)工具检查目标文件提供的符号。您可以选择这组符号,在那里您可以学习Matthew Slattery在其答案中提供的两个符号。