【问题标题】:What are the return values of system calls in Assembly?汇编中系统调用的返回值是什么?
【发布时间】:2021-05-01 14:04:13
【问题描述】:

当我尝试研究内核系统调用的返回值时,我发现了描述它们的表格以及我需要在不同的寄存器中放入什么才能让它们工作。但是,我没有找到任何说明 what 是我从系统调用获得的返回值的文档。我只是在不同的地方发现我收到的将在 EAX 寄存器中。


TutorialsPoint:

结果通常在 EAX 寄存器中返回。

Assembly Language Step-By-Step: Programming with Linux 一书 Jeff Duntemann 在他的程序中多次指出:

  • 在EAX中查看sys_read的返回值

  • 复制 sys_read 返回值以备不时之需


我没有解释这个返回值的任何网站。有没有网络资源?或者有人可以向我解释一下这个价值观吗?

【问题讨论】:

  • 尝试谷歌搜索“sys_read”。这是我发现的东西。 asm.sourceforge.net/syscall.html
  • @MarkLakata 我试过但没有帮助。正如我所说,它只说明了我需要在不同的寄存器中放入什么以便系统调用工作。 (例如herehere。)
  • 如果你用谷歌搜索“sys_read”,第 4 个链接是这样的:lwn.net/Articles/604287,它包含所有血淋淋的细节。
  • @MarkLakata:感谢您的链接;添加到我的答案中。

标签: linux assembly return-value nasm system-calls


【解决方案1】:

另请参阅this excellent LWN article about system calls,它假定 C 知识。

还有:The Definitive Guide to Linux System Calls (on x86),以及相关的:What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?


C 是 Unix 系统编程的语言,所以所有的文档都是用 C 来写的。然后在任何给定的平台上,C 接口和 asm 之间的细微差别都有文档,通常在手册页的注释部分.

sys_read 表示原始系统调用(与 libc 包装函数相反)。 read 系统调用的内核实现是一个名为sys_read() 的内核函数。您不能使用call 指令调用它,因为它在内核中,而不是库中。但是人们仍然谈论“调用sys_read”以将其与libc函数调用区分开来。但是,即使您指的是原始系统调用(尤其是当 libc 包装器没有做任何特别的事情时),也可以说 read,就像我在这个答案中所做的那样。

还请注意,syscall.h 使用实际系统调用号定义了诸如 SYS_read 之类的常量,或者为 Linux 定义了 asm/unistd.h__NR_read 名称为相同的常量。 (在 int 0x80syscall 指令之前放入 EAX 的值)。


Linux 系统调用返回值(在 x86 上的 EAX/RAX 中)要么是“正常”成功,要么是 -errno code 错误。例如-EFAULT 如果你传递了一个无效的指针。此行为记录在 syscalls(2) 手册页中。

-1 到 -4095 表示错误,其他都表示成功。有关此 -4095UL .. -1UL 范围的更多详细信息,请参阅 AOSP non-obvious syscall() implementation,该范围可在 Linux 上跨体系结构移植,并适用于 每个 系统调用。 (在未来,不同的架构可以为 MAX_ERRNO 使用不同的值,但现有架构(如 x86-64)的值保证与保持内核 ABI 稳定的 Linus 不破坏用户空间策略的一部分保持相同。 )

例如,glibc 的 generic syscall(2) 包装函数使用以下序列:cmp rax, -4095 / jae SYSCALL_ERROR_LABEL,对于所有 Linux 系统调用,它是 guaranteed to be future-proof

您可以使用该包装函数进行任何系统调用,例如syscall( __NR_mmap, ... )。 (或者使用像 https://github.com/linux-on-ibm-z/linux-syscall-support/blob/master/linux_syscall_support.h 这样的 inline-asm 包装标头,它对多个 ISA 具有安全的 inline-asm,避免出现其他一些 inline-asm 包装器所具有的缺失 "memory" clobbers 等问题。)


有趣的案例包括getpriority,其中内核 ABI 将 -20..19 返回值范围映射到 1..40,然后 libc 对其进行解码。更多详情请见a related answer about decoding syscall error return values

对于 mmap,如果您愿意,您还可以通过检查返回值是否不是页面对齐来检测错误(例如,对于 4k 页面大小,低 11 中的任何非零位),如果是这样的话比检查p > -4096ULL 更有效。


要查找特定平台的常量的实际数值,您需要找到它们所在的 C 头文件 #defined。有关详细信息,请参阅my answer on a question about that。例如在asm-generic/errno-base.h / asm-generic/errno.h.


每个 sys 调用的返回值的含义都记录在第 2 节手册页中,例如 read(2)。 (sys_read 是 glibc read() 函数对其进行非常薄的包装的原始系统调用。)大多数手册页都有一个完整的返回值部分。例如

返回值

成功时,返回读取的字节数(零表示 文件结尾),并且文件位置由这个数字提前。它 如果此数字小于字节数,则不是错误 要求;这可能会发生,例如,因为更少的字节是
现在实际可用(可能是因为我们接近尾声-
文件,或者因为我们正在从管道或终端读取),或者
因为 read() 被信号打断了。另见注释。

出错时返回-1,并适当设置errno。在这个 情况下,未指定文件位置(如果有)
变化。

请注意,最后一段描述了 glibc 包装器如何解码值,如果原始系统调用的返回值为负数,则将 sets errno 解码为 -EAX,因此如果原始系统调用返回 @,则返回 errno=EFAULT 和返回 -1 987654370@.

还有一整节列出了read() 允许返回的所有可能的错误代码,以及它们对read() 的具体含义。 (POSIX 标准化了大部分这种行为。)

【讨论】:

  • 1. sys_read 是否等于read()? 2、什么是-errno-EFAULT-retval?它与大会有什么关系?
  • @PichiWuana:1. 是的,当然。 2. C 是 Unix 系统编程的语言。一切都用 C 语言记录。 C 基本上只是可​​移植的汇编语言。 errno is a global 在任何库函数遇到错误后设置。 - 是 C 一元否定运算符。 EFAULT 是一个由 C 预处理器宏定义的整数常量。 retval 是我编造的一个变量名,用于描述 EAX 在系统调用后持有的内容。
  • 这是否意味着如果您了解良好的汇编编程,您将很容易理解 C?反之亦然?
  • @PichiWuana:嗯,C 本身的可移植性太高了,所以有很多规则,比如有符号溢出是未定义的行为,有符号类型的右移是实现定义的,不管它们是否这样做算术或逻辑移位。但是,是的,了解 asm 有助于理解 C 的工作原理,反之亦然。 C 中的指针和数组的工作方式与 asm 中的相同。两者都知道,我可以很容易地看到简单的 C 函数将如何编译为 asm。您甚至可以将这些知识用于hand-hold a compiler into making better asm by tweaking the source
  • @PichiWuana:稍微更新了我的答案,假设 C 知识较少。我只是假设这部分很明显,因为在我了解它在 asm 中的实际工作原理之前,我知道 C 中的 Unix 系统编程。
猜你喜欢
  • 1970-01-01
  • 2018-04-13
  • 2020-06-07
  • 2016-01-16
  • 2020-08-17
  • 2010-12-18
  • 1970-01-01
  • 2012-01-24
相关资源
最近更新 更多