为了分析ARMv7架构寄存器的使用,利用C程序生成ARMv7汇编,并分析之。
1、C源程序代码如下(为了简化,函数功能很简单):
# cat callfunc.c
#include #include #include int main() { int input=10; int tmp,result; tmp = func1(input); result = func2(tmp); printf("result = %d n", result); return 0; } int func1(int a) { a++; return a; } int func2(int b) { b=b*b; return b; } 2、交叉编译生成ARM汇编代码: # arm-none-linux-gnueabi-gcc -march=armv7-a callfunc.c -S -o callfunc.asm 生成ARMv7的汇编代码如下: .arch armv7-a .fpu softvfp .eabi_attribute 20, 1 .eabi_attribute 21, 1 .eabi_attribute 23, 3 .eabi_attribute 24, 1 .eabi_attribute 25, 1 .eabi_attribute 26, 2 .eabi_attribute 30, 6 .eabi_attribute 34, 1 .eabi_attribute 18, 4 .file "callfunc.c" .section .rodata .align 2 ;2^2,即4字节对齐;以"."开头的是伪指令,具有编译器相关,平台无关性; .LC0: .ascii "result = %d 12 00" .text .align 2 .global main ;全局函数声明,相当于C语言中的extern .type main, %function main: .fnstart ;函数开始标志 @ args = 0, pretend = 0, frame = 16 ;@标志注释,由编译器添加 @ frame_needed = 1, uses_anonymous_args = 0 stmfd sp!, {fp, lr} ;fp=r11=0,lr=0x40291664 ;分别存储fp,lr到sp,sp-4;sp0=sp-4;fp(R11)桢指针 ; ;关于fp:通常在C程序编译过程中,所有函数的局部变量被分配在一个连续的存储区中, ;一般这个存储区是在堆栈中,这个连续的存储区称为这个函数的存储“帧”,它是通过一个指针访问的。 ;寄存器 fp (桢指针)应当是零或者是指向栈回溯结构的列表中的最后一个结构, ;提供了一种追溯程序的方式,来反向跟踪调用的函数。 ;即:其作用是用来回溯,从代码中也可以看出来,fp只是起标签作用,理论上不使用fp也不会影响程序执行 .save {fp, lr} ;关于.save:其作用与以下.setfp、.pad等的伪指令作用类似,对相应汇编指令产生注释。 ;具体注释内容见网页(http://sourceware.org/binutils/docs/as/ARM-Directives.html) .setfp fp, sp, #4 add fp, sp, #4 ;sp=0xbe88f3e0,fp=0xbe88f3e4 .pad #16 sub sp, sp, #16 ;sp=0xbe88f3d0 mov r3, #10 ;r3=10 str r3, [fp, #-8] ;r3->fp-8即sp-4 ldr r0, [fp, #-8] ;r3->r0 bl func1 ;jump to func1 str r0, [fp, #-12] ;r0->fp-12 ldr r0, [fp, #-12] bl func2 str r0, [fp, #-16] movw r3, #:lower16:.LC0 movt r3, #:upper16:.LC0 ;movw和movt的作用为:r3 = (movt #:upper16:.LC0 << 16) | (movw #:lower16:.LC0)). ;以上两句的作用就是输出.LC0段的.ascii那一行的内容,即打印那个result = %d 12 00 mov r0, r3 ldr r1, [fp, #-16] bl printf ;打印r0、r1中内容 mov r3, #0 mov r0, r3 sub sp, fp, #4 ldmfd sp!, {fp, pc} .fnend .size main, .-main .align 2 .global func1 .type func1, %function func1: .fnstart @ args = 0, pretend = 0, frame = 8 @ frame_needed = 1, uses_anonymous_args = 0 @ link register save eliminated. str fp, [sp, #-4]! ;r11(fp)入栈;sp=0xbe88f3d0-4=0xbe88f3cc,r11=0xbe88f3e4;fp(即r11)存储到sp-4(0x)的地址中 .save {fp} .setfp fp, sp, #0 add fp, sp, #0 ;fp=0xbe88f3cc .pad #12 sub sp, sp, #12 ;sp=0xbe88f3c0 str r0, [fp, #-8] ;r0存储到fp-8指向的地址处 ldr r3, [fp, #-8] add r3, r3, #1 str r3, [fp, #-8] ldr r3, [fp, #-8] mov r0, r3 add sp, fp, #0 ldmfd sp!, {fp} ;r11(fp)出栈 bx lr .fnend .size func1, .-func1 .align 2 .global func2 .type func2, %function func2: .fnstart @ args = 0, pretend = 0, frame = 8 @ frame_needed = 1, uses_anonymous_args = 0 @ link register save eliminated. str fp, [sp, #-4]! .save {fp} .setfp fp, sp, #0 add fp, sp, #0 .pad #12 sub sp, sp, #12 str r0, [fp, #-8] ldr r3, [fp, #-8] ;r0->r3 ldr r2, [fp, #-8] ;r0->r2 mul r3, r2, r3 ;r2*r3->r3 str r3, [fp, #-8] ldr r3, [fp, #-8] mov r0, r3 ;r3->r0 add sp, fp, #0 ldmfd sp!, {fp};sp->fp ;pop fp bx lr .fnend .size func2, .-func2 .ident "GCC: (Sourcery CodeBench Lite 2011.09-70) 4.6.1" .section .note.GNU-stack,"",%progbits 分析内容见代码注释 注意: 1、编译器默认用来传输参数的寄存器是r0~r3,参数超过四个就要用到栈。 2、bl printf:该命令是打印命令,默认打印r0开始的寄存器内容,测试过参数超过4个时候,打印时r0存储打印格式,r1-r3存储要打印的数,剩下的参数需要其他寄存器存储,但是printf如何实现打印多出来的几个寄存器的,目前暂不清楚。欢迎补充。
设计资源 培训 开发板 精华推荐
- LTC1433 演示板,低噪声电流模式降压型 DC/DC 转换器
- 用于反激式转换器的 NCP1236 固定频率电流模式控制器的典型应用
- DER-720 - 使用带集成二极管的 HiperPFS-4 的 275 W PFC 前端
- LT1505 的典型应用 - 恒流/恒压高效电池充电器
- 简易遥控红外发射器(待验证)
- 使用符合 EN55022 B 类(24Vin 和 48Vin,单输出)EMC 滤波的 RP40-4824DFR DC/DC 转换器的典型应用
- 用于 CYPD5236-96BZXI USB Type-C 端口控制器的双端口坞站下游端口应用中的 CCG5
- TDA7391LVPD 35W桥式汽车收音机低电压放大器典型应用电路
- 个人时钟天气站SD²电池扩展板
- MMOS伺服电机直驱方向盘模拟器控制板
- 有奖直播|贝能国际推出基于英飞凌技术的毫米波雷达模组,完美解决PIR市场痛点
- 免费申请测评:超小型 Linux 开发套件:Quantum Tiny Linux(带 SoM 和扩展板)
- 用心分享,一起成长!EEWORLD月月有奖优秀主题/回复第28期开始啦~
- “玄铁杯”第三届RISC-V应用创新大赛—国产高性能RISC-V Linux开发板LicheePi 4A报名专场,万元奖金,邀您奔赴开源设计盛宴
- 你吐槽 我送书
- 免费下载|PathWave帮您迎接高速数字设计复杂性的挑战
- 【评论有礼】大话CC2650,从资料到例程、从应用到生态全方位讲解!
- 有奖连线:“泰”想开车 动力篇:800V超充技术,扫清里程焦虑