为什么ELF入口点0x8048000不能通过“ld -e”选项更改?

时间:2011-11-14 02:25:59

标签: linker elf memory-layout

跟进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 program0x0000000000400078显示为文本段的VirtAddr0x400078 1}}作为切入点。运行时,会打印“Hello world”。

但是,当我尝试链接ld -N -e0x400082 -Ttext=0x400082 program.o -o program(移动文本段和入口点4个字节)时,程序将为killed。现在,使用readelf -l检查它会显示两个类型LOAD的不同标头,一个位于0x0000000000400082,另一个位于0x00000000004000b0

当我尝试0x400086时,一切正常,只有一个LOAD部分。

  1. 这里发生了什么事?
  2. 我可以选择哪些内存地址,我选择哪些内存以及为什么?
  3. 谢谢你。

1 个答案:

答案 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