在 ELF 文件中,如何确定 _start 的地址?

6
我一直在阅读ELF规范,但无法确定程序入口点和_start地址来自哪里。
看起来它们应该在一个相当一致的位置上,但我写了几个微不足道的程序,_start总是在不同的位置上。
有人能解释一下吗?
3个回答

4

_start符号可以在任何目标文件中定义。通常情况下它会自动生成(相当于C语言中的main函数)。您也可以在汇编源文件中自己生成它:

.globl _start
_start:
    // assembly here

当链接器处理完所有目标文件后,它会寻找_start符号,并将其值放入elf头文件e_entry字段中。加载器从该字段中取出地址,并在已将所有节加载到内存并准备执行文件后调用它。

1
通常情况下,使用GCC/glibc时,_start并不是真正的main函数:_start会调用初始化函数,然后调用main函数,最后使用main函数返回的值来调用exit()函数。 - ysdx

3
请查看链接器脚本ld正在使用的内容:
ld -verbose

格式文档在此处: https://sourceware.org/binutils/docs-2.25/ld/Scripts.html

脚本文件基本上决定了可执行文件的生成方式。

在Binutils 2.24 Ubuntu 14.04 64位版本中,它包含以下行:

ENTRY(_start)

这将设置入口点为_start符号(如ctn所述,进入ELF头)。

然后:

. = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;

将第一个header的地址设置为0x400000+SIZEOF_HEADERS

我已将该地址修改为0x800000,使用ld -T传递我的自定义脚本后,它起作用了:readelf -s显示_start在该地址处。

另一种更改它的方法是使用-Ttext-segment=0x800000选项。

使用0x400000=4Mb=getconf PAGE_SIZE的原因是从第二页的开头开始,如为什么ELF执行入口点虚拟地址形式为0x80xxxxx而不是0x0?所述。

一个问题描述了如何从命令行设置_start为什么ELF入口点0x8048000无法使用"ld -e"选项更改?

SIZEOF_HEADERS是ELF文件+程序header的大小,它们位于ELF文件的开头。该数据由Linux加载到虚拟内存空间的最开始处(TODO为什么?)。在一个最小化的Linux x86-64 hello world中,有2个程序header,它的值为0xb0,所以_start符号位于0x4000b0。


4MiB是两个x86-64大页。只有x86上的传统32位(非PAE)分页使用4M大页。为什么在x86_64 ABI中将地址0x400000选为文本段的起始地址?但标准页面大小为4k。 - Peter Cordes
顺带一提,现代的发行版会配置GCC使用ld -pie命令来选择不同的链接器脚本。即使使用gcc -fno-pie -no-pie或者纯粹的ld命令也可以以这种方式工作。 - Peter Cordes

0

我不确定,但可以尝试这个链接http://www.docstoc.com/docs/23942105/UNIX-ELF-File-Format。在第8页上展示了如果是可执行程序的入口点在哪里。基本上,你需要计算偏移量并获取它。请确保记住x86的小端序(我猜你在使用它),如果你按字节读取,请重新排序。编辑:或许不用,说实话我也不太确定。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接