AOSP 非显而易见的 syscall() 实现

Ser*_*gio 2 assembly arm libc system-calls linux-kernel

据我所知,ARM 的 Linux ABI 声明系统调用返回值是通过传递的r0,如果它是负数,它应该被线程化为 errno 值否定。即系统调用结束了一些错误。AOSP以某种奇特的方式执行此检查

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)
Run Code Online (Sandbox Code Playgroud)

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

PS 如果我遗漏了什么或者只是我的分析不正确 - 任何评论都表示赞赏。

Pet*_*des 6

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

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

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

(但请注意,您仍然无法mmap的虚拟地址空间的最高页,即使是32位进程64位内核下,即使-4096UL是从非返回错误mmap。内核内部使用相同的波段-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 ininclude/linux/err.h,而不是特定arch 的 header。(评论指出,如果需要的话,它可以是特定于架构的,并且是因为内核指针的工作方式而专门选择的。)

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


有趣的事实:这意味着sys_getpriority不能nice直接返回关卡。相反,它返回20-nice,它将-20..19nice 值的范围映射到40..1。在通用retval > -4096UL检查检测到非错误返回值后,libc 包装函数必须解码 nice值。


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


有关的: