匯編:普通的函數調用的匯編代碼解析


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、上面的例子沒有說明多個參數在棧中的存取次序是怎樣的。

下面的例子來補充說明。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM