问题描述
我有一个带有_start
标签的程序集文件,这是.text
段中的第一件事.我希望这个标签成为我的应用程序的入口点.
I have an assembly file with a _start
label as the first thing in the .text
segment. I would like this label to be the entry point of my application.
每当我将此文件与另一个具有main
函数的文件一起传递时,无论如何,该main
函数最终都将成为我应用程序的入口点.
Whenever I pass this file together with another file that have a function called main
, that main
function ends up being the entry point of my application no matter what.
我正在使用GNU链接器,并尝试过-e _start
标志以及更改输入文件的顺序.只要存在main
函数,它将成为入口点.如果我重命名main
函数,则它可以正常工作,而我的_start
标签成为入口点.
I am using the GNU linker and have tried the -e _start
flag, along with changing the input file order. As long as there exist a main
function, it will become the entry point.. If I rename the main
function, it works fine and my _start
label becomes the entry point.
似乎是由于对编译器的-O2
标志.
Seems like it is because of -O2
flag to the compiler.
原样
.text
.global _start
_start:
jmp main
main.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>
有什么想法吗?
推荐答案
看来您的问题确实是如何在生成的可执行文件中将特定功能放在所有其他功能之前?
第一件事是,仅在某些情况下这样做才有价值. ELF 可执行文件的入口点编码在 ELF 标头中.入口点在可执行文件中的位置无关.
First thing is that doing this only has value in certain circumstances. An ELF executable has the entry point encoded in the ELF header. The placement of the entry point in the executable isn't relevant.
一种特殊情况是非mulitboot兼容的内核,其中自定义引导程序加载了由 GCC 生成并转换为二进制输出的内核.查看您的问题历史记录表明,引导加载程序/内核开发可能符合您的要求.
One special circumstance is a non-mulitboot compatible kernel where a custom bootloader loads a kernel that was generated by GCC and converted to binary output. Looking through your question history suggests that bootloader / kernel development might be a possibility for your requirement.
使用 GCC 时,您不能假定生成的代码将按照您想要的顺序排列.正如您所发现的那样,选项(如优化)可能会使功能彼此相对重新排序或完全消除某些功能.
When using GCC you can't assume that the generated code will be in the order you want. As you have found out options (like optimizations) may reorder the functions relative to each other or eliminate some altogether.
将函数首先放置在 ELF 可执行文件中的一种方法是将其放置在其自己的节中,然后创建一个链接程序脚本以将该节首先放置.应该与 C 一起使用的示例链接脚本link.ld
为:
One way to put a function first in an ELF executable is to place it into its own section and then create a linker script to position that section first. An example linker script link.ld
that should work with C would be:
/*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
文件可以修改为执行以下操作:
This script explicitly places whatever is in the section .text.prologue
before any other code. We just need to place _start
into that section. Your as.s
file could be modified to do this:
.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
您将像这样编译,组装和链接它们:
You'd compile, assemble and link them like this:
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
:
An objdump -D test
should show the function _start
before main
:
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
这篇关于使用gnu链接器更改入口点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!