[英]If an object file defines _start and doesn't use any libraries, why do I still need to link it before I can execute it?
I have a hello world program:我有一个 hello world 程序:
.global _start
.text
_start:
# write (1, msj, 13)
mov $1, %rax # system call 1 is write
mov $1, %rdi # file handler 1 is stdout
mov $message, %rsi # address of string to output
mov $13, %rdx # number of bytes
syscall
# exit(0)
mov $60, %rax # system call 60 is exit
xor %rdi, %rdi # we want to return code 0
syscall
message:
.ascii "Hello, world\n"
I can assemble this into an object file with:我可以将它组装成一个 object 文件:
as hello.s -o hello.o
This object file is not executable.此 object 文件不可执行。 When I try to execute it, I get:
当我尝试执行它时,我得到:
bash: ./hello.o: cannot execute binary file: Exec format error
I need to invoke the linker to make this viable:我需要调用 linker 以使其可行:
ld hello.o -o hello
At this point, the hello
program works.至此,
hello
程序开始工作了。 However, the use of the linker here is confusing to me.... I'm not linking in any external libraries.但是,在这里使用 linker 让我感到困惑......我没有在任何外部库中链接。 I seem to just be linking the object file to nothing.
我似乎只是将 object 文件链接到任何内容。
What is the linker doing for such a "self-contained" program? linker 为这样一个“自成一体”的程序做了什么?
ELF files have different types, like ELFTYPE_EXEC (traditional non-PIE executable) or ELFTYPE_REL (relocatable object file, normally with a .o
filename). ELF 文件有不同的类型,例如 ELFTYPE_EXEC(传统的非 PIE 可执行文件)或 ELFTYPE_REL(可重定位的 object文件,通常使用
.o
文件名)。
as
doesn't have a special-case mode that outputs an executable instead of an object file. as
没有输出可执行文件而不是 object 文件的特殊情况模式。 There are other assemblers, or at least one: FASM, that do have a special mode to output an ELF executable directly.还有其他汇编器,或者至少一个:FASM,它们确实有一个特殊模式,可以直接执行 ELF 可执行文件 output。
Given the ELF object file that as
produces, you could: as
生成的 ELF object 文件,您可以:
.so
shared libraries;.so
共享库; those could have static constructors (init functions) that run before your _start
._start
之前运行的 static 构造函数(初始化函数)。 (For example glibc's libc.so
does this, which is why it happens to work to call libc functions from _start
on Linux without manually calling glibc init functions, if you dynamically link.) libc.so
就是这样做的,这就是为什么它恰好可以从 Linux 上的_start
调用 libc 函数而无需手动调用 glibc 初始化函数,如果你动态链接的话。) The .o
needs to be linked because no absolute address has been chosen for it to be loaded at, to fill in things like your 64-bit absolute immediate in mov $message, %rsi
. .o
需要链接,因为没有选择绝对地址来加载它,以在mov $message, %rsi
中填写诸如 64 位绝对立即数之类的内容。
(If you'd use lea message(%rip), %rsi
the code would be position-independent but the distance between the .text
and .rodata
sections wouldn't be known yet. Although you put your string right in .text
so that would get resolved at assemble time if you hadn't chosen the least efficient way to get an address into a register, so that would give you a stand-alone block of code+data. But the most efficient way, mov $message, %esi
, would also need an absolute (32-bit) address.) (如果您使用
lea message(%rip), %rsi
代码将与位置无关,但.text
和.rodata
部分之间的距离尚不清楚。尽管您将字符串正确放入.text
所以如果您没有选择效率最低的方法将地址放入寄存器,那么这将在汇编时得到解决,这样会给您一个独立的代码+数据块。但最有效的方法是mov $message, %esi
,还需要一个绝对(32 位)地址。)
as
doesn't know what you want to do, and GNU Binutils was primarily written for use by compiler back-ends, so there was no point making as
more complicated to be able to write an ELF-type EXEC
file directly since that's what ld
is for. as
不知道你想做什么,而且 GNU Binutils 主要是为编译器后端编写的,所以直接编写 ELF 类型的EXEC
文件没有意义, as
这就是ld
是为了。 This is the Unix philosophy of making small separate tools that do one thing well.这就是 Unix 的理念,即制作能够做好一件事的小型独立工具。
If you want to assemble + link with one command, make a shell script, or use a compiler front-end:如果你想用一个命令组装 + 链接,制作一个 shell 脚本,或者使用编译器前端:
gcc -nostdlib -static -no-pie start.s -o static_executable
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.