汇编:普通的函数调用的汇编代码解析
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、上面的例子没有说明多个参数在栈中的存取次序是怎样的。
下面的例子来补充说明。