【问题标题】:Good references for the syscalls系统调用的良好参考
【发布时间】:2011-10-17 20:33:36
【问题描述】:

我需要一些参考资料,但是一份很好的参考资料,可能还有一些很好的例子。我需要它,因为我开始使用 NASM 汇编器编写汇编代码。我有这个参考:

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

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

【问题讨论】:

    标签: linux assembly reference nasm


    【解决方案1】:

    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%esi%edi%ebp中,以该顺序。然后调用int $0x80,系统调用的返回值是%eax。请注意,此返回值与参考中所说的不同;该参考显示了 C 库将如何返回它,但系统调用在错误时返回 -errno(例如 -EINVAL)。在这种情况下,C 库会将其移动到 errno 并返回 -1。请参阅syscalls(2)intro(2) 了解更多详情。

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

    最后,如何找到系统调用号?他们可以在/usr/include/linux/unistd.h 找到。在我的系统上,这只是包括/usr/include/asm/unistd.h,最后包括/usr/include/asm/unistd_32.h,所以数字在那里(对于write,你可以看到__NR_write4)。来自/usr/include/linux/errno.h 的错误编号也是如此(在我的系统上,在追逐包含链后,我在/usr/include/asm-generic/errno-base.h 找到第一个错误编号,其余的在/usr/include/asm-generic/errno.h 找到)。对于使用其他常量或结构的系统调用,它们的文档会告诉您应该查看哪些标头以找到相应的定义。


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

    还有另一种较旧的方法vsyscall,它类似于vDSO,但使用固定地址的单个页面。此方法已弃用,如果您使用的是最新内核,则会在系统日志中显示警告,甚至可以在更新的内核上在引导时禁用,并且将来可能会被删除。不要使用它。

    【讨论】:

    • 这是我见过的最好的答案之一。
    【解决方案2】:

    如果您下载该网页(如第二段中建议的那样)并下载内核源代码,则可以单击“源代码”列中的链接,并直接转到实现系统调用的源文件。您可以阅读他们的 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
    

    【讨论】:

    • 当然,即使您在 google 或任何源代码搜索引擎中搜索每一个以实现它,您也可以找到如何使用它们的直接示例——我就是这样做的。我的帖子的重点是使用一些不错的备忘单跳过搜索和分析。谢谢。
    猜你喜欢
    • 2010-09-09
    • 2011-02-03
    • 1970-01-01
    • 2010-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-16
    相关资源
    最近更新 更多