Linux从_获取终端参数开始不使用C中的内联程序集
我正在尝试使用内联汇编编写自己的启动函数。但当我试图从堆栈(%rsp和%rsp+8)读取argc和argv时,我得到了错误的值。我不知道我做错了什么Linux从_获取终端参数开始不使用C中的内联程序集,c,x86-64,inline-assembly,C,X86 64,Inline Assembly,我正在尝试使用内联汇编编写自己的启动函数。但当我试图从堆栈(%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);
#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"
...
知道我的错在哪里吗?
我使用的是Ubuntu20.04虚拟机,这看起来对一个最小的
\u开始是正确的:
,但是你把它放在一个非裸的C函数中。编译器生成的代码将在执行进入asm语句之前运行,例如,push%rbp
/mov%rsp,%rbp
。要了解这一点,请查看gcc-S
output,或是GDB之类的调试器中的单步操作
将asm语句放在全局范围内(如中),或在\u start()
上使用\u属性(裸))
。请注意,\u start
实际上不是一个函数
一般来说,不要在非裸函数中使用GNU C Basic asm语句。虽然您可以使用-O3
,因为这意味着-fomit帧指针
,所以在代码运行时堆栈仍然指向argc和argv
GNU/Linux上的动态链接可执行文件将从动态链接器挂钩运行libc启动代码,因此实际上可以从\u start
使用printf
,而无需手动调用那些init函数。与静态链接不同
但是,您的main
尝试返回到您的\u start
,但您没有显示\u start
调用退出。您应该调用exit
而不是直接进行_exit系统调用,以确保即使输出重定向到文件(使stdout完全缓冲),stdio缓冲区也会被刷新。从\u start
的末尾掉下来是不好的,会崩溃或进入无限循环,这取决于执行所处的位置。这对于最小的\u start:
看起来是正确的,但您将它放在一个非裸的C函数中。编译器生成的代码将在执行进入asm语句之前运行,例如,push%rbp
/mov%rsp,%rbp
。要了解这一点,请查看gcc-S
output,或是GDB之类的调试器中的单步操作
将asm语句放在全局范围内(如中),或在\u start()
上使用\u属性(裸))
。请注意,\u start
实际上不是一个函数
一般来说,不要在非裸函数中使用GNU C Basic asm语句。虽然您可以使用-O3
,因为这意味着-fomit帧指针
,所以在代码运行时堆栈仍然指向argc和argv
GNU/Linux上的动态链接可执行文件将从动态链接器挂钩运行libc启动代码,因此实际上可以从\u start
使用printf
,而无需手动调用那些init函数。与静态链接不同
但是,您的main
尝试返回到您的\u start
,但您没有显示\u start
调用退出。您应该调用exit
而不是直接进行_exit系统调用,以确保即使输出重定向到文件(使stdout完全缓冲),stdio缓冲区也会被刷新。从\u start
的末尾掉下来会很糟糕,会崩溃或进入无限循环,具体取决于执行所处的位置。您是否尝试过调试它并获取发生seg故障的PC?发生seg故障是因为rsi(argv)中的地址是随机地址,而不是指向[“一”、“二”、“三”]的指针,但是在System V abi中,它说这个指针应该在$rsp+8,如果您使用的是-nostartfiles
,那么很可能根本没有调用设置命令行参数处理的代码。@scrutari:您考虑的是Windows。在Unix系统(和GNU/Linux)上,命令arg不是一个简单的字符串,它们在进入用户空间时已与堆栈上的argc
和argv[]
分开(通过shell或其他方式传递给execve
)。这对于最小的\u start:
来说是正确的,如果OP没有将它放在将在asm语句之前使用堆栈的非裸
C函数中。切勿在非裸函数中使用GNU C Basic asm语句。您是否尝试过调试它并获取发生seg故障的PC?发生seg故障是因为rsi(argv)中的地址是随机地址,而不是指向[“一”、“二”、“三”]的指针,但是在System V abi中,它说这个指针应该在$rsp+8,如果您使用的是-nostartfiles
,那么很可能根本没有调用设置命令行参数处理的代码。@scrutari:您考虑的是Windows。在Unix系统(和GNU/Linux)上,命令arg不是一个简单的字符串,它们在进入用户空间时已与堆栈上的argc
和argv[]
分开(通过shell或其他方式传递给execve
)。这对于最小的\u start:
来说是正确的,如果OP没有将它放在将在asm语句之前使用堆栈的非裸
C函数中。切勿在非裸函数中使用GNU C Basic asm语句。
$ gcc test.c -nostartfiles
$ ./a.out one two three
0
Segmentation fault (core dumped)
$