为啥使用“ld -e”选项不能更改 ELF 入口点 0x8048000?
Posted
技术标签:
【中文标题】为啥使用“ld -e”选项不能更改 ELF 入口点 0x8048000?【英文标题】:Why is the ELF entry point 0x8048000 not changeable with the "ld -e" option?为什么使用“ld -e”选项不能更改 ELF 入口点 0x8048000? 【发布时间】:2011-12-28 08:01:53 【问题描述】:跟进Why is the ELF execution entry point virtual address of the form 0x80xxxxx and not zero 0x0? 和Why do virtual memory addresses for linux binaries start at 0x8048000?,为什么我不能让ld
使用与默认ld -e
不同的入口点?
如果我这样做,我会得到一个返回代码为 139 的 segmentation fault
,即使是默认入口点附近的地址也是如此。为什么?
编辑:
我会让问题更具体:
.text
.globl _start
_start:
movl $0x4,%eax # eax = code for 'write' system call
movl $1,%ebx # ebx = file descriptor to standard output
movl $message,%ecx # ecx = pointer to the message
movl $13,%edx # edx = length of the message
int $0x80 # make the system call
movl $0x0,%ebx # the status returned by 'exit'
movl $0x1,%eax # eax = code for 'exit' system call
int $0x80 # make the system call
.data
.globl message
message:
.string "Hello world\n" # The message as data
如果我用as program.s -o program.o
编译它,然后用ld -N program.o -o program
静态链接它,readelf -l program
显示0x0000000000400078
作为文本段的VirtAddr
,0x400078
作为入口点。运行时会打印“Hello world”。
但是,当我尝试与ld -N -e0x400082 -Ttext=0x400082 program.o -o program
链接时(将文本段和入口点移动4 个字节),程序将是killed
。使用readelf -l
检查它现在会显示两个不同的LOAD
类型的标题,一个在0x0000000000400082
,一个在0x00000000004000b0
。
当我尝试0x400086
时,一切正常,而且只有一个LOAD
部分。
-
这是怎么回事?
哪些内存地址可以选择,哪些不能选择,为什么?
谢谢。
【问题讨论】:
我还可以使用链接描述文件修改入口点:***.com/a/30536800/895245 【参考方案1】:为什么我不能让 ld 使用与 ld -e 的默认入口点不同的入口点
你当然可以。这个:
int foo(int argc, char *argv[]) return 0;
gcc main.c -Wl,-e,foo
不起作用,因为执行不是从 main 开始的。它从_start
开始,它从crt0.o
(glibc 的一部分)链接而来,并安排诸如动态链接之类的东西正确启动。通过将_start
重定向到foo
,您已经绕过了所有需要的 glibc 初始化,因此无法正常工作。
但是,如果您不需要动态链接,并且愿意做 glibc 通常为您做的事情,那么您可以随意命名入口点。示例:
#include <syscall.h>
int foo()
syscall(SYS_write, 1, "Hello, world\n", 13);
syscall(SYS_exit, 0);
gcc t.c -static -nostartfiles -Wl,-e,foo && ./a.out
Hello, world
哦,你这个问题的标题与你的实际问题不符(坏主意(TM))。
要回答标题中的问题,您确定可以更改可执行文件的链接地址。默认情况下,您会获得0x8048000
加载地址(仅限32 位;64 位默认为0x400000
)。
您可以轻松地将其更改为例如0x80000
通过将-Wl,-Ttext-segment=0x80000
添加到链接行。
更新:
但是,当我尝试链接 ld -N -e0x400082 -Ttext=0x400082 program.o -o 程序(将文本段和入口点移动 4 个字节)时,程序将被终止。
好吧,如果不违反.text
节对齐约束(即4),就不可能将Ttext
分配给0x400082
。您必须保持 .text 地址在至少 4 字节边界上对齐(或更改.text
所需的对齐方式)。
当我将起始地址设置为 0x400078、0x40007c、0x400080、0x400084、...、0x400098 并使用 GNU-ld 2.20.1 时,程序运行正常。
但是,当我使用 binutils 的当前 CVS 快照时,该程序适用于 0x400078、0x40007c、0x400088、0x40008c,并在 0x400080、0x400084、0x400090、0x400094、0x400098 时被杀死。这可能是链接器中的错误,或者我违反了其他一些约束(但我不知道是哪个)。
此时,如果您真的感兴趣,我建议您下载 binutils 源代码,构建 ld
,并找出导致它创建两个 PT_LOAD
段而不是一个的确切原因。
更新 2:
对具有重叠 LMA 的部分强制使用新分段。
啊!这只是意味着您需要将.data
移开。这使得一个工作可执行文件:
ld -N -o t t.o -e0x400080 -Ttext=0x400080 -Tdata=0x400180
【讨论】:
我更新了我的问题,以更好地说明什么不能按预期工作。 谢谢,很好的回答,我没有考虑对齐。 我使用 git bisect 查找然后在 binutils 2.20 和 2.21 之间进行更改,从而引入了您描述的更改。它被称为“elf.c (_bfd_elf_map_sections_to_segments):为具有重叠 LMA 的部分强制新段。” (repo.or.cz/w/binutils.git/commit/…)以上是关于为啥使用“ld -e”选项不能更改 ELF 入口点 0x8048000?的主要内容,如果未能解决你的问题,请参考以下文章