10

跟进为什么ELF执行入口点虚拟地址的形式是0x80xxxxxx而不是零0x0?为什么 Linux 二进制文件的虚拟内存地址从 0x8048000 开始?,为什么我不能ld使用与默认值不同的入口点ld -e

如果我这样做,我会得到一个segmentation fault返回码 139 的结果,即使是默认入口点附近的地址也是如此。为什么?

编辑:

我会让问题更具体:

        .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 programreadelf -l program显示0x0000000000400078VirtAddr文本段的和0x400078作为入口点。运行时,会打印“Hello world”。

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

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

  1. 这里发生了什么?
  2. 我可以选择哪些内存地址,哪些不能选择,为什么?

谢谢。

4

1 回答 1

25

为什么我不能让 ld 使用与 ld -e 的默认值不同的入口点

你当然可以。这:

int foo(int argc, char *argv[]) { return 0; }

gcc main.c -Wl,-e,foo

行不通,因为执行不是从 main 开始的。它从 开始_start,它从crt0.o(glibc 的一部分)链接并安排诸如动态链接之类的东西以正确启动。通过重定向_startfoo,您已经绕过了所有需要的 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 个字节)链接时,程序将被终止。

好吧,如果不违反节对齐约束(即 4),就不可能分配Ttext到。您必须保持 .text 地址在至少 4 字节边界上对齐(或更改所需的对齐方式)。0x400082.text.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
于 2011-11-14T05:41:34.070 回答