系统调用的良好参考

时间:2011-08-01 23:38:05

标签: linux assembly reference nasm

我需要一些参考但是很好的参考,可能有一些很好的例子。我需要它因为我开始使用NASM汇编程序在汇编中编写代码。我有这个参考:

http://bluemaster.iu.hio.no/edu/dark/lin-asm/syscalls.html

这是非常好用的,但它有很多限制,因为它没有解释其他寄存器中的字段。例如,如果我使用写入系统调用,我知道我应该在EAX寄存器中放置1,并且ECX可能是指向字符串的指针,但是EBX和EDX呢?我想要解释一下,EBX确定输入(stdin为0,其他为1等),EDX是要输入的字符串的长度等等。我希望你理解我想要的东西,我找不到任何这样的材料,所以这就是我在这里写的原因。 提前谢谢。

2 个答案:

答案 0 :(得分:10)

Linux中的标准编程语言是C.因此,系统调用的最佳描述将它们显示为要调用的C函数。鉴于它们作为C函数的描述以及如何将它们映射到汇编中的实际系统调用的知识,您将能够轻松地使用任何系统调用。

首先,您需要一个对C程序员看来的所有系统调用的引用。我所知道的最好的是Linux man-pages project,特别是system calls部分。

我们以write系统调用为例,因为它是您问题中的一个。如您所见,第一个参数是有符号整数,通常是open系统调用返回的文件描述符。这些文件描述符也可以从父进程继承,通常发生在前三个文件描述符中(0 = stdin,1 = stdout,2 = stderr)。第二个参数是指向缓冲区的指针,第三个参数是缓冲区的大小(作为无符号整数)。最后,该函数返回一个有符号整数,即写入的字节数,或错误的负数。

现在,如何将其映射到实际的系统调用?有许多方法可以在32位x86上进行系统调用(根据您的寄存器名称,这可能是您正在使用的);请注意,它在64位x86上完全不同(确保您在32位模式下组装并链接32位可执行文件;请参阅this question以获取其他方法可能出错的示例)。 32位x86中最古老,最简单和最慢的是int $0x80方法。

对于int $0x80方法,您将系统调用号码放在%eax中,参数放在%ebx%ecx%edx,{{1按此顺序,},%esi%edi。然后调用%ebp,系统调用的返回值在int $0x80。请注意,此返回值与引用所示的不同;该引用显示了C库将如何返回它,但系统调用在出错时返回%eax(例如-errno)。在这种情况下,C库会将其移至-EINVAL并返回errno。有关详细信息,请参阅syscalls(2)intro(2)

因此,在-1示例中,您将write系统调用号放在write中,第一个参数(文件描述符号)在%eax中,第二个%ebx中的参数(指向字符串的指针),以及%ecx中的第三个参数(字符串的长度)。系统调用将在%edx中返回写入的字节数或否定的错误号(如果返回值介于-1和-4095之间,则它是一个否定的错误号)。

最后,您如何找到系统呼叫号码?他们可以在%eax找到。在我的系统上,这只包含/usr/include/linux/unistd.h,最后包含/usr/include/asm/unistd.h,因此数字就在那里(对于/usr/include/asm/unistd_32.h,您可以看到write__NR_write) 。同样适用于来自4的错误编号(在我的系统上,追逐包含链后,我在/usr/include/linux/errno.h找到第一个,其余在/usr/include/asm-generic/errno-base.h)。对于使用其他常量或结构的系统调用,它们的文档告诉您应该查看哪些标题以查找相应的定义。


现在,正如我所说,/usr/include/asm-generic/errno.h是最古老,最慢的方法。较新的处理器具有更快的特殊系统调用指令。为了使用它们,内核提供了一个虚拟动态共享对象(int $0x80;它就像一个共享库,但只在内存中),它有一个函数可以调用,使用可用的最佳方法进行系统调用你的硬件。它还提供特殊功能来获取当前时间,甚至不需要进行系统调用,以及其他一些事情。当然,如果您不使用动态链接器,则使用起来会有点困难。

还有另一种较旧的方法vDSO,它与vsyscall类似,但在固定地址使用单个页面。不推荐使用此方法,如果您使用的是最新的内核,则会在系统日志中出现警告,可能会在更新的内核上启动时禁用,并且将来可能会被删除。不要使用它。

答案 1 :(得分:0)

如果您下载该网页(如第二段中所示)并下载内核源代码,您可以单击“源”列中的链接,然后直接转到实现系统调用的源文件。您可以阅读他们的C签名,以查看每个参数的用途。

如果您只是在寻找快速参考,那么每个系统调用都有一个C库接口,其名称相同,加上sys_。因此,举例来说,您可以查看man 2 lseek以获取有关sys_lseek参数的信息:

off_t lseek(int fd, off_t offset, int whence);

其中,如您所见,参数与HTML表中的参数匹配:

%ebx           %ecx    %edx
unsigned int   off_t   unsigned int