【问题标题】:where did the _syscallN macros go in <linux/unistd.h>?<linux/unistd.h> 中的 _syscallN 宏在哪里?
【发布时间】:2011-02-22 15:50:32
【问题描述】:

过去的情况是,如果您需要在不使用现有库的情况下直接在 linux 中进行系统调用,您可以只包含 &lt;linux/unistd.h&gt;,它会定义一个类似这样的宏:

#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
type name(type1 arg1,type2 arg2,type3 arg3) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
  : "=a" (__res) \
  : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
      "d" ((long)(arg3))); \
if (__res>=0) \
  return (type) __res; \
errno=-__res; \
return -1; \
}

然后你可以在你的代码中放一个地方:

_syscall3(ssize_t, write, int, fd, const void *, buf, size_t, count);

这将为您定义一个正确执行系统调用的write 函数。

似乎这个系统已经被某些东西取代了(我猜是每个进程都会获得的“[vsyscall]”页面)更健壮。

那么程序在较新的 linux 内核上直接执行系统调用的正确方法是什么(请具体说明)?我意识到我应该使用 libc 并让它为我完成工作。但是让我们假设我有充分的理由想知道如何做到这一点:-)。

【问题讨论】:

    标签: c linux kernel inline-assembly system-calls


    【解决方案1】:

    好的,所以我进一步研究了它,因为我在这里没有得到太多回应,并找到了一些很好的信息。首先在linux中启动应用程序时,除了传统的argc、argv、envp参数。还有另一个数组传递了一些称为 auxv 的更多数据。详情请见here

    其中一个键/值对具有等效于 AT_SYSINFO 的键。在/usr/include/asm/auxvec.h/usr/include/elf 中定义。

    与此键关联的值是系统调用函数的入口点(在映射到每个进程的“vdso”或“vsyscall”页面中。

    您可以将传统的int 0x80syscall 指令替换为对该地址的调用,它实际上会执行系统调用。不幸的是,这很丑陋。所以 libc 的人想出了一个很好的解决方案。当他们分配TCB 并将其分配给gs 段时。他们将AT_SYSINFO 的值放在TCB 中的某个固定偏移量中(不幸的是,它不是跨版本固定的,因此您不能总是依赖于相同的偏移量)。因此,您可以直接说call *%gs:0x10,而不是传统的int 0x80,它将调用vdso 部分中的系统调用例程。

    我想这里的目标是让编写 libc 更容易。这允许 libc 人员编写一个代码块来处理系统调用,而不必再担心它。内核人员可以随时更改系统调用的完成方式,他们只需要更改vdso 页面的内容即可使用新机制就可以了。事实上,您甚至不需要重新编译您的 libc!然而,这确实让我们编写内联汇编并试图在引擎盖下玩东西的人感到很痛苦。

    幸运的是,如果您真的想要手动操作,旧方法仍然有效:-)。

    编辑: 我在实验中注意到的一件事是 AT_SYSINFO 似乎没有被赋予我的 x86_64 盒子上的程序(AT_SYSINFO_EHDR 是,但我是还不知道如何利用它)。所以我不是100%确定这种情况下系统调用函数的地址是如何确定的。

    【讨论】:

      猜你喜欢
      • 2023-03-12
      • 2018-09-25
      • 1970-01-01
      • 2023-04-03
      • 1970-01-01
      • 2015-12-27
      • 1970-01-01
      • 2012-05-30
      • 1970-01-01
      相关资源
      最近更新 更多