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

标签 assembly linux-kernel arm system-calls libc

据我所知,ARM 的 Linux ABI 声明系统调用返回值是通过 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消极情绪?

附言如果我遗漏了什么或者只是我的分析不正确 - 欢迎提出任何意见。

最佳答案

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

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

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

(但请注意,您仍然无法映射虚拟地址空间的最高页面 even for a 32-bit process under a 64-bit kernel,即使 -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 in include/linux/err.h ,而不是特定于架构的 header 。 (一条评论指出,如果需要的话,它可以成为特定于体系结构的,并且它是由于内核指针的工作方式而被特别选择的。)

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 . (看起来是一个不错的库;避免了诸如缺少 "memory" 某些系统调用包装器 header 所具有的破坏之类的问题。)


相关:

关于assembly - AOSP 非显而易见的 syscall() 实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47551940/

相关文章:

c/c++中的Android系统应用程序?

assembly - CMPXCHG——忽略 ZF 标志安全吗?

c - 数据段前未初始化的可写数据

c - libopcodes : get the size of a instruction

c - 使用 x86 asm 制作 MMAP 宏

c - 用 C 语言复制 Spinrite 动画效果

Android:杀死(所有)前台运行的应用程序

linux-kernel - 在网络编程的上下文中,什么是 "bluebook"?

android - Android 的 Linux rt- 补丁有人吗?

assembly - 如何确定 ARMv8 处理器是否支持 AArch32 执行状态?