Linux 从 _start 获取终端参数无法在 C 中使用内联汇编

Linux getting terminal arguments from _start not working with inline assembly in C

我正在尝试使用内联汇编编写自己的 _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 函数中。编译器生成的代码将 运行,例如push %rbp / mov %rsp, %rbp,执行前进入asm语句之前。要查看此内容,请查看 gcc -S 输出,或在 GDB 等调试器中单步执行。

将您的 asm 语句放在全局范围内(如在 How Get arguments value using inline assembly in C without Glibc? 中)或在您的 _start() 上使用 __attribute__((naked))。请注意 _start 并不是真正的函数

作为规则,永远不要在非裸函数中使用 GNU C Basic asm 语句。尽管您可能会使用 -O3 来使用它,因为这意味着 -fomit-frame-pointer 所以当您的代码 运行.

时,堆栈仍将指向 argc 和 argv

GNU/Linux 上的动态链接可执行文件将从动态链接器挂钩 运行 libc 启动代码,因此您实际上 可以 使用 printf 来自_start 无需手动调用那些初始化函数。与静态链接不同。

然而,您的 main 试图 return 到您的 _start,但您没有显示 _start 呼叫 exit。您 应该 调用 exit 而不是直接进行 _exit 系统调用,以确保即使输出被重定向到文件(使 stdout 完全缓冲)也能刷新 stdio 缓冲区。从 _start 的末尾掉下来会很糟糕,崩溃或进入无限循环取决于执行的结果。