为什么32位下的系统调用要用asmlinkage

为什么32位下的系统调用要用asmlinkage

asmlinkage是告诉编译器参数将通过堆栈传递。kernel 里的system call函数中(C 函数),为什么每一个函数原型的前面都有一个"asmlinkage" 的tag?例如:

asmlinkage long sys_nice(int increment)

“asmlinkage” 是在i386 system call 实现中相当重要的一个gcc 标签(tag)。

当system call handler(系统调用处理程序)要call相对应的system call routine(系统调用例程)时,便将一般用途的暂存器的值push 到stack 里,因此system call routine 就要从stack来读取system call handler 传递的参数。这就是asmlinkage 标签的用意。因为32体系结构,

system call handler 是assembly code,system call routine(例如:sys_nice)是C code,当assembly code 调用C function,并且是以stack 方式传参数(parameter)时,在C function 的prototype(原型)前面就要加上"asmlinkage "。

加上"asmlinkage" 后,C function 就会由stack 取参数,而不是从register 取参数。

对于未从汇编代码调用的C函数,我们可以安全地假定默认调用约定为cdecl(或快速调用stacall ,这无关紧要,因为gcc会做好调用方和被调用方参数传递的工作。默认调用约定可以是在编译时指定)。但是,对于从汇编代码调用的C函数,我们应该显式声明该函数的调用约定,因为在汇编端传递参数的代码已固定。例如,如果将patch_espfix_desc声明为asmlinkage,则gcc将编译该函数以从堆栈中检索参数,这与将参数放入寄存器的汇编端不一致。

为什么所有系统调用函数都使用堆栈来传递参数?

原因是在处理来自用户空间的系统调用请求时,内核无论如何都需要将所有寄存器保存到堆栈中(以便在返回用户空间之前恢复环境),所以之后参数在堆栈上可用,不需要再做其他的工作。

另一方面,如果要将fastcall用于调用约定,则需要完成更多工作。我们首先需要知道,当用户程序发出系统调用时,在x86-linux中,%eax保存系统调用号,%ebx,%ecx,%edx,%esi,%edi,%ebp用于将6个参数传递给系统调用(在“ int 80h”或“ sysenter”之前)。但是,fastcall的调用约定是在%eax中传递第一个参数,在%edx中传递第二个参数,在%ecx中传递第三个,其他参数从右到左推入堆栈。这样,为了在内核中实施这种快速调用约定,除了将所有寄存器保存在堆栈上之外,还需要做一些其他的工作。

80x86 的 assembly 有2种传递参数的方法:

  1. register method
  2. stack method

Register method 大多使用通用寄存器组(general-purpose)传递参数,这种方法的好处是简单快速。另外一种传递参数的做法是使用stack(堆栈),assembly code 的模式如下:

  1. push number1
  2. push number2
  3. push number3
  4. call sum

在 ‘sum’ procedure 里取值的方法,最简单的做法是:

  1. pop ax
  2. pop bx
  3. pop cx

其它有关asmlinkage

  1. asmlinkage是一个定义
  2. "asmlinkage"被定义在/usr/include/linux/linkage.h
  3. 如果您看了linkage.h,会发现"attribute"这个语法,这是gcc用来定义function attribute(功能属性)的语法。
发布了42 篇原创文章 · 获赞 18 · 访问量 7540

猜你喜欢

转载自blog.csdn.net/weixin_44395686/article/details/105038091