内联汇编访问系统时间后C段错误

C segmentation fault after accessing the system time by inline assembly

我一直在尝试在 Ubuntu 16.04 下使用 C 中的内联汇编来访问系统时间。我的代码如下所示:

struct timeval *date_time;

asm(
    "movl 6, %%eax;"
    "push [=10=];"
    "push %0;"
    "push [=10=];"
    "int [=10=]x80;"
    :
    :"b"(date_time)
);

假设我在特定函数中存储了上面的几行。每当我调用该函数时,它都会触发错误代码:

Segmentation fault (core dumped)

由于我是内联汇编的新手,我猜代码可能有问题。因此,如果您能指出我做错了什么,我将非常高兴。感谢您的所有建议。

您这里有 很多 个错误,其中最重要的是:

  • 您将东西压入堆栈,但在离开内联汇编块之前没有再次弹出它们。编译器不知道你这样做了,所以它会在错误的地方查找堆栈上的所有内容(例如 return 地址)。这很可能是导致崩溃的原因。

  • 更一般地说,使用这种内联汇编风格的编译器根本不解释汇编指令。他们相信你正确地使用了输入、输出、破坏注解。如果您甚至忘记提及 一个 已修改的寄存器或内存区域,编译器将围绕汇编插入生成不正确的代码,程序将无法运行。

  • "Ubuntu 16.04" 是 Linux 的分布,因此您使用了错误的调用约定。 Linux 在寄存器中获取系统调用参数,而不是在堆栈中,as documented here,并且 gettimeofday 不是 x86-32/Linux 上的系统调用编号 116。 (始终使用 sys/syscall.h 中的 SYS_foo 常量作为系统调用编号。)

另外,在实际插入的程序集中,最好尽量少。在这种情况下,这意味着 int 指令本身。相反,使用输入和输出约束设置参数。这为编译器提供了最大的优化余地。 (如果你手写汇编是因为编译器没有足够好的优化工作,你应该写一个完整的纯汇编的“.s”文件,而不是一个带有巨大汇编插入的.c文件;这更可维护。)

此任务的正确代码类似于

#include <assert.h>
#include <sys/time.h>
#include <sys/syscall.h>

struct timeval
call_gettimeofday()
{
    struct timeval ret;
    int dummy;
    asm("int [=10=]x80"
        : "=m" (ret), "=a" (dummy)
        : "1" (SYS_gettimeofday), "b" (&ret), "c" (0));
    assert(!dummy); // gettimeofday should never fail
    return ret;
}

最后一点,使用内联汇编进行系统调用几乎总是错误的。 C 库的包装器函数可能做的工作比您所看到的要多,并且他们知道如何在可能的情况下使用更有效的陷阱序列(使用 sysentersyscall 而不是 int) .在 gettimeofday 的情况下,差异更加深刻:C 库知道如何执行 gettimeofday 操作 而根本不会陷入内核!(阅读 vDSO 以了解这是如何实现的。)