【问题标题】:What is the return value in rax after a failed Fork syscall?Fork 系统调用失败后,rax 中的返回值是多少?
【发布时间】:2017-06-28 18:50:52
【问题描述】:

我知道如果在 C 中调用 fork() 会返回 -1 如果有错误,但我想知道当你在汇编中调用 sys_fork 时错误返回值是什么。

我通常会假设它也返回 -1,但我已经处理了 sys_brk 并且程序集中的原始系统调用返回了与 C Brk() 包装器不同的东西。

有谁知道汇编中的 fork 错误返回值是什么?

(我在 Linux 上做 64 位 NASM 汇编)

【问题讨论】:

  • IIRC,通常是 Linux 系统调用,至少在 ia32 和 x86_64 中,在失败时返回负的 errno 值(然后 glibc 包装器再次否定并存储到 errno)。跨度>
  • 根据x86_64 ABI,返回值在EAXRAX。另见Where is the X86-64 ABI documented?
  • 根据ABI,@jww,他知道返回值在RAX。问题中说明了这一点。问题是如何解释RAX 中的值。换句话说,它是什么意思?
  • @Daniel Schepler 听起来你在说它会在失败时返回一些低于零的值,具体取决于遇到的错误类型。因此,如果我不关心到底出了什么问题,我可以这样做:“cmp rax, 0 (next line) jl sys_fork_error”?
  • @old_timer 就像它对 C fork() 系统调用所做的那样,它在子进程中返回 0,在父进程中返回 pid。但这是一个成功的调用。我不知道如何强制 fork() 失败并检查返回值。

标签: linux assembly x86-64 system-calls


【解决方案1】:

首先要注意,C 库包装器 fork(2) 调用 sys_clone 而不是 sys_fork

C 库/内核差异

从 2.3.3 版本开始,而不是调用内核的 fork() 系统
调用,作为 NPTL 的一部分提供的 glibc fork() 包装器
线程实现使用提供
的标志调用clone(2) 与传统系统调用的效果相同。


introduction to the section 2 of the Linux manual 解释了如何在一般上下文中解释系统调用的返回值:

返回值

发生错误时,大多数系统调用都会返回负错误号(即
errno(3) 中描述的常数之一的取反值。 C
库包装器对调用者隐藏了这个细节:当系统调用
返回负值,包装器将绝对值复制到
errno 变量,并返回 -1
作为
的返回值 包装。

所以对于大多数系统调用,EAX/RAX 在错误时保持-ESOMETHING,或在成功时保持非负结果。 libc 包装器对此进行解码以实现第 2 节手册页中描述的 errno 设置和返回 -1 行为,该手册主要记录包装器; “C 库/内核差异”的详细信息有时可以在 Linux 手册页的“注释”部分找到。

重要的是要注意,正如第一段所说,这适用于大多数但不是所有系统调用。 sys_fork 在这方面并不特别。几个有趣的特殊情况是getpriorityerrno(3)更多下文开头提到的mmap,和mmap。 (有效指针可以设置其高位,因此区分错误和成功需要其他技巧,like checking the low bits since a successful mmap always returns a page-aligned address。)这些 ABI 详细信息未记录在手册页中。


为了确定sys_fork 调用是否成功,测试负值就足够了:

test eax, eax
jl _error_handler              ;See Peter Cordes's comments below

我已经包含了有关 C 库包装器的部分,例如 fork(2),因为它提供了一种找出错误号的实用方法。
errno 的取反值是系统调用可能返回的错误值。

再次
ENOMEM
ENOSYS
ERESTARTNOINTR

通常,C 库包装器可以在将返回值写入errno 之前添加、删除或转码,因此这是失败的。

找出可能的返回值的确定方法是查看源。
例如,sys_fork 调用的do_fork 除了上面列出的值之外,还可以返回EINVALEPERM
其他值是可能的,我还没有深入研究所有的嵌套函数调用。

sys_clone 也调用do_fork 所以我假设 clone(2) 可以返回fork(2) 可以返回的所有错误号。


调查上面提到的sys_getpriority 的情况出现了一条评论

/*
 * Ugh. To avoid negative return values, "getpriority()" will
 * not return the normal nice-value, but a negated value that
 * has been offset by 20 (ie it returns 40..1 instead of -20..19)
 * to stay compatible.
 */

因此,似乎 Linux 系统调用总是在错误时返回负值,C 库包装器试图将这些值标准化为 errno,引入了额外的复杂层。

正如我们从mmap 案例中看到的,设置符号位并不总是意味着它是一个错误值。 根据this answer(下面的 Peter Cordes 评论的优点),[-4095, -1] 范围内的值总是意味着错误,但其他负值不应该。

【讨论】:

  • 您确定需要测试完整的rax,而不仅仅是eaxpid_t 是 32 位类型,所以我认为在这里避免使用 REX 前缀是安全的。 (实际上,您确定返回值完全符号扩展为 64 位吗?)
  • @PeterCordes 老实说,我相信问题下的 cmets 认为在最坏的情况下 PID 应该像 Windows 中的句柄一样进行符号扩展。我会用你的输入来纠正答案。谢谢!
  • 我在 amd64 Linux 4.11 上检查了 sys_open(argv[1], 0 /*O_RDONLY*/) (NASM source)。 即使open(2) 返回int,负的系统调用返回值确实是符号扩展至整个RAX。不过,如果返回值是带符号的 32 位整数,您不妨检查 eax
  • 您关于“大多数”但并非全部的观点让我想到了 mmap。我找到了a blog post 的详细信息。 musl libc 显然使用 if (r > -4096UL) 进行 all 系统调用来检测错误,除非有任何特殊情况不使用通常的帮助函数将此类返回值解码为 errno 并返回 @987654369 @。我对您的答案进行了修改,但您可能还想再修改一下。
  • 另见stackoverflow.com/questions/18996410/…。显然,从 -1 到 -4095 的值总是意味着错误,而其他值总是意味着非错误。我想这就是为什么mmap 没有提到将返回值解码为特例,但getpriority 需要使用偏移量对返回值进行编码。 (我希望我在花时间进行编辑之前就发现了这一点。我真的不想再进行一次编辑,但也许你会想要。)
猜你喜欢
  • 1970-01-01
  • 2012-08-20
  • 2021-02-18
  • 2012-03-10
  • 1970-01-01
  • 2023-03-31
  • 2012-02-11
  • 2012-02-02
  • 2015-12-22
相关资源
最近更新 更多