Warm tip: This article is reproduced from serverfault.com, please click

x86 64-Linux从_start获取终端参数不适用于C中的内联汇编

(x86 64 - Linux getting terminal arguments from _start not working with inline assembly in C)

发布于 2020-12-06 15:23:00

我正在尝试使用内联汇编编写自己的_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

Questioner
Lantanar
Viewed
0
Peter Cordes 2020-12-07 00:02:22

这看起来很正确,_start:但是你将其放入了非nakedC函数中。在执行进入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启动代码,因此你实际上可以使用printffrom_start而不用手动调用这些init函数。与静态链接不同。

但是,你main尝试返回到_start,但是你没有显示_start呼叫exit应该调用exit而不是直接进行_exit系统调用,以确保即使将输出重定向到文件(也可以将stdout完全缓冲),也可以清除stdio缓冲区。跌落到最后_start可能会很糟糕,崩溃或陷入无限循环,具体取决于执行的执行情况。