跟进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
1}}作为切入点。运行时,会打印“Hello world”。
但是,当我尝试链接ld -N -e0x400082 -Ttext=0x400082 program.o -o program
(移动文本段和入口点4个字节)时,程序将为killed
。现在,使用readelf -l
检查它会显示两个类型LOAD
的不同标头,一个位于0x0000000000400082
,另一个位于0x00000000004000b0
。
当我尝试0x400086
时,一切正常,只有一个LOAD
部分。
谢谢你。
答案 0 :(得分:24)
为什么我不能让ld使用与ld -e
默认的不同的入口点
你确定可以。这样:
int foo(int argc, char *argv[]) { return 0; }
gcc main.c -Wl,-e,foo
不会起作用,因为执行不会从主要开始。它从_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个字节)时,该程序将被终止。
嗯,如果不违反Ttext
部分对齐约束(即4),就无法将0x400082
分配给.text
。您必须将.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