AOSP non-obvious syscall() implementation

…衆ロ難τιáo~ 提交于 2020-01-24 22:46:08

问题


As far as I know Linux ABI for ARM states that syscall return value is passed via r0 and if it is negative it should be threaded as errno value negated. I.e syscall has ended up with some error. AOSP does this check in some fancy way:

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)

Looks like this function won't threat r0 < -4096 as error condition. While it should. Of course real errno will fit into allowed gap, but anyway what was rationale to perform check in that way instead of just testing r0 for negativity?

P.S. If I've missed something or just my analysis is incorrect - any comments appreciated.


回答1:


Apper limit 4095 for errno value is intentional, and using this knowledge in the code is fine. Practically, this modifies model of value, returning by syscalls and some other kernel functions, to:

  • [-4095 ... -1] - error
  • other values - result

This model allows more possible values for result. E.g., if interpret result as unsigned long integer, then value's range for result is extended from [0... (MAX/2)] to [0 ... (MAX - 4096)]. This is useful, e.g. when result is a pointer, see implementation of IS_ERR macro.




回答2:


Typically the return value of mmap is negative if interpreted as a signed number.




回答3:


This is how Linux's system-call ABI/API is designed, to avoid using up half the range of possible return values for a small set of error codes. (Nobody will ever need more than 4095 errno codes?)

As @Timothy points out, mmap is a great example: it needs to return a pointer. mmap's return value is always page-aligned, so on systems with 4k pages or larger pages (i.e. almost(?) everything), -4095 is the highest magnitude negative number you can use without also being a valid page address.

-4095 = 2^32 - 4095 in a 32-bit 2's complement system. The highest page-start address is 2^32-4096. This is the obvious motivation for picking exactly this range as the ones that mean "error".

The range of values which mean error is in theory platform-dependent and part of the syscall ABI, but AFAIK Linux uses [-4095..-1] on every platform it supports. The MUSL libc implementation hard-codes -4096UL in a C source file that's not x86 or ARM-specific: /src/internal/syscall_ret.c. As @Tsyvarev points out, Linux defines MAX_ERRNO to 4095 in include/linux/err.h, not an arch-specific header. (A comment points out that it could be made arch-specific if it ever needs to be, and that it was specifically chosen because of how kernel pointers work.)

The x86-64 System V ABI does have an informative (not normative) section on Linux system calls, where this -4095 to -1 range is specified. (Search for -4095).

Fun fact: sys_getpriority can't return the nice level directly. Instead it returns 20-nice, which maps the -20..19 range of nice values to 40..1. Generic wrappers can check for errors with retval > -4096UL because all system calls avoid returning non-error values in that range, even if they have to jump through a hoop to do it.


Related:

  • What are the return values of system calls in Assembly?
  • Linux System Calls, Error Numbers, and In-Band Signaling (using x86-64 examples, but the concept is of course identical). The key concept here is In-Band Signaling.
  • The Definitive Guide to Linux System Calls (on x86)


来源:https://stackoverflow.com/questions/47551940/aosp-non-obvious-syscall-implementation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!