解析C程序的内部机制(转)
1.几条汇编指令_bl_add_sub_ldm_stm
⑥ADD/SUB 加法/减法
举例1:
add r0,r1,#4
效果为
r0=r1+4;
举例2:
sub r0,r1,#4
效果为
r0=r1-4;
举例3:
sub r0,r1,r2
效果为
r0=r1-r2;
⑦BL (Brarch and Link)带返回值的跳转 跳转到指定指令,并将返回地址(下一条指令)保存在lr寄存器;
⑧LDM/STM 读内存,写入多个寄存器/把多个寄存器的值写入内存
可搭配的后缀有 过后增加(Increment After)、预先增加(Increment Before)、过后减少(Decrement After)、预先减少(Decrement Before);
举例1:
stmdb sp!, (fp,ip,lr,pc)
假设Sp=4096。 db意思是先减后存,按 高编号寄存器存在高地址 存。
举例2:
ldmia sp, (fp,ip,pc)
2.解析C程序的内部机制
003_led.c内部机制分析:
start.S:
①设置栈;
②调用main,并把返回值地址保存到lr中;
led.c的main()内容:
①定义2个局部变量;
②设置变量;
③return 0;
问题:
①为什么要设置栈?
因为c函数要用。
②怎么使用栈?
a.保存局部变量;
b.保存lr等寄存器;
③调用者如何传参数给被调用者?
④被调用者如何传返回值给调用者?
⑤怎么从栈中恢复那些寄存器?
在arm中有个ATPCS规则,约定r0-r15寄存器的用途。
r0-r3:调用者和被调用者之间传参数;
r4-r11:函数可能被使用,所以在函数的入口保存它们,在函数的出口恢复它们;
下面分析个实例 start.S:
.text .global _start _start: /* 设置内存: sp 栈 */ ldr sp, =4096 /* nand启动 */ // ldr sp, =0x40000000+4096 /* nor启动 */ /* 调用main */ bl main halt: b halt
led.c:
int main() { unsigned int *pGPFCON = (unsigned int *)0x56000050; unsigned int *pGPFDAT = (unsigned int *)0x56000054; /* 配置GPF4为输出引脚 */ *pGPFCON = 0x100; /* 设置GPF4输出0 */ *pGPFDAT = 0; return 0; }
将前面的程序反汇编得到led.dis如下:
led.elf: file format elf32-littlearm Disassembly of section .text: 00000000 <_start>: 0: e3a0da01 mov sp, #4096 ; 0x1000 4: eb000000 bl c <main> 00000008 <halt>: 8: eafffffe b 8 <halt> 0000000c <main>: c: e1a0c00d mov ip, sp 10: e92dd800 stmdb sp!, {fp, ip, lr, pc} 14: e24cb004 sub fp, ip, #4 ; 0x4 18: e24dd008 sub sp, sp, #8 ; 0x8 1c: e3a03456 mov r3, #1442840576 ; 0x56000000 20: e2833050 add r3, r3, #80 ; 0x50 24: e50b3010 str r3, [fp, #-16] 28: e3a03456 mov r3, #1442840576 ; 0x56000000 2c: e2833054 add r3, r3, #84 ; 0x54 30: e50b3014 str r3, [fp, #-20] 34: e51b2010 ldr r2, [fp, #-16] 38: e3a03c01 mov r3, #256 ; 0x100 3c: e5823000 str r3, [r2] 40: e51b2014 ldr r2, [fp, #-20] 44: e3a03000 mov r3, #0 ; 0x0 48: e5823000 str r3, [r2] 4c: e3a03000 mov r3, #0 ; 0x0 50: e1a00003 mov r0, r3 54: e24bd00c sub sp, fp, #12 ; 0xc 58: e89da800 ldmia sp, {fp, sp, pc} Disassembly of section .comment: 00000000 <.comment>: 0: 43434700 cmpmi r3, #0 ; 0x0 4: 4728203a undefined 8: 2029554e eorcs r5, r9, lr, asr #10 c: 2e342e33 mrccs 14, 1, r2, cr4, cr3, {1} 10: Address 0x10 is out of bounds.
分析上面的汇编代码:
开发板上电后,将从0地址开始执行,即开始执行
mov sp, #4096:设置栈地址在4k RAM的最高处,sp=4096; bl c <main>:调到c地址处的main函数,并保存下一行代码地址到lr,即lr=8; mov ip, sp:给ip赋值sp的值,ip=sp=4096 stmdb sp!, {fp, ip, lr, pc}:按高编号寄存器存在高地址,依次将pc、lr、ip、fp存入sp-4中; sub fp, ip, #4:fp的值为ip-4=4096-4=4092; sub sp, sp, #8:sp的值为sp-8=(4096-4x4)-8=4072; mov r3, #1442840576:r3赋值0x5600 0000; add r3, r3, #80:r3的值加0x50,即r3=0x5600 0050; str r3, [fp, #-16]:r3存入[fp-16]所在的地址,即地址4076处存放0x5600 0050; mov r3, #1442840576:r3赋值0x5600 0000; add r3, r3, #84:r3的值加0x54,即r3=0x5600 0054; str r3, [fp, #-20]:r3存入[fp-20]所在的地址,即地址4072处存放0x5600 0054; ldr r2, [fp, #-16]:r2取[fp-16]地址处的值,即[4076]地址的值,r2=0x5600 0050; mov r3, #256:r3赋值为0x100; str r3, [r2]:将r3写到r2内容所对应的地址,即0x5600 0050地址处的值为0x100;;对应c语言*pGPFCON = 0x100;; ldr r2, [fp, #-20]:r2取[fp-20]地址处的值,即[4072]地址的值,r2=0x5600 0054; mov r3, #0:r3赋值为0x00; str r3, [r2]:将r3写到r2内容所对应的地址,即0x5600 0054地址处的值为0x00;对应c语言*pGPFDAT = 0; mov r3, #0:r3赋值为0x00; mov r0, r3:r0=r3=0x00; sub sp, fp, #12:sp=fp-12=4092-12=4080; ldmia sp, {fp, sp, pc}:从栈中恢复寄存器,fp=4080地址处的值=原来的fp,sp=4084地址处的值=4096,pc=4088地址处的值=8,随后调到0x08地址处继续执行。
过程中的内存数据情况:
前面那个例子,汇编调用main.c并没有传递参数,这里修改下c程序,让其传递参数。
start.S:
.text .global _start _start: /* 设置内存: sp 栈 */ ldr sp, =4096 /* nand启动 */ // ldr sp, =0x40000000+4096 /* nor启动 */ mov r0, #4 bl led_on ldr r0, =100000 bl delay mov r0, #5 bl led_on halt: b halt
led.c:
void delay(volatile int d) { while (d--); } int led_on(int which) { unsigned int *pGPFCON = (unsigned int *)0x56000050; unsigned int *pGPFDAT = (unsigned int *)0x56000054; if (which == 4) { /* 配置GPF4为输出引脚 */ *pGPFCON = 0x100; } else if (which == 5) { /* 配置GPF5为输出引脚 */ *pGPFCON = 0x400; } /* 设置GPF4/5输出0 */ *pGPFDAT = 0; return 0; }
led.elf:
led.elf: file format elf32-littlearm Disassembly of section .text: 00000000 <_start>: 0: e3a0da01 mov sp, #4096 ; 0x1000 4: e3a00004 mov r0, #4 ; 0x4 8: eb000012 bl 58 <led_on> c: e59f000c ldr r0, [pc, #12] ; 20 <.text+0x20> 10: eb000003 bl 24 <delay> 14: e3a00005 mov r0, #5 ; 0x5 18: eb00000e bl 58 <led_on> 0000001c <halt>: 1c: eafffffe b 1c <halt> 20: 000186a0 andeq r8, r1, r0, lsr #13 00000024 <delay>: 24: e1a0c00d mov ip, sp 28: e92dd800 stmdb sp!, {fp, ip, lr, pc} 2c: e24cb004 sub fp, ip, #4 ; 0x4 30: e24dd004 sub sp, sp, #4 ; 0x4 34: e50b0010 str r0, [fp, #-16] 38: e51b3010 ldr r3, [fp, #-16] 3c: e2433001 sub r3, r3, #1 ; 0x1 40: e50b3010 str r3, [fp, #-16] 44: e51b3010 ldr r3, [fp, #-16] 48: e3730001 cmn r3, #1 ; 0x1 4c: 0a000000 beq 54 <delay+0x30> 50: eafffff8 b 38 <delay+0x14> 54: e89da808 ldmia sp, {r3, fp, sp, pc} 00000058 <led_on>: 58: e1a0c00d mov ip, sp 5c: e92dd800 stmdb sp!, {fp, ip, lr, pc} 60: e24cb004 sub fp, ip, #4 ; 0x4 64: e24dd00c sub sp, sp, #12 ; 0xc 68: e50b0010 str r0, [fp, #-16] 6c: e3a03456 mov r3, #1442840576 ; 0x56000000 70: e2833050 add r3, r3, #80 ; 0x50 74: e50b3014 str r3, [fp, #-20] 78: e3a03456 mov r3, #1442840576 ; 0x56000000 7c: e2833054 add r3, r3, #84 ; 0x54 80: e50b3018 str r3, [fp, #-24] 84: e51b3010 ldr r3, [fp, #-16] 88: e3530004 cmp r3, #4 ; 0x4 8c: 1a000003 bne a0 <led_on+0x48> 90: e51b2014 ldr r2, [fp, #-20] 94: e3a03c01 mov r3, #256 ; 0x100 98: e5823000 str r3, [r2] 9c: ea000005 b b8 <led_on+0x60> a0: e51b3010 ldr r3, [fp, #-16] a4: e3530005 cmp r3, #5 ; 0x5 a8: 1a000002 bne b8 <led_on+0x60> ac: e51b2014 ldr r2, [fp, #-20] b0: e3a03b01 mov r3, #1024 ; 0x400 b4: e5823000 str r3, [r2] b8: e51b3018 ldr r3, [fp, #-24] bc: e3a02000 mov r2, #0 ; 0x0 c0: e5832000 str r2, [r3] c4: e3a03000 mov r3, #0 ; 0x0 c8: e1a00003 mov r0, r3 cc: e24bd00c sub sp, fp, #12 ; 0xc d0: e89da800 ldmia sp, {fp, sp, pc} Disassembly of section .comment: 00000000 <.comment>: 0: 43434700 cmpmi r3, #0 ; 0x0 4: 4728203a undefined 8: 2029554e eorcs r5, r9, lr, asr #10 c: 2e342e33 mrccs 14, 1, r2, cr4, cr3, {1} 10: Address 0x10 is out of bounds.
简单分析下反汇编:
mov sp, #4096:设置栈地址在4k RAM的最高处,sp=4096; mov r0, #4:r0=4,作为参数; bl 58 <led_on>:调到58地址处的led_on函数,并保存下一行代码地址到lr,即lr=8;在led_on中会使用到r0; ldr r0, [pc, #12]:r0=[pc+12]处的值=[c+12=20]的值=0x186a0=1000000,作为参数; bl 24 <delay>:调用24地址处的delay函数,并保存下一行代码地址到lr,即lr=24;在delay中会使用到r0; mov r0, #5:r0=5,作为参数; bl 58 <led_on>:调到58地址处的led_on函数,并保存下一行代码地址到lr,即lr=58;在led_on中会使用到r0;