C 获取可执行文件中文本部分的开始和结束地址
我需要获取可执行文件文本部分的开始和结束地址。我怎样才能得到它 我可以从C 获取可执行文件中文本部分的开始和结束地址,c,gcc,ld,C,Gcc,Ld,我需要获取可执行文件文本部分的开始和结束地址。我怎样才能得到它 我可以从\u init符号或\u start符号中获取起始地址,但是结束地址呢?我认为“文本>代码>节的结尾地址是在开始代码之前的最后一个地址吗? 或者我应该编辑默认的ld脚本并添加自己的符号来指示文本部分的开始和结束,并在编译时将其传递给GCC吗?在这种情况下,我应该把新的符号放在哪里,我应该考虑init和Fini部分吗?< /P> 获取文本部分的起始地址和结束地址的好方法是什么?.rodata不保证总是直接位于.text之后。您
\u init
符号或\u start
符号中获取起始地址,但是结束地址呢?我认为“<代码>文本>代码>节的结尾地址是在开始代码之前的最后一个地址吗?
或者我应该编辑默认的ld脚本并添加自己的符号来指示文本部分的开始和结束,并在编译时将其传递给GCC吗?在这种情况下,我应该把新的符号放在哪里,我应该考虑init和Fini部分吗?< /P>
获取文本部分的起始地址和结束地址的好方法是什么?
.rodata
不保证总是直接位于.text
之后。您可以使用objdump-h文件
和readelf--sections文件
获取更多信息。使用objdump,可以将大小和偏移量都输入到文件中。基于ELF的平台的GNU binutils默认链接器脚本通常定义大量不同的符号,这些符号可用于查找各个部分的开始和结束
文本部分的结尾通常由三种不同符号的选择来引用:etext
、\u etext
或\u etext
;该启动可作为\u可执行文件\u启动
找到。(请注意,这些符号通常使用该机制导出,这意味着如果可执行文件中的其他内容定义了它们,而不仅仅是引用它们,它们将被覆盖。特别是,这意味着\u etext
或\u 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
$
$cat etext.c
#包括
外部字符可执行文件启动;
外部字符;
内部主(空)
{
printf(“0x%lx\n”,(无符号长)和\u可执行\u开始);
printf(“0x%lx\n”,(无符号长)和\uuuuetext);
返回0;
}
$gcc-Wall-o etext-etext.c
美元/文本
0x8048000
0x80484a0
$
我不相信这些符号中有任何一个是由任何标准指定的,因此这不应该被认为是可移植的(我不知道GNU binutils是否为所有基于ELF的平台提供了它们,或者提供的符号集是否在不同的binutils版本中发生了变化),虽然我猜如果a)你正在做一些需要这些信息的事情,b)你正在考虑黑客链接器脚本作为一个选项,那么可移植性就不太重要了
要查看在特定平台上构建特定内容时得到的确切符号集,请将
--verbose
标记指定给ld
(或-Wl,--verbose
指定给gcc
)以打印它选择使用的链接器脚本(确实有几种不同的默认链接器脚本,它们根据链接器选项和正在构建的对象的类型而不同)。 Linux,考虑使用<代码> NM(1)。工具,用于检查对象文件提供的符号。您可以从这组符号中选择,从中可以了解Matthew Slattery在其答案中提供的两个符号。
提到“文本”段是不正确的,因为可能有多个(对于共享库的常见情况,这是可以保证的,但是一个ELF二进制文件仍然可以有多个具有相同标志的PT_LOAD
部分)
下面的示例程序转储由dl_iterate_phr
返回的所有信息。您对带有PF_X
标志的PT_LOAD
类型的任何段都感兴趣(请注意,PT_GNU__STACK
如果将-z execstack
传递给链接器,则PT_GNU____堆栈
将包含该标志,因此您确实需要检查这两个标记)
定义GNU源
#包括
#包括
#包括
#包括
常量字符*类型(ElfW(Word)类型)
{
开关(类型)
{
案例PT_NULL:
return“PT_NULL”;//不应在运行时看到,只应在文件中看到!
案例PT_荷载:
返回“PT_负载”;
案例PT_动态:
返回“PT_动态”;
案例PT_INTERP:
返回“PT_INTERP”;
案例PT_注:
返回“PT_注释”;
案例PT_SHLIB:
返回“PT_SHLIB”;
病例PT_PHDR:
返回“PT_PHDR”;
案例PT_TLS:
返回“PT_TLS”;
案例PT_GNU_EH_框架:
返回“PT_GNU_EH_FRAME”;
案例PT_GNU_堆栈:
返回“PT_GNU_STACK”;
案例PT_GNU_RELRO:
返回“PT_GNU_RELRO”;
案例PT_SUNWBSS:
返回“PT_SUNWBSS”;
案例PT_SUNWSTACK:
返回“PT_SUNWSTACK”;
违约:
if(PT_LOOS dlpi_添加);
}
if(尺寸>偏移量(结构数据包phdr信息、数据包)
{
printf(“subs:%lld\n”,info->dlpi\u subs);
}
if(大小>偏移量(结构数据集phdr信息、数据集tls修改))
{
printf(“tls modid:%zu\n”,info->dlpi\u tls\u modid);
}
if(大小>偏移量(结构数据集phdr信息、数据集tls数据))
{
printf(“tls数据:%p\n”,信息->dlpi\U tls\U数据);
}
printf(“段:%d\n”,信息->dlpi\u phnum);
对于(j=0;jdlpi\u phnum;j++)
{
常量ElfW(Phdr)*hdr=&info->dlpi_Phdr[j];
printf(“段%2d\n”,j);
printf(“类型:0x%08X(%s)\n”,hdr->p_类型,type_str(hdr->p_类型));
printf(“文件偏移量:0x%08zX\n”,hdr->p_偏移量);
printf(“虚拟地址:%p\n”,(void*)hdr->p\u vaddr);
printf(“物理地址:%p\n”,(void*)hdr->p\u paddr);
printf(“文件大小:0x%08zX\n”,hdr->p_filesz);
printf(“内存大小:0x%08zX\n”,hdr->p_memsz);
printf(“标志:0x%08X(%s)\n”、hdr->p_标志、标志(hdr->p_标志));
printf(“对齐:%zd\n”,hdr->p\u对齐);
如果(hdr->p_memsz)
{
printf(“派生地址范围:%p到%p\n”,
(无效*)(信息->地址+hdr->地址),
(无效*)(信息->dlpi地址+hdr->p\U地址+
#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);
}