【问题标题】:Change entry point with gnu linker使用 gnu 链接器更改入口点
【发布时间】:2017-02-17 15:37:34
【问题描述】:

我有一个带有_start 标签的程序集文件作为.text 段中的第一件事。我希望这个标签成为我的应用程序的入口点。

每当我将此文件与另一个具有称为main 的函数的文件一起传递时,无论如何,main 函数最终都会成为我的应用程序的入口点。

我正在使用 GNU 链接器并尝试了 -e _start 标志,以及更改输入文件顺序。只要存在main 函数,它将成为入口点。如果我重命名main 函数,它工作正常,我的_start 标签成为入口点。

编辑:似乎是因为编译器的-O2 标志。

as.s

.text
.global  _start
_start:
jmp main

ma​​in.c

int main(){
    return 0;
}

编译

gcc -O2 -c as.s -o as.o
gcc -O2 -c main.c -o main.o
ld -e _start as.o main.o -o test

输出

00000000004000b0 <main>:
  4000b0:   31 c0                   xor    %eax,%eax
  4000b2:   c3                      retq   

00000000004000b3 <_start>:
  4000b3:   e9 f8 ff ff ff          jmpq   4000b0 <main>

有什么想法吗?

【问题讨论】:

  • 您忘记发布任何证明,也没有版本或环境信息,也没有minimal reproducible example。我已经测试过了,效果很好。
  • 您确实意识到,仅仅因为main 出现在_start 之前,它就不是入口点,对吧?入口点在 ELF 标头字段中给出。
  • 那种。无论如何,我需要 _start 标签位于可执行区域的开头。不过,-O1-O0 似乎可以正常工作。
  • 您是否需要在文件的开头使用它,因为您正在使用引导加载程序加载它并且您打算 JMP/CALL 到它。要求将 _start 之类的特定标签放在特定位置对我来说,这比表面上看到的要多。
  • 如果我想让一个特定的函数成为可执行文件中的第一件事,我会将它放在它自己的部分中,并创建一个链接描述文件,在开始时定位该部分。

标签: c gcc assembly ld


【解决方案1】:

看来您的问题确实是如何在生成的可执行文件中将特定功能放在所有其他功能之前?

首先,这样做只有在某些情况下才有价值。 ELF 可执行文件的入口点编码在 ELF 标头中。可执行文件中入口点的位置无关紧要。

一种特殊情况是非多重引导兼容内核,其中自定义引导加载程序加载由 GCC 生成并转换为二进制输出的内核。查看您的问题历史表明引导加载程序/内核开发可能符合您的要求。


当使用 GCC 时,你不能假设生成的代码会按照你想要的顺序。正如您所发现的那样,选项(如优化)可能会相对于彼此重新排序功能或完全消除一些功能。

ELF 可执行文件中首先放置函数的一种方法是将其放置在自己的部分中,然后创建链接描述文件以首先放置该部分。应该与 C 一起使用的示例链接器脚本 link.ld 是:

/*OUTPUT_FORMAT("elf32-i386");*/
OUTPUT_FORMAT("elf64-x86-64");

ENTRY(_start);

SECTIONS
{
    /* This should be your memory offset (VMA) where the code and data
     * will be loaded. In Linux this is 0x400000, multiboot loader is
     * 0x100000 etc */
    . = 0x400000;

    /* Place special section .text.prologue before everything else */
    .text : {
        *(.text.prologue);
        *(.text*);
    }

    /* Output the data sections */
    .data : {
        *(.data*);
    }

    .rodata : {
        *(.rodata*);
    }

    /* The BSS section for uniitialized data */
    .bss : {
        __bss_start = .;
        *(COMMON);
        *(.bss);
        . = ALIGN(4);
        __bss_end = .;
    }

    /* Size of the BSS section in case it is needed */
    __bss_size = ((__bss_end)-(__bss_start));

    /* Remove the note that may be placed before the code by LD */
    /DISCARD/ : {
        *(.note.gnu.build-id);
    }
}

此脚本明确地将.text.prologue 部分中的任何内容放在任何其他代码之前。我们只需要将_start 放入该部分。您的as.s 文件可以修改为:

.global  _start

# Start a special section called .text.prologue making it
# allocatable and executable
.section .text.prologue, "ax"

_start:
jmp main

.text
# All other regular code in the normal .text section

您可以像这样编译、组装和链接它们:

gcc -O2 -c main.c -o main.o
gcc -O2 -c as.s -o as.o
ld -Tlink.ld main.o as.o -o test

objdump -D test 应该在 main 之前显示函数 _start

test:     file format elf32-i386


Disassembly of section .text:

00400000 <_start>:
  400000:       e9 0b 00 00 00          jmp    400010 <main>
  400005:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%eax,%eax,1)
  40000c:       00 00 00
  40000f:       90                      nop

00400010 <main>:
  400010:       31 c0                   xor    %eax,%eax
  400012:       c3                      ret

【讨论】:

    猜你喜欢
    • 2019-04-26
    • 1970-01-01
    • 2017-04-18
    • 2013-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-24
    相关资源
    最近更新 更多