×
单片机 > 单片机程序设计 > 详情

ARMv7汇编代码分析

发布时间:2020-12-31 发布时间:
|

为了分析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 1200"

.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 1200

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如何实现打印多出来的几个寄存器的,目前暂不清楚。欢迎补充。

关键字:ARMv7  汇编  寄存器

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
LCD驱动-JZ2440