【问题标题】:AOSP non-obvious syscall() implementationAOSP 非显而易见的 syscall() 实现
【发布时间】:2017-11-29 11:39:22
【问题描述】:

据我所知,Linux ABI for ARM 声明系统调用返回值是通过r0 传递的,如果它是负数,则它应该作为 errno 值被否定。即系统调用以一些错误告终。 AOSP does this check 以某种奇特的方式:

ENTRY(syscall)
    mov     ip, sp
    stmfd   sp!, {r4, r5, r6, r7}
    .cfi_def_cfa_offset 16
    .cfi_rel_offset r4, 0
    .cfi_rel_offset r5, 4
    .cfi_rel_offset r6, 8
    .cfi_rel_offset r7, 12
    mov     r7, r0
    mov     r0, r1
    mov     r1, r2
    mov     r2, r3
    ldmfd   ip, {r3, r4, r5, r6}
    swi     #0
    ldmfd   sp!, {r4, r5, r6, r7}
    .cfi_def_cfa_offset 0
    cmn     r0, #(MAX_ERRNO + 1) /* Set C flag if r0 is between -4095 and -1, set Z flag if r0 == -4096 */
    bxls    lr                   /* return if Z is set or C is clear */
    neg     r0, r0
    b       __set_errno_internal
END(syscall)

看起来这个函数不会威胁r0 < -4096 作为错误条件。虽然它应该。当然,真正的 errno 将符合允许的差距,但无论如何,以这种方式执行检查而不是仅仅测试 r0 的否定性的理由是什么?

附:如果我遗漏了什么或只是我的分析不正确 - 任何 cmet 表示赞赏。

【问题讨论】:

  • 根据特定的系统调用,其他负值可能不是错误。

标签: assembly linux-kernel arm system-calls libc


【解决方案1】:

这就是 Linux 的系统调用 ABI/API 的设计方式,只允许有限范围的 -errno 值,以避免为一小组错误代码使用一半的可能返回值范围。 (没有人需要超过 4095 个 errno 代码?)

正如@Timothy 指出的那样,mmap 是一个很好的例子:它需要返回一个指针。 mmap 的返回值始终是页面对齐的,因此在具有 4k 页面或更大页面的系统上(即几乎(?)所有内容),-4095 是您可以使用的最大量级负数,而不是有效的页面地址.

-4095 = 2^32 - 4095 在 32 位 2 的补码系统中。最高页起始地址为2^32-4096。这是准确选择 this 范围作为表示“错误”的范围的明显动机。

(但请注意,您仍然无法映射虚拟地址空间的最高页面even for a 32-bit process under a 64-bit kernel,即使-4096UL 将是来自mmap 的非错误返回。内核在内部 em> 对指针使用相同的带内-errno 代码机制,而不仅仅是系统调用 ABI,因此它保留首页以防止拥有也是错误代码的有效指针。这也避免了极端情况,例如指向数组过去一端的指针为 0 (NULL)。)


理论上,表示错误的值范围取决于平台,并且是系统调用 ABI 的一部分,但 AFAIK Linux 在它支持的每个平台上都使用[-4095..-1]。 MUSL libc 实现将-4096UL 硬编码在非 x86 或 ARM 特定的 C 源文件中:/src/internal/syscall_ret.c。

正如@Tsyvarev 指出的那样,Linux 将MAX_ERRNO 定义为4095 in include/linux/err.h,而不是特定于架构的标头。 (有评论指出,如果需要,它可以成为特定于架构的,并且由于内核指针的工作方式而专门选择了它。)

x86-64 System V ABI 还有一个关于 Linux 系统调用的信息性(非规范性)部分,其中指定了 -4095 到 -1 的范围。 (Search for -4095)。 (使用 x86-64 System V 的其他平台可以改变它们的系统调用 ABI,例如 FreeBSD 或 MacOS)


有趣的事实:这意味着sys_getpriority 不能直接返回nice 级别。而是it returns 20-nice, which maps the -20..19 range of nice values to 40..1。 libc 包装函数必须在通用 retval > -4096UL 检查检测到非错误返回值后解码 nice 值。


有趣的事实 #2:某些 Linux 平台(例如 MIPS 和 PowerPC)上的系统调用 ABI 在返回值中返回 -1,在单独的寄存器中返回 errno 代码。来源:本syscall inline-asm wrapper library中的cmets。 (看起来像一个不错的库;避免了一些系统调用包装头具有的诸如缺少 "memory" clobbers 之类的问题。)


相关:

【讨论】:

    【解决方案2】:

    如果将mmap 解释为有符号数,则通常返回值为负数。

    【讨论】:

      【解决方案3】:

      errno 值的 Apper 限制 4095 是故意的,在代码中使用这个知识就可以了。实际上,这会修改价值模型,通过系统调用和其他一些内核函数返回:

      • [-4095 ... -1] - 错误
      • 其他值 - 结果

      此模型允许更多可能的结果值。例如,如果将结果解释为无符号长整数,则结果的值范围从[0... (MAX/2)] 扩展到[0 ... (MAX - 4096)]。这很有用,例如当 result 是指针时,请参见 implementation 的 IS_ERR 宏。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-09-29
        • 1970-01-01
        • 2011-07-01
        • 2012-02-14
        • 2012-05-07
        • 2011-03-20
        • 2011-02-11
        • 2010-10-25
        相关资源
        最近更新 更多