arm上的参数列表传递的分析(以android为例)
1. Linux中可变列表实现的源码分析
查看Linux0.11的内核源代码,对va_list, va_start, va_arg 的实现如下:
- va_list的实现没有差别,char
typedef char va_list; va_start的实现
#define va_start(AP, LASTARG) \
(__builtin_saveregs (), \
AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))
注:__builtin_saveregs()是gcc内置的函数,用来保存堆栈,其实好像也没什么必要这么做
然后将AP指向可变参数前一个参数的的结尾。这里的LASTARG就是前面的param1va_arg的实现
#define va_arg(AP, TYPE) \
(AP += __va_rounded_size (TYPE), \
*((TYPE *) (AP - __va_rounded_size (TYPE))))
注:这里的逻辑有点奇怪,先将指针移动到该参数的结尾,再返回指针减去该参数长度的位置内容。
其实这是对C语言的 , 操作符的利用,用来实现类型转换。,后面的值才是返回值,所以移动指针的操作必须在前。一个很好的小技巧辅助函数
#define __va_rounded_size(TYPE) \
(((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int))
注:前面的va_start和va_arg 都没有使用 sizeof,而是用的 va_rounded_size,这个是什么意思呢?其实和结构的成员对齐相似,每个参数最少占用4个字节。
例如一个char型的参数,也要占用4个字节,4个连续的char型参数,就要占用16个字节。
所以这里使用用 [x+ (n-1)] / n * n 的对齐公式来实现最少对齐。
如果要取一个char型参数,要用va_arg(args, int), 而不能用va_arg(args, char),否则会产生编译警告,运行异常。
2. ARM上可变参数列表实现分析——二进制
对于pc上的可变参数列表,比较容易理解:参数全部存储在栈上。所以:va_list p定义一个指针,va_start(p, arg_a)获取参数列表地址,该地址就是va_start第二个参数对应数据之后的地址,,在栈上表现为:arg_a+sizeof(arg_a). 此后根据参数类型,使用va_arg依次从指定的参数列表地址取数据。
但时对于arm上,一个会使用寄存器传递参数的平台,又回怎样处理的呢?
通过写一个简单的示例程序:
int test(int a, ...){
va_list p;
va_start(p, a);
printf("%d", va_arg(p, int));
va_end(p);
}
实际编译后如下:
.text:00000BE0 ; test(int, ...)
.text:00000BE0 EXPORT _Z4testiz
.text:00000BE0 _Z4testiz ; CODE XREF: main+8p
.text:00000BE0
.text:00000BE0 var_1C = -0x1C
.text:00000BE0 varg_r0 = -0x10
.text:00000BE0 varg_r1 = -0xC
.text:00000BE0 varg_r2 = -
.text:00000BE0 varg_r3 = -
.text:00000BE0
.text:00000BE0 PUSH {R0-R3}
.text:00000BE2 PUSH {R0-R2,LR}
.text:00000BE4 LDR R0, =(unk_2168 - 0xBEE)
.text:00000BE6 ADD R3, SP, #0x20+varg_r2
.text:00000BE8 LDR R1, [SP,#0x20+varg_r1]
.text:00000BEA ADD R0, PC ; format
.text:00000BEC STR R3, [SP,#0x20+var_1C]
.text:00000BEE BLX printf
.text:00000BF2 ADD SP, SP, #0xC
.text:00000BF4 POP {R3}
.text:00000BF6 ADD SP, SP, #0x10
.text:00000BF8 BX R3
.text:00000BF8 ; End of function test(int,...)
可以看到粗体部分,进行了2次push。而有所了解得程序员,我们知道一般只会push一次,即后面的那一个——用来保存寄存器状态和返回地址。
那么第一次是干什么呢?显然这和可变参数有关!
arm上使用寄存器传递参数时一般只是用r0~r3,因此,系统直接将其压入栈中!然后和一般函数一样的方式去保护可能会被覆盖的寄存器的状态。
将r0~r3压入栈中之后,如果还有更多的参数,则之前必须(应当)已经压入栈中,那么此时的状态就和pc上的类似了。如下所示
(高地址)
...
arg_5
arg_4
arg_3<---r3
arg_2<---r2
arg_1<---r1
arg_0<---r0
<需要保护的寄存器值>
...
(低地址)
3. 可变参数列表类型的转换
在Android上, va_list
可变参数列表并没有直接定义,是gcc的一个built in类型.但是实际分析中发现,其实va_list
就是一个char*
,因此我们只需要将其想办法进行类型转化即可.
但是实际过程中,无法通过强制类型转化完成.因此,使用内联汇编完成类型转换.如下
va_list args;
char* argBuf; __asm__(
"mov %[argBuf], %[args]"
: [argBuf] "=r" (argBuf)
: [args] "r" (args)
:
);
arm上的参数列表传递的分析(以android为例)的更多相关文章
- Chapter5_初始化与清理_数组初始化与可变参数列表
一.数组初始化 数组是相同类型的,用一个标识符名称封装到一起的一个对象序列或基本类型数据序列.编译器是不允许指定数组的长度的,当使用语句int[] a时,拥有的只是一个符号名,即一个数组的引用,并不拥 ...
- linux-3.2.36内核启动1-启动参数(arm平台 启动参数的获取和处理,分析setup_arch)【转】
转自:http://blog.csdn.net/tommy_wxie/article/details/17093297 最近公司要求调试一个内核,启动时有问题,所以就花了一点时间看看内核启动. 看的过 ...
- 解决nginx上传模块nginx_upload_module传递GET参数
解决nginx上传模块nginx_upload_module传递GET参数的方法总结 最近用户反映我们的系统只能上传50M大小的文件, 希望能够支持上传更大的文件. 很显然PHP无法轻易实现大文件上传 ...
- SpringMVC入门(二)—— 参数的传递、Controller方法返回值、json数据交互、异常处理、图片上传、拦截器
一.参数的传递 1.简单的参数传递 /* @RequestParam用法:入参名字与方法名参数名不一致时使用{ * value:传入的参数名,required:是否必填,defaultValue:默认 ...
- C++中三种传递参数方法的效率分析
众所周知,在C++中有三种参数传递的方式: 按值传递(pass by value) #include <iostream> using namespace std; void swap(i ...
- ARM linux的启动部分源代码简略分析【转】
转自:http://www.cnblogs.com/armlinux/archive/2011/11/07/2396784.html ARM linux的启动部分源代码简略分析 以友善之臂的mini2 ...
- ARM上的linux如何实现无线网卡的冷插拔和热插拔
ARM上的linux如何实现无线网卡的冷插拔和热插拔 fulinux 凌云实验室 1. 冷插拔 如果在系统上电之前就将RT2070/RT3070芯片的无线网卡(以下简称wlan)插上,即冷插拔.我们通 ...
- (63)Wangdao.com第十天_预处理、预解析_函数 上下文对象、参数列表对象
预解析.预处理 1. 在全局代码执行之前,js 引擎 就会创建一个栈来存储管理所有的 执行上下文对象 2. 在 全局执行上下文 window 确定以后,进行压栈 3. 在 函数执行上下文对象 确定以后 ...
- Linux移植之tag参数列表解析过程分析
在Linux移植之内核启动过程start_kernel函数简析中已经指出了start_kernel函数的调用层次,这篇主要是对具体的tag参数列表进行解析. 1.内存参数ATAG_MEM参数解析 2. ...
随机推荐
- C#创建datatable
Asp.net DataTable添加列和行的方法 方法一: DataTable tblDatas = new DataTable("Datas"); DataColumn dc ...
- ObjectID和Handle
一个dwg对应一个arx database,也就是一套9个符号表和一个有名词典. 一个CAD session中是可以加载多个database的.加载后每个对象都有一个handle和一个objectid ...
- POJ1229 域名匹配
给你两个域名,域名中包含一些通配符. * :匹配一个或任意多个部分 ?:匹配一个或三个部分 !:匹配三个以上部分. 求这两个域名是否能够表示同一个域名? 域名的长度不超过255. 分析:设给出的域名为 ...
- 黄聪:说说JSON和JSONP,也许你会豁然开朗(转)
前言 由于Sencha Touch 2这种开发模式的特性,基本决定了它原生的数据交互行为几乎只能通过AJAX来实现. 当然了,通过调用强大的PhoneGap插件然后打包,你可以实现100%的Socke ...
- python的json模块
Python JSON 本章节我们将为大家介绍如何使用 Python 语言来编码和解码 JSON 对象. 环境配置 在使用 Python 编码或解码 JSON 数据前,我们需要先安装 JSON 模块. ...
- csv文件批量导入数据到sqlite。
csv文件批量导入数据到sqlite. 代码: f = web.input(bs_switch = {}) # bs_switch 为from表单file字段的namedata =[i.split( ...
- [HTML] CSS3 圆角
使用 CSS3 border-radius 属性,你可以给任何元素制作 "圆角". CSS3 border-radius 属性 使用 CSS3 border-radius 属性,你 ...
- CyclicBarrier类合唱演绎
package a.jery; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutorServi ...
- SecureCRT 连接后一些会话选项配置修改
- [EventBus源码解析] 订阅者处理消息的四种ThreadMode
前言 在前面,我们探讨了如何在自己的代码中引入EventBus,进行基本的事件分发/监听:对注册观察者与事件发送的过程进行了浅析.从之前的学习中,我们了解到,EventBus一共有4种onEvent方 ...