Eva*_*ran 5 c linux kernel system-calls inline-assembly
过去的情况是,如果你需要直接在 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; \
}
Run Code Online (Sandbox Code Playgroud)
然后你可以在代码中的某个地方放置:
_syscall3(ssize_t, write, int, fd, const void *, buf, size_t, count);
Run Code Online (Sandbox Code Playgroud)
这将为您定义一个write正确执行系统调用的函数。
似乎这个系统已经被更强大的东西(我猜测每个进程都会获得的“[vsyscall]”页面)取代。
那么程序在较新的 Linux 内核上直接执行系统调用的正确方法(请具体)是什么?我意识到我应该使用 libc 并让它为我完成工作。但我们假设我有充分的理由想知道如何做到这一点:-)。
好的,所以我进一步研究了它,因为我在这里没有得到太多回应,并发现了一些很好的信息。首先在linux中启动应用程序时,除了传统的argc、argv、envp参数之外。还有另一个数组传递了更多数据,称为 auxv。详细信息请参见此处。
这些键/值对之一的键相当于AT_SYSINFO。/usr/include/asm/auxvec.h在或 中定义/usr/include/elf。
与此键关联的值是系统调用函数的入口点(在映射到每个进程的“vdso”或“vsyscall”页面中)。
您可以用对此地址的调用替换传统的int 0x80or指令,它实际上会执行系统调用。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%确定在这种情况下系统调用函数的地址是如何确定的。