asmlinkage 在 Linux 代码中定义为空

asmlinkage defined to nothing in Linux code

我正在研究 asmlinkage 标签。

来自https://kernelnewbies.org/FAQ/asmlinkage

This is a #define for some gcc magic that tells the compiler that the function should not expect to find any of its arguments in registers (a common optimization), but only on the CPU's stack.

查看定义,它被定义为空 x86_64:

#ifdef __cplusplus
#define CPP_ASMLINKAGE extern "C"
#else
#define CPP_ASMLINKAGE
#endif

#ifndef asmlinkage
#define asmlinkage CPP_ASMLINKAGE
#endif

此外,我读到 ABI 规范说我们应该将系统调用号放在一个寄存器中,将参数放在其他寄存器中。

那我们为什么要在栈中寻找函数参数。系统调用号是不是也入栈了?

asmlinkage tells your compiler to look on the CPU stack for the function parameters, instead of registers

事实上,它使用了GCC的regparam属性(Function-Attributes),或者syscall_linkage for IA64:

#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))

但是你的问题是为什么这是必要的。系统调用是用户space 可以调用以请求内核为他们执行某些操作的服务(因此在内核space 中执行)。从某种意义上说,这些函数非常不正统,因为您不能期望它们的行为像普通函数一样,在普通函数中,参数通常通过写入程序堆栈来传递,而是写入寄存器。虽然仍在 userspace 中,但调用系统调用需要将某些值写入某些寄存器已被翻译。系统调用号将始终写入 eax,而其余参数将写入 ebx、ecx 等。 现在,当发生软件中断时,CPU 切换到内核模式,然后执行 system_call()。当 CPU 切换到内核模式时,它首先将所有寄存器保存在 CPU 堆栈中(eax、ebx、ecx 等)。检查验证参数等其他事情后,如果一切都在 order.So, 中,它将调用相应的系统调用,因为所有关于参数的信息从用户 space 一直传递到这个点很好地存储在堆栈中,编译器必须被指示,因此 asmlinkage

另外一个原因是内核在处理用户[=27=的系统调用请求时无论如何都需要将所有的寄存器保存到堆栈中(以便在返回给用户之前恢复环境space) ], 所以参数在栈上可用后,就不需要额外的努力了。

系统调用不是用户空间程序或共享库的一部分,它们是由内核提供的系统服务,在内核 space 中执行 .. 为了从用户 space 移动到kernel space 需要执行一些特殊的汇编指令来告诉处理器跳转到管理模式,在此之前它需要将一些信息压入寄存器以便内核端知道要执行什么系统调用- 它有一个 table 函数指针,指向 'service' 每个系统调用的各种函数)-

#include "SYS.h"

ENTRY(syscall)
        pop     %ecx    /* rta */
        pop     %eax    /* syscall number */
        push    %ecx
        KERNCALL
        push    %ecx    /* need to push a word to keep stack frame intact
                           upon return; the word must be the return address. */
        jb      1f
        ret

其中 KERNCALL 依赖于体系结构,是一些汇编语言指令,告诉 cpu 跳转到内核中的管理员模式 space -

./lib/libc/amd64/SYS.h:#define  KERNCALL    movq %rcx, %r10; syscall
./lib/libc/i386/SYS.h:#define   KERNCALL    int [=11=]x80

所以事情是这样的..当你编译一个程序时,优化器偶尔会把函数的参数扔进寄存器而不是把它们放在程序的堆栈上..这个优化之所以有效,是因为编译器为调用者和调用者都发出了代码被叫者和双方都知道这种轻微的手。然而对于内核来说不是这样......它不知道在哪个寄存器中寻找什么,因此用于系统调用的所有参数必须在用户态程序的堆栈上。