asmlinkage

在大型C语言项目工程或者linux内核中我们都会经常见到两个FASTCALL和armlinkage


两个标识符(修饰符),那么它们各有什么不同呢?今天就给大家共同分享一下自己的心得.


大家都知道在标准C系中函数的形参在实际传入参数的时候会涉及到参数存放的问题,那么这些参数存放在哪里呢? 有一定理论基础的朋友一定会肯定地回答:这些函数参数和函数内部局部变量一起被分配到了函数的局部堆栈中,真的是这样吗?其实还有例外的情况:


首 先作为linux操作系统,它不一定就只运行在X86平台下面,还有其他平台例如ARM,PPC,达芬奇等等,所以在不同的处理器结构上不能保证都是通过 局部栈传递参数的,可能此时就有朋友就会问:不放在栈中能放在哪里呢?熟悉ARM的朋友一定知道ARM对函数调用过程中的传参定义了一套规则,叫 ATPCS(内地叫AAPCS),规则中明确指出ARM中R0-R4都是作为通用寄存器使用,在函数调用时处理器从R0-R4中获取参数,在函数返回时再 将需要返回的参数一次存到R0-R4中,也就是说可以将函数参数直接存放在寄存器中,所以为了严格区别函数参数的存放位置,引入了两个标记,即asmlinkage和FASTCALL,前者表示将函数参数存放在局部栈中,后者则是通知编译器将函数参数用寄存器保存起来


我们在搜索一些额外的线索,ARM中R0-R4用于存放传入参数,隐约告诉我们,作为高水平嵌入式系统开发者,或者高水平C语言程序员,函数的参数不应 该大于5个,那么有人就会反过来问:超过5个的那些参数又何去何从?我的回答是:传入参数如果超过5个,多余的参数还是被存放到局部栈中,此时有人可能又 会问:将函数参数传入局部栈有什么不好?我的回答是:表面上没什么不好,但是如果你是一名具有linux内核修养的程序员,你就会隐约记得linux中, 不管是系统调用,还是系统陷阱都会引起用户空间陷入内核空间,我们知道,系统空间的权限级是0,用户空间的权限级为3,系统调用从权限级为3的用户空间陷 到权限级为0的内核空间,必然引起堆栈切换,linux系统将从全局任务状态栈TSS中找到一个合适的内核栈信息保存覆盖当前SP,SS两个寄存器的内 容,以完成堆栈切换,此时处于内核空间所看到的栈已不是用户空间那个栈,所以在调用的时候压入用户栈的数据就在陷入内核的那个瞬间,被滞留在用户空间栈, 内核根本不知道它的存在了,所以作为安全考虑或者作为高水平程序员的切身修养出发,都不应该向系统调用级函数传入过多的参数。
from:http://www.groad.net/bbs/read.php?tid-1108.html
有网友来信问到,kernel 里的 system call 实作函数中(C 函数),为什麼每一个函数原型宣告的前面都有一个 "asmlinkage" 的字串?例如: 


asmlinkage long sys_nice(int increment) 


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


当 system call handler 要 呼叫相对应的 system call routine 时,便将一般用途暂存器的值 push 到 stack 里,因 此 system call routine 就要由 stack 来读取 system call handler 传递的参数。这就 是 asmlinkage 标签的用意。 


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 取参数(可能发生在程式码最佳化後)。 


更进一步的说明... 


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


1. register method 
2. stack method 


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


push number1 
push number2 
push number3 
call sum 


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


pop ax  
pop ax 
pop bx 
pop cx 


Stack Top 是放 IP,我们传给 sum procedure 的参数由 stack 的後一个 entry 开始读取。 


其它有关 asmlinkage 


1. asmlinkage 是一个定义 
2. "asmlinkage" 被定义在 /usr/include/linux/linkage.h 
3. 如果您看了 linkage.h,会发现 "__attribute__" 这个语法,这是 gcc 用来定义 function attribute 的语法。
 
 
二:它是GCC对C程序的一种扩展, #define asmlinkage __attribute__((regparm(0)))
表示用0个寄存器传递函数参数,这样,所有的函数参数强迫从栈中提取。
这个asmlinkage大都用在系统调用中,系统调用需要在entry.s文件中用汇编语言调用,所以必须要保证它符合C语言的参数传递规则,才能用汇编语言正确调用它。
这也是为何使用asmlinkage的原因吧!这是我的理解。
仔细看一下有asmlinkage的地方通常是系统调用的函数,因为在系统调用中,寄存器从用户空间传过来后SAVE_ALL压入堆栈,接着调用相应的系统调用函数,这样系统调用函数一定要保证是通过堆栈传递参数的


转贴一段:
The asmlinkage tag is one other thing that we should observe about this simple function. 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. Recall our earlier assertion that system_call consumes its first argument, the system call number, and allows up to four more arguments that are passed along to the real system call. system_call achieves this feat simply by leaving its other arguments (which were passed to it in registers) on the stack. All system calls are marked with the asmlinkage tag, so they all look to the stack for arguments. Of course, in sys_ni_syscall's case, this doesn't make any difference, because sys_ni_syscall doesn't take any arguments, but it's an issue for most other system calls. And, because you'll be seeing asmlinkage in front of many other functions, I thought you should know what it was about.


具体可以研究一下arch/i386/kernel/entry.S


是不是为了保护寄存器内容不受更改 才用堆栈传递参数 以便以后能RESTORE--ALL?


系统调用把参数存放在寄存器中然后进入内核空间,其实就和正常的函数调用一样,在内核中通过SAVE_ALL构造一个函数调用的栈帧环境,然后调用相应的系统调用函数而已


这里使用的堆栈就是原来SAVE--ALL的寄存器的内容吧??
对的


ENTRY(system_call)
pushl %eax # save orig_eax
SAVE_ALL
GET_THREAD_INFO(%ebp)
# system call tracing in operation
/* Note, _TIF_SECCOMP is bit number 8, and so it needs testw and not testb */
testw $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SECCOMP),TI_flags(%ebp)
jnz syscall_trace_entry
cmpl $(nr_syscalls), %eax
jae syscall_badsys
syscall_call:
call *sys_call_table(,%eax,4)
movl %eax,EAX(%esp) # store the return value


FROM:http://blog.chinaunix.net/u3/104479/showart_2089369.html
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值