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