【发布时间】:2024-01-22 21:54:01
【问题描述】:
有一个 c 运行时库,根据 https://en.wikipedia.org/wiki/Crt0 在文件 ctr0.o 中调用以在调用 main 之前初始化变量。我在这里复制了它:
.text
.globl _start
str : .asciz "abcd\n"
_start:
xor %ebp, %ebp #basePointer == 0
mov (%rsp), %edi #argc from stack
lea 8(%rsp), %rsi #pointer to argv
lea 16(%rsp,%rdi,8), %rdx #pointer to envp
xor %eax, %eax
call main
mov %eax, %edi
xor %eax, %eax
call _exit
main:
lea str(%rip), %rdi
call puts
我对实施有一些疑问:
-
调用
_start之前堆栈中的内容应该是链接器的唯一条目吗?我之所以问是因为存在诸如mov (%rsp), %edi #argc from stack之类的表达式,其中_start正在从堆栈中获取值,但_start不应该有任何argc(只有main有)也没有argv和@987654332 @。所有这些参数都是main函数的一部分,而不是_start入口点。那么_start之前的堆栈是什么? -
这应该被设计为提供来自
.data或.bss段的变量的初始化,但我在这里看不到它们的这种初始化。它可能与堆栈有关,但我不知道如何。在变量被初始化之前(应该在ctr0.o,这里),保持initial值和链接器为它们保留空间(也来自那个链接)。 gcc 在内存类型的哪个部分为那些未初始化的变量保留空间? -
最后,如何编译这个程序集,没有stdlib,但需要它的一些功能(
puts,_exit)才能工作?我试过cc -nostdlib foo.s但是/usr/bin/ld: /tmp/ccSKxoPY.o: in function `_start': (.text+0x21): undefined reference to `_exit' /usr/bin/ld: /tmp/ccSKxoPY.o: in function `main': (.text+0x2d): undefined reference to `puts' collect2: error: ld returned 1 exit status
(不能使用stdlib,否则会有2个_start入口点声明)。
【问题讨论】:
-
1) 参见例如this article 或 ELF 规范。 TL;DR:操作系统将参数、环境变量和其他东西放在堆栈上 2)
.data只是从文件中映射,.bss在调用入口点时由加载器分配并归零 3)不要那样做,即使在某些情况下它可能会起作用 -
不链接libc就不能调用libc函数。 (当然,除非您定义自己的版本。)没有魔法,它们只是共享或静态库中某些指令前面的标签。 (当他们使用
syscall指令调用内核代码时,魔法就会发生。) -
@PeterCordes 您能否给出完整的答案或详细说明您的评论?我为什么要
syscall,我的代码在哪里,它会添加什么,堆栈和syscall之间的关系是什么?您只是在没有上下文的情况下提出了一些想法,但我想要上下文(完整答案) -
关于问题3,如果你解释你想做什么,我们可以给出更好的答案。
-
也明白 crt0 是一个特定的术语和实现,而不是通用的语言。此外,编译器碰巧使用的调用约定并不一定要反映操作系统在如何传递命令行方面的设计选择。引导带必须符合操作系统,而不是相反。操作系统设计选择他们希望如何传递命令行等。