_syscallN 宏到哪里去了<linux/unistd.h>?

问题描述 投票:0回答:1

过去的情况是,如果你需要直接在 Linux 中进行系统调用而不使用现有的库,你可以只包含

<linux/unistd.h>
,它会定义一个类似于这样的宏:

#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个回答
5
投票

编辑

对于 64 位系统,有关

AT_SYSINFO
的信息不再与 x86-64 相关,只有一种触发系统调用的机制,
syscall
。例如,关于其工作原理的代码,内核现在有一个名为
nolibc
的仅标头库,其中包含用于执行系统调用的代码:

https://elixir.bootlin.com/linux/v6.8.9/source/tools/include/nolibc/arch-x86_64.h


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

这些键/值对之一的键相当于

AT_SYSINFO
。在
/usr/include/asm/auxvec.h
/usr/include/elf
中定义。

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

您可以用对此地址的调用替换传统的

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

我想这里的目标是让编写 libc 更容易。这使得 libc 人员可以编写一个代码块来处理系统调用,而不必再担心它。内核人员可以随时更改系统调用的完成方式,他们只需更改

vdso
页面的内容即可使用新机制,然后就可以了。事实上,您甚至不需要重新编译您的 libc!然而,这确实让我们编写内联汇编并尝试使用底层的东西变得很痛苦。

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

编辑:我在实验中注意到的一件事是

AT_SYSINFO
似乎没有提供给我的x86_64盒子上的程序(
AT_SYSINFO_EHDR
是,但我不知道如何利用还没有)。所以我不能100%确定在这种情况下系统调用函数的地址是如何确定的。

© www.soinside.com 2019 - 2024. All rights reserved.