Linux从_获取终端参数开始不使用C中的内联程序集

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);

我正在尝试使用内联汇编编写自己的启动函数。但当我试图从堆栈(%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"
...
知道我的错在哪里吗?
我使用的是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)
$