C代码:
#include <stdio.h>
int show()
{
return 0 ;
}
void say()
{
}
int main( )
{
show();
say();
return 0;
}
汇编:
root@ubuntu:/work/demo/demo# arm-linux-gcc -v
...............
gcc version 3.4.5
root@ubuntu:/work/demo/demo# arm-linux-gcc -S demo.c
root@ubuntu:/work/demo/demo# cat demo.s
.file "demo.c"
.text
.align 2
.global show
.type show, %function
show:
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 1, uses_anonymous_args = 0
mov ip, sp
stmfd sp!, {fp, ip, lr, pc}
sub fp, ip, #4
mov r3, #0
mov r0, r3 //r0~r3
ldmfd sp, {fp, sp, pc}
.size show, .-show
.align 2
.global say
.type say, %function
say:
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 1, uses_anonymous_args = 0
mov ip, sp
stmfd sp!, {fp, ip, lr, pc}
sub fp, ip, #4
ldmfd sp, {fp, sp, pc}
.size say, .-say
.align 2
.global main
.type main, %function
main:
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 1, uses_anonymous_args = 0
mov ip, sp ;ip = sp
stmfd sp!, {fp, ip, lr, pc}
; ! 表示更新基址寄存器。这里表示将寄存器组{fp,ip,lr,pc}中的内容保存到堆栈中,并更新堆栈指针寄存器。
; 《ARM处理器开发详解.刘洪涛》P51 P53 P54讲的Arm处理器指令寻址方式,按类描述了各种指令语法格式的含义,并在后面对个指令进行了相信介绍。
sub fp, ip, #4 ;fp = ip -4
bl show
bl say
mov r3, #0
mov r0, r3
ldmfd sp, {fp, sp, pc}
.size main, .-main
.ident "GCC: (GNU) 3.4.5"
arm s3c2440使用满递减栈,sp指向栈顶,堆栈向内存地址减小的地方生长。
函数一级调用堆栈push/pop图:
STMFD和LDMFD的原理:[4][5]
在数据栈操作中,
ldmfd对应通常寻址方式的ldmia; increase after
stmfd对应通常寻址模式的stmdb;decrease before
指令格式 ldm/stm{<cond>} <addressing_mode> <Rn>{!},<reglist>{^}
stmfd sp!,{r0-r7,pc} 伪代码:
start_addr= Rn - (n_regs*4);
end_addr = Rn - 4
if( ConditionPassed(cond) && w==1 ) //w表示指令执行后,基址寄存器是否更新。就是那个!号。
{
addr = start_addr;
for(i=0 to 15)
{
if(reglist[i]==True)
{ //在列表内
*addr = Ri ; //这里Memory是stack,把Ri的数据传输到Memory中。
addr +=4;
}
}
}
ldm{<cond>} <addressing_mode> <Rn>{!},<reglist>{^}
ldmfd (ldmia)伪代码:每次传送后加4.
ldmfd sp!,{r0-r7,pc}伪代码:
start_addr = Rn
end_addr = Rn +(n_regs*4)-4
if(ConditionPassed(cond)){//addr表示当前地址。
addr = start_addr;
for(i=0 to 15){
if(reglist[i]==True){
Ri = *addr
addr += 4;
}
}
}
参考:
1.Procedure Call Standard for the ARM® Architecture
http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042e/IHI0042E_aapcs.pdf
2.理解APCS-- ARM过程调用标准
3.Uboot中start.S源码的指令级的详尽解析
http://www.crifan.com/files/doc/docbook/uboot_starts_analysis/release/htmls/index.html
4.指令STMFD和LDMFD分析
5.《arm体系结构与编程》.杜春雷
++1 、 C函数的汇编解析,C函数参数多于四个的情况:
//cadd.c
int add(int a, int b, int c, int d, int e)
{
return a+b+c+d+e;
}
int func_add(int a,int b, int c, int d, int e)
{
return add(a, b, c, d, e);
}
int main()
{
int a = 1;
int b = 2;
int c = 3;
int d = 4;
int e = 5;
func_add(a,b,c,d,e);
return 0;
}
root@ubuntu:/work/demo/casm# arm-linux-gcc -S cadd.c
root@ubuntu:/work/demo/casm# cat cadd.s
.file "cadd.c"
.text
.align 2
.global add
.type add, %function
add:
@ args = 4, pretend = 0, frame = 16
@ frame_needed = 1, uses_anonymous_args = 0
mov ip, sp
stmfd sp!, {fp, ip, lr, pc}
sub fp, ip, #4
sub sp, sp, #16
str r0, [fp, #-16]
str r1, [fp, #-20]
str r2, [fp, #-24]
str r3, [fp, #-28]
ldr r2, [fp, #-16]
ldr r3, [fp, #-20]
add r3, r2, r3
ldr r2, [fp, #-24]
add r3, r3, r2
ldr r2, [fp, #-28]
add r3, r3, r2
ldr r2, [fp, #4]
add r3, r3, r2
mov r0, r3
sub sp, fp, #12
ldmfd sp, {fp, sp, pc}
.size add, .-add
.align 2
.global func_add
.type func_add, %function
func_add:
@ args = 4, pretend = 0, frame = 16
@ frame_needed = 1, uses_anonymous_args = 0
mov ip, sp
stmfd sp!, {fp, ip, lr, pc}
sub fp, ip, #4
sub sp, sp, #20
str r0, [fp, #-16]
str r1, [fp, #-20]
str r2, [fp, #-24]
str r3, [fp, #-28]
ldr r3, [fp, #4]
str r3, [sp, #0]
ldr r0, [fp, #-16]
ldr r1, [fp, #-20]
ldr r2, [fp, #-24]
ldr r3, [fp, #-28]
bl add
mov r3, r0
mov r0, r3
sub sp, fp, #12
ldmfd sp, {fp, sp, pc}
.size func_add, .-func_add
.align 2
.global main
.type main, %function
main:
@ args = 0, pretend = 0, frame = 20
@ frame_needed = 1, uses_anonymous_args = 0
mov ip, sp
stmfd sp!, {fp, ip, lr, pc}
sub fp, ip, #4
sub sp, sp, #24
mov r3, #1
str r3, [fp, #-16]
mov r3, #2
str r3, [fp, #-20]
mov r3, #3
str r3, [fp, #-24]
mov r3, #4
str r3, [fp, #-28]
mov r3, #5
str r3, [fp, #-32]
ldr r3, [fp, #-32]
str r3, [sp, #0]
ldr r0, [fp, #-16]
ldr r1, [fp, #-20]
ldr r2, [fp, #-24]
ldr r3, [fp, #-28]
bl func_add
mov r3, #0
mov r0, r3
sub sp, fp, #12
ldmfd sp, {fp, sp, pc}
.size main, .-main
.ident "GCC: (GNU) 3.4.5"
++2 、下面再看如果用某个函数的返回值做为函数的参数,传参次序会不会不一样:
++3、上面的例子没有说明多个参数在栈中的存取次序是怎样的。
下面的例子来补充说明。