_syscallNmacros从哪里进入<linux / unistd.h>?

它曾经是这样的情况,如果你需要直接在linux中进行系统调用而不使用现有的库,那么你可以包含<linux/unistd.h> ,它会定义一个类似这样的macros:

 #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,并让它为我做的工作。 但是,让我们假设我有一个体面的理由想要知道如何做到这一点:-)。

好,所以我进一步研究,因为我没有得到太多的回应,并找到了一些很好的信息。 首先当一个应用程序在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%确定在这种情况下如何确定系统调用函数的地址。