glibc时间函数实现

时间:2017-07-03 20:55:50

标签: c++ linux gcc assembly glibc

我试图理解glibc中{(3}}

中time()的实现

如果展开宏(将鼠标悬停在它们上面),则会得到:

time_t time (time_t *t)
{
  unsigned long int resultvar;
  long int __arg1 = (long int) (t);
  register long int _a1 asm (""rdi"") = __arg1;
  asm volatile ( ""syscall\n\t"" : ""=a"" (resultvar) : ""0"" (201) , ""r"" (_a1) : ""memory"", ""cc"", ""r11"", ""cx"");
  (long int) resultvar;
}

装配命令显然必须返回当前时间,但我无法理解。检查英特尔手动系统调用我看到它是垃圾邮件r11和cx,其他垃圾邮件是gcc的东西,到那里,我很好。

从我读到的内容(https://code.woboq.org/userspace/glibc/sysdeps/unix/sysv/linux/x86/time.c.html#time)" 0"意味着某些东西被用作输入和输出,虽然我没有得到什么"(201)"意思是(我期待变量名,而不是数字)。有什么想法吗?

我不确定_a1是否需要信息,我认为它可以是NULL,因此汇编命令的唯一真正输入是"(201)",但我可以&# 39;告诉系统调用如何与201一起让我访问内部时钟。

感谢。

P.S。 奖金问题:从我在英特尔手册上看到的情况来看,我认为唯一可用的时钟应该是在MoBo而不是CPU硬件的一部分。我误解了吗?

2 个答案:

答案 0 :(得分:3)

这种看起来很复杂的内联汇编只会导致编译器发出以下汇编指令:

mov     eax, 201
syscall

因此,整个time函数只是:

time:
    mov     eax, 201
    syscall
    ret

立即值201(十六进制表示的0xC9)移入EAX寄存器,然后执行syscall指令。该指令正如名称所暗示的那样:它进行系统调用。这基本上就是你在Linux上调用平台API函数的方式。另见System V AMD64 ABI的A.2节(" AMD64 Linux内核约定")。

简而言之:

  • 系统调用ID号放在rax

    (在这种情况下,数字只有32位,因此汇编代码将其放入eax。高32位为implicitly zeroed,节省了{{1}的大小字节指令。)

  • 系统调用的参数(如果有)放在寄存器中:movrdirsirdxr10 ,和r8

    (在这种情况下,对于系统调用#201,没有需要指定的参数,因此r9函数不会初始化这些寄存器。)

  • 调用time后,其结果将包含在syscall中。通常,负值(-4095到-1)表示错误,对应于rax

  • 对于系统调用,−errnorcx寄存器被视为 volatile ,这意味着它们的内容可能会被破坏。如果调用者关心这些值,则需要保留它们。所有其他寄存器'值将在系统调用中保存。

    (这就是为什么在扩展的内联asm语法中使用clobbers的原因。)

有一个可用的64位Linux系统调用的引用here(32位Linux系统调用是here)。您可以看到201(0xC9)对应r11

sys_timesys_time寄存器解释为RDI值。这段代码:

time_t*

使函数的参数long int __arg1 = (long int) (t); register long int _a1 asm ("rdi") = __arg1; 存储在t寄存器中。但是,这实际上并不会导致生成任何机器指令,因为System V AMD64调用约定已经传递了RDI中函数的第一个参数,因此RDI已经 <{1}}中的

t系统调用只会填充它在RDI中找到的指针,这与sys_time函数的RDI参数相同。它还在time中返回其结果(错误代码),它总是用于System V AMD64调用约定下函数的返回值,因此也不需要机器指令。

也许更清楚:

t

答案 1 :(得分:0)

关于您的PS:所有当前的Intel CPU都有自己的时钟,可以使用rdtsc等指令读取。但是,它们是否可以用作实时时钟取决于执行环境的配置(例如,需要内核/管理程序支持,可能需要禁用某些省电状态,等等。)