arm平台函数传递参数,反汇编实例分析
测试前,需要了解下sysv的传参方式:
1、输入参数通过r0-r3传递,多余的放入堆栈中;返回值放入r0,不够的话放入{r0,r1}或者{r0,r1,r2,r3},比如:
int foo(int a, int b, int c, int d), 输入:r0 = a, r1 = b, r2 = c, r3 = d,返回:r0 = 类型为int的retvalue
int *foo(char a, double b, int c, char d), 输入:r0 = a, r1用于对齐(double 要求8字节对齐), b = {r2, r3},c放在堆栈的sp[0]位置,d放在堆栈的sp[4]位置,这里的sp是指进入函数时的sp;返回:r0 = 类型为int *的retvalue
2、注意如果返回值是结构体,情况有些特殊:
struct client foo(int a, char b, float c), 输入:r0 = 一个strcut client *变量,由调用者给出, r1 = a, r2 = b, r3 = c;返回:strcut client *变量,和调用者给的一样
为了测试arm平台函数参数如何对齐,多余参数如何传递,以及如何返回一个结构体类型的变量,编写如下代码:
#include <stdio.h>
#include <stdlib.h>typedef struct _Foo{int a;char b;double c;float d;
}Foo;Foo test(int a, char b, double c, float d)
{Foo *f = (Foo *)malloc(sizeof(Foo));f->a = a;f->b = b;f->c = c;f->d = d;return *f;
}int main(void)
{Foo retvalue; retvalue = test(1,2,3,4);return retvalue.a;
}
malloc会有内存溢出,这样写是为了反汇编更简单些,编译时不开优化,使用-marm参数指定使用arm指令集,然后反汇编得到:
00000000 <test>:0: e92d4810 push {r4, fp, lr}4: e28db008 add fp, sp, #8 //fp = sp + 88: e24dd01c sub sp, sp, #28 //sp = sp -28c: e50b0018 str r0, [fp, #-24] ; 0xffffffe8 //fp[-24] = r0 = Foo * temp10: e50b101c str r1, [fp, #-28] ; 0xffffffe4 //fp[-28] = r1 = int a14: e1a03002 mov r3, r2 //fp[-29] = r2 = char b, r3 not used18: e54b301d strb r3, [fp, #-29] ; 0xffffffe31c: e3a00018 mov r0, #2420: ebfffffe bl 0 <malloc> //malloc(24)24: e1a03000 mov r3, r0 //r3 = f28: e50b3010 str r3, [fp, #-16] //fp[-16] = f2c: e51b3010 ldr r3, [fp, #-16] //r3 = f30: e51b201c ldr r2, [fp, #-28] ; 0xffffffe4 //r2 = fp[-28] = int a34: e5832000 str r2, [r3] //f->a = r2 = a38: e51b3010 ldr r3, [fp, #-16]3c: e55b201d ldrb r2, [fp, #-29] ; 0xffffffe3 //r2 = fp[-29] = char b40: e5c32004 strb r2, [r3, #4] //f->b = r2 = b44: e51b2010 ldr r2, [fp, #-16] //r2 = f48: e99b0018 ldmib fp, {r3, r4} //double c = {r3, r4}4c: e5823008 str r3, [r2, #8] //f[8] = r350: e582400c str r4, [r2, #12] //f[12] = r4, f->c = c54: e51b3010 ldr r3, [fp, #-16] //r3 = f58: e59b200c ldr r2, [fp, #12] //r2 = float d5c: e5832010 str r2, [r3, #16] //f->d = float d60: e51b2018 ldr r2, [fp, #-24] ; 0xffffffe8 //r2 = r0 = Foo *temp64: e51b3010 ldr r3, [fp, #-16] //r3 = f68: e1a0c002 mov ip, r2 //ip = r0 = Foo *temp6c: e1a0e003 mov lr, r3 //lr = f70: e8be000f ldm lr!, {r0, r1, r2, r3} //拷贝f指向的前16个字节到Foo *temp指向的74: e8ac000f stmia ip!, {r0, r1, r2, r3}78: e89e0003 ldm lr, {r0, r1} //拷贝后面8个字节,加起来=24=sizeof(Foo)7c: e88c0003 stm ip, {r0, r1}80: e51b0018 ldr r0, [fp, #-24] ; 0xffffffe8 //返回r0 = Foo *temp84: e24bd008 sub sp, fp, #888: e8bd8810 pop {r4, fp, pc}0000008c <main>:8c: e92d4810 push {r4, fp, lr}90: e28db008 add fp, sp, #8 //fp = sp + 894: e24dd02c sub sp, sp, #44 ; 0x2c //sp = sp - 4498: e24b0024 sub r0, fp, #36 ; 0x24 //r0 = fp - 36 = &retvalue = Foo *temp9c: e59f3028 ldr r3, [pc, #40] ; cc <main+0x40>a0: e58d3008 str r3, [sp, #8] //float d, 放入堆栈a4: e3a03000 mov r3, #0a8: e59f4020 ldr r4, [pc, #32] ; d0 <main+0x44>ac: e88d0018 stm sp, {r3, r4} //double c, 放入堆栈b0: e3a02002 mov r2, #2 //b = 2b4: e3a01001 mov r1, #1 //a = 1b8: ebfffffe bl 0 <test>bc: e51b3024 ldr r3, [fp, #-36] ; 0xffffffdc //r3 = retvalue.ac0: e1a00003 mov r0, r3 //r0 = r3 = retvalue.a,main返回值c4: e24bd008 sub sp, fp, #8c8: e8bd8810 pop {r4, fp, pc}cc: 40800000 .word 0x40800000d0: 40080000 .word 0x40080000
重点分析test函数,它的参数为:
r0: struct Foo *temp,通过main函数传递过来的,用于存放struct Foo结构体
r1: int a
r2: char b, 即使是char,也独立占一个寄存器,不与其他参数共用寄存器
r3: for alignment,下一个参数是double,要求对齐为8
c: sp[0-7],多余的参数放在堆栈上,这里是double c
d: sp[8-11],float d
test函数的堆栈结构为:
下面,逐行分析test的汇编代码,来验证上述内容。
1、进入test函数时,sp指向double c的低四字节,然后push {r4, fp, lr}之后,sp指向保存r4的位置:
0: e92d4810 push {r4, fp, lr}
2、fp=sp+8, sp=sp-28:
4: e28db008 add fp, sp, #8 //fp = sp + 88: e24dd01c sub sp, sp, #28 //sp = sp -28
3、把struct Foo *temp,也就是r0,存到fp-24的位置上:
c: e50b0018 str r0, [fp, #-24] ; 0xffffffe8 //fp[-24] = r0 = Foo *temp
4、把int a,也就是r1,存到fp-28的位置上:
10: e50b101c str r1, [fp, #-28] ; 0xffffffe4 //fp[-28] = r1 = int a
5、把char b,也就是r2,存到fp-29的位置上,注意只放了一个字节,还剩下三个字节没有使用;注意r3没有有效值,只是为了对齐的,所以可以直接覆盖:
14: e1a03002 mov r3, r2 //fp[-29] = r2 = char b, r3 not used18: e54b301d strb r3, [fp, #-29] ; 0xffffffe3
6、至此,输入参数已全部保存在堆栈上(double c, float d, 一开始就在堆栈高地址上)
7、调用malloc(24)函数,因为sizeof(Foo)=24,返回值r0赋值给r3,保存在fp-16位置,也就是变量Foo *f:
1c: e3a00018 mov r0, #2420: ebfffffe bl 0 <malloc> //malloc(24)24: e1a03000 mov r3, r0 //r3 = f28: e50b3010 str r3, [fp, #-16] //fp[-16] = f2c: e51b3010 ldr r3, [fp, #-16] //r3 = f
8、从fp-28取出int a,保存到f+0位置上,也就是f->a=a:
30: e51b201c ldr r2, [fp, #-28] ; 0xffffffe4 //r2 = fp[-28] = int a34: e5832000 str r2, [r3] //f->a = r2 = a
9、从fp-29取出char b,保存到f+4位置上,也就是f->b=b:
38: e51b3010 ldr r3, [fp, #-16]3c: e55b201d ldrb r2, [fp, #-29] ; 0xffffffe3 //r2 = fp[-29] = char b40: e5c32004 strb r2, [r3, #4] //f->b = r2 = b
10、ldmib fp, {r3, r4},地址先增加4,然后取值保存到r3,地址再增加4,取值保存到r4,地址值不回写,也就是取出double c,放入r3,r4,然后保存到f+8地址和f+12地址上,也就是f->c=c:
44: e51b2010 ldr r2, [fp, #-16] //r2 = f48: e99b0018 ldmib fp, {r3, r4} //double c = {r3, r4}4c: e5823008 str r3, [r2, #8] //f[8] = r350: e582400c str r4, [r2, #12] //f[12] = r4, f->c = c
11、取fp+12位置的float d,保存到f+16地址,也就是f->d=d:
54: e51b3010 ldr r3, [fp, #-16] //r3 = f58: e59b200c ldr r2, [fp, #12] //r2 = float d5c: e5832010 str r2, [r3, #16] //f->d = float d
12、ip = Foo * temp = [fp - 24] = r0@entry,lr = f = [fp - 16],从f指向的地址取16字节,保存到ip指向的地址,然后再取8字节,保存到ip指向的地址,也就是按值拷贝f指向的结构体到Foo *temp = r0指向的结构体:
60: e51b2018 ldr r2, [fp, #-24] ; 0xffffffe8 //r2 = r0 = Foo *temp64: e51b3010 ldr r3, [fp, #-16] //r3 = f68: e1a0c002 mov ip, r2 //ip = r0 = Foo *temp6c: e1a0e003 mov lr, r3 //lr = f70: e8be000f ldm lr!, {r0, r1, r2, r3} //拷贝f指向的前16个字节到r0 = Foo *temp指向的74: e8ac000f stmia ip!, {r0, r1, r2, r3}78: e89e0003 ldm lr, {r0, r1} //拷贝后面8个字节,加起来=24=sizeof(Foo)7c: e88c0003 stm ip, {r0, r1}
13、设置返回值r0,为Foo *temp,返回:
80: e51b0018 ldr r0, [fp, #-24] ; 0xffffffe8 //返回r0 = Foo *temp84: e24bd008 sub sp, fp, #888: e8bd8810 pop {r4, fp, pc}
然后分析main函数
main的堆栈为:
这里的SP=SP-44也就是test函数堆栈的SP@entry
main的汇编代码为:
1、进入函数后,push{r4, fp, lr},sp指向存放r4的位置:
8c: e92d4810 push {r4, fp, lr}
2、fp=sp+8, sp=sp-44:
90: e28db008 add fp, sp, #8 //fp = sp + 894: e24dd02c sub sp, sp, #44 ; 0x2c //sp = sp - 44
3、为临时变量Foo retvalue申请内存,&retvalue为r0 = fp-36,也就是传递给test函数的那个Foo *temp:
98: e24b0024 sub r0, fp, #36 ; 0x24 //r0 = fp - 36 = &retvalue = Foo *temp
4、取fload d的输入值,放入sp+8位置:
9c: e59f3028 ldr r3, [pc, #40] ; cc <main+0x40>a0: e58d3008 str r3, [sp, #8] //float d, 放入堆栈
5、取double c的输入值,放入sp+0位置:
a4: e3a03000 mov r3, #0a8: e59f4020 ldr r4, [pc, #32] ; d0 <main+0x44>ac: e88d0018 stm sp, {r3, r4} //double c, 放入堆栈
6、设置r1 = int a = 1, r2 = char b = 2,注意到r0 = Foo * temp = &retvalue,r3用于对齐,double c 和float d已放入堆栈sp0-sp12的位置上,调用test函数:
b0: e3a02002 mov r2, #2 //b = 2b4: e3a01001 mov r1, #1 //a = 1b8: ebfffffe bl 0 <test>
7、取retvalue.a的值到r3,然后赋值给r0,为函数main的返回值:
bc: e51b3024 ldr r3, [fp, #-36] ; 0xffffffdc //r3 = retvalue.ac0: e1a00003 mov r0, r3 //r0 = r3 = retvalue.a,main返回值
8、main函数返回:
c4: e24bd008 sub sp, fp, #8c8: e8bd8810 pop {r4, fp, pc}cc: 40800000 .word 0x40800000d0: 40080000 .word 0x40080000
arm平台函数传递参数,反汇编实例分析相关推荐
- python函数定义与参数_Python函数的定义方式与函数参数问题实例分析
本文实例讲述了Python函数的定义方式与函数参数问题.分享给大家供大家参考,具体如下: 涉及内容: 函数的定义方式 函数的文字描述 空操作语句 位置参数 默认参数 关键参数 可变长度参数 函数的定义 ...
- PHP - 回调函数概念与用法实例分析 - 学习/实践
1.应用场景 主要用于理解回调函数的概念, 对比JavaScript中的回调函数, 更加深刻理解回调函数的本质, 以及如何高效使用~~~ 2.学习/操作 1. 文档阅读 https://www.jb5 ...
- pthread_create函数详解(向线程函数传递参数)
一.pthread_create函数: 1.简介:pthread_create是UNIX环境创建线程的函数 2.头文件:#include <pthread.h> 3.函数声明: int p ...
- python 函数传递参数的多种方法
python中函数根据是否有返回值可以分为四种:无参数无返回值,无参数有返回值,有参数无返回值,有参数有返回值. Python中函数传递参数的形式主要有以下五种,分别为位置传递,关键字传递,默认值传递 ...
- python参数传递方法_深入理解python中函数传递参数是值传递还是引用传递
python 的 深入理解python中函数传递参数是值传递还是引用传递 目前网络上大部分博客的结论都是这样的: Python不允许程序员选择采用传值还是传 引用.Python参数传递采用的肯定是&q ...
- js中函数传递参数,究竟是值传递还是引用传递?
记住真理: js函数传递参数,不管是简单数据类型,还是引用数据类型,都是值传递!! 下面是js红包书里面的例子: function setName(obj) { obj.name = "Ni ...
- linux中probe函数传递参数的寻找(下)
点击打开链接 linux中probe函数传递参数的寻找(下) 通过追寻driver的脚步,我们有了努力的方向:只有找到spi_bus_type的填充device即可,下面该从device去打通,当两个 ...
- C++11向线程函数传递参数
template< class Function, class... Args > explicit thread( Function&& f, Args&& ...
- pthread_create函数的详细讲解(包括向线程函数传递参数详解)
pthread_create是UNIX环境创建线程函数 头文件 #include<pthread.h> 函数声明 int pthread_create(pthread_t*restrict ...
最新文章
- map/reduce的概念
- VC++套接字、数据库、文件读写综合应用-客户端读取文件套接字接收服务端写入数据库
- Android Material Design TabLayout属性app:tabMode和app: tabGravity
- System.getProperty(user.dir)
- [JavaScript] - replaceAll,将字符串中的字母或数字等全部替换掉的方式
- MAC修改.bashrc/.bash_profile无效,默认的用户配置文件是.zshrc,
- 简单的事情搞复杂:挂个版本到网站,拖了几个月还没做
- Oracle的exp导出、imp导入数据命令
- 光滑曲线_光滑流形初步(2)——切向量与微分
- XML 大于号 小于号 处理
- asp.net/c# 注册页实现激活邮箱验证
- CE修改器入门:寻找指针基址
- (私人收藏)2019WER积木教育机器人赛(普及赛)解决方案-(全套)获取能源核心...
- 转:数据可视化之美:经典案例与实践解析
- Contiki 配置参数“技巧”说明
- 麻省理工科技评论:AI预言的七宗罪(上)
- mybatis从入门到精通(刘增辉著)-读书笔记第二章
- 关于任务规划和提高执行力
- 每台计算机用户都有一个独有的,因特网上的每台正式计算机用户都有一个独有的()。A.Mac地址B.网络号C.主机号D.IP地址...
- 海思Hi3559AV100移植Qt5.9.1