我正在尝试使用内联汇编编写自己的_start函数。但是,当我尝试从堆栈(%rsp和%rsp + 8)读取argc和argv时,我得到了错误的值。我不知道我在做什么错。
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <syscall.h>
int main(int argc, char *argv[]) {
printf("%d\n", argc);
printf("%s\n", argv[0]);
printf("got here\n");
return 0;
}
void _start() {
__asm__(
"xor %rbp, %rbp;"
"movl (%rsp), %edi;"
"lea 8(%rsp), %rsi;"
"xor %rax, %rax;"
"call main"
...
终端:
$ gcc test.c -nostartfiles
$ ./a.out one two three
0
Segmentation fault (core dumped)
$
知道我的错在哪里吗?我正在使用Ubuntu 20.04 VM
这看起来很正确,_start:
但是你将其放入了非naked
C函数中。在执行进入asm语句之前,编译器生成的代码将运行,例如push %rbp
/ mov %rsp, %rbp
。要查看此信息,请查看gcc -S
输出或调试器(如GDB)中的单步执行。
将你的asm语句放在全局范围内(例如,如何在不使用Glibc的C中使用内联汇编获取参数值?)或__attribute__((naked))
在上使用_start()
。请注意,这_start
并不是真正的功能
通常,切勿在非裸函数中使用GNU C Basic asm语句。尽管你可以使用它,-O3
因为这暗示着-fomit-frame-pointer
你的代码运行时,堆栈仍将指向argc和argv。
GNU / Linux上的动态链接的可执行文件将通过动态链接器挂钩运行libc启动代码,因此你实际上可以使用printf
from_start
而不用手动调用这些init函数。与静态链接不同。
但是,你main
尝试返回到_start
,但是你没有显示_start
呼叫exit
。你应该调用exit
而不是直接进行_exit系统调用,以确保即使将输出重定向到文件(也可以将stdout完全缓冲),也可以清除stdio缓冲区。跌落到最后_start
可能会很糟糕,崩溃或陷入无限循环,具体取决于执行的执行情况。