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图:

汇编:普通的函数调用的汇编代码解析_ubuntu_02


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"


汇编:普通的函数调用的汇编代码解析_堆栈_03


++2 、下面再看如果用某个函数的返回值做为函数的参数,传参次序会不会不一样:

汇编:普通的函数调用的汇编代码解析_堆栈_04


++3、上面的例子没有说明多个参数在栈中的存取次序是怎样的。

下面的例子来补充说明。

汇编:普通的函数调用的汇编代码解析_ubuntu_05