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

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过程调用标准

http://blog.csdn.net/keyboardota/article/details/6799054

3.Uboot中start.S源码的指令级的详尽解析

http://www.crifan.com/files/doc/docbook/uboot_starts_analysis/release/htmls/index.html

4.指令STMFD和LDMFD分析
http://blog.csdn.net/chuchuanchuan/article/details/8098987
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、上面的例子没有说明多个参数在栈中的存取次序是怎样的。

下面的例子来补充说明。

posted @ 2014-12-03 14:11  oucaijun  阅读(5009)  评论(0编辑  收藏  举报
下载TeamViewer完整版 下载TeamViewer