为什么 ELF 入口点 0x8048000 不能用“ld -e"更改?选项? [英] Why is the ELF entry point 0x8048000 not changeable with the "ld -e" option?

查看:31
本文介绍了为什么 ELF 入口点 0x8048000 不能用“ld -e"更改?选项?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Following up Why is the ELF execution entry point virtual address of the form 0x80xxxxx and not zero 0x0? and Why do virtual memory addresses for linux binaries start at 0x8048000?, why cannot I make ld use a different entry point than the default with ld -e?

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

If I do so, I either get a segmentation fault with return code 139, even for addresses close by the default entry point. Why?

我会让问题更具体:

        .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
" # The message as data

如果我用 as program.s -o program.o 编译它,然后用 ld -N program.o -o program, 静态链接它readelf -l program 显示 0x0000000000400078 作为文本段的 VirtAddr0x400078 作为入口点.运行时会打印Hello world".

If I compile this with as program.s -o program.o and then link it statically with ld -N program.o -o program, readelf -l program shows 0x0000000000400078 as the VirtAddr of the text segment and 0x400078 as entry point. When run, `Hello world" is printed.

但是,当我尝试与 ld -N -e0x400082 -Ttext=0x400082 program.o -o program 链接时(将文本段和入口点移动 4 个字节),程序将是 <代码>杀死.现在用 readelf -l 检查它会显示两个不同的 LOAD 类型的头文件,一个在 0x0000000000400082 一个在 0x00000000004000b0.

However, when I try to link with ld -N -e0x400082 -Ttext=0x400082 program.o -o program (moving text segment and entry point by 4 bytes), the program will be killed. Inspecting it with readelf -l now shows two different headers of type LOAD, one at 0x0000000000400082 and one at 0x00000000004000b0.

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

When I try 0x400086, it all works, and there is only one LOAD section.

  1. 这是怎么回事?
  2. 哪些内存地址可以选择,哪些不能选择,为什么?

谢谢.

推荐答案

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

why cannot I make ld use a different entry point than the default with ld -e

你当然可以.这个:

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

gcc main.c -Wl,-e,foo

不起作用,因为执行不是从 main 开始的.它从 _start 开始,它从 crt0.o(glibc 的一部分)链接并安排诸如动态链接之类的事情以正确启动.通过将 _start 重定向到 foo,您绕过了所有必需的 glibc 初始化,因此无法正常工作.

wouldn't work, because the execution doesn't start at main. It starts at _start, which is linked from crt0.o (part of glibc) and arranges for things like dynamic linking, etc. to start up properly. By redirecting _start to foo, you've bypassed all that required glibc initialization, and so things don't work.

但是如果您不需要动态链接,并且愿意做 glibc 通常为您做的事情,那么您可以随意命名入口点.示例:

But if you don't need dynamic linking, and are willing to do what glibc normally does for you, then you can name the entry point whatever you want. Example:

#include <syscall.h>

int foo()
{
  syscall(SYS_write, 1, "Hello, world
", 13);
  syscall(SYS_exit, 0);
}

gcc t.c -static -nostartfiles -Wl,-e,foo && ./a.out
Hello, world

哦,你这个问题的标题与你的实际问题不符(坏主意(TM)).

Oh, and your title of this question doesn't match your actual question (bad idea(TM)).

要回答标题中的问题,您确定可以更改可执行文件的链接地址.默认情况下,您会获得 0x8048000 加载地址(仅限 32 位;64 位默认为 0x400000).

To answer the question in the title, you sure can change the address your executable is linked at. By default, you get 0x8048000 load address (only in 32-bits; 64-bit default is 0x400000).

您可以轻松地将其更改为例如0x80000 通过将 -Wl,-Ttext-segment=0x80000 添加到链接行.

You can easily change that to e.g. 0x80000 by adding -Wl,-Ttext-segment=0x80000 to the link line.

更新:

但是,当我尝试链接 ld -N -e0x400082 -Ttext=0x400082 program.o -o 程序(将文本段和入口点移动 4 个字节)时,程序将被终止.

However, when I try to link with ld -N -e0x400082 -Ttext=0x400082 program.o -o program (moving text segment and entry point by 4 bytes), the program will be killed.

好吧,在不违反 .text 节对齐约束(即 4)的情况下,不可能将 Ttext 分配给 0x400082.您必须保持 .text 地址在至少 4 字节边界上对齐(或更改 .text 所需的对齐方式).

Well, it is impossible to assign Ttext to 0x400082 without violating .text section alignment constraint (which is 4). You must keep the .text address aligned on at least 4-byte boundary (or change the required alignment of .text).

当我将起始地址设置为 0x400078、0x40007c、0x400080、0x400084、...、0x400098 并使用 GNU-ld 2.20.1 时,程序运行正常.

When I set the start address to 0x400078, 0x40007c, 0x400080, 0x400084, ..., 0x400098 and use GNU-ld 2.20.1, the program works.

但是,当我使用 binutils 的当前 CVS 快照时,该程序适用于 0x400078、0x40007c、0x400088、0x40008c,并在 0x400080、0x400084、0x400090、0x400094、0x400098 时被杀死.这可能是链接器中的错误,或者我违反了其他一些约束(但我不知道是哪个).

However, when I use current CVS snapshot of binutils, the program works for 0x400078, 0x40007c, 0x400088, 0x40008c, and gets Killed for 0x400080, 0x400084, 0x400090, 0x400094, 0x400098. This might be a bug in the linker, or I am violating some other constraint (I don't see which though).

此时,如果您真的感兴趣,我建议您下载 binutils 源代码,构建 ld,并弄清楚究竟是什么原因导致它创建了两个 PT_LOAD 段而不是一个.

At this point, if you are really interested, I suggest downloading binutils sources, building ld, and figuring out what exactly causes it to create two PT_LOAD segments instead of one.

更新 2:

对具有重叠 LMA 的部分强制使用新分段.

Force new segment for sections with overlapping LMAs.

啊!这只是意味着您需要将 .data 移开.这使得一个工作可执行文件:

Ah! That just means you need to move .data out of the way. This makes a working executable:

ld -N -o t t.o -e0x400080 -Ttext=0x400080 -Tdata=0x400180

这篇关于为什么 ELF 入口点 0x8048000 不能用“ld -e"更改?选项?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆