TX2440 裸跑实验-汇编C语言混编(ADS1.2编译) 进阶(二)
一.实验目的:
理解汇编的编程结构,有助于学习ARM汇编与C语言的调用关系,熟悉堆栈的调用关系.
二.实验环境:
TX2440平台
三.实验工具:
ADS1.2 + AXD + JLINK调试
四.实验步骤:
1.在ads 上建立新工程,增加两个源文件call_C_func.s 和 func.c
1)call_C_func.s源码
;我的assembler程序,EastonWoo procedure
area init,code,readonly
import add_8
import main
export assembler_add_4
export assembler_add_5
export assembler_add_8
entry ;一个进程中至少有一个.
start
ldr sp,=0x33ff8000 ;程序有效开始指令地址:0x30000000,,堆栈设在内存里面,也就是大于0x30000000,注意不要跟代码区冲突;
;如果设成0x1000,是写不进栈的,读出来是0xFFFFFFFF.
;********************一:汇编中调用C语言函数:***************************
stmfd sp!,{r0-r3};start看作子程序,保护好r0-r3 压栈
;5个实参:优先使用ip,即r0-r3,其中ip.ip作为第5个实参
;6个实参:优先使用ip,lr,即r0-r3,ip,lr.其中ip,lr分别作为第5,6个实参.
;其中lr会在子程序压栈,出栈.
;7个实参:优先使用ip,lr,和一个保存局部变量的寄存器v1,即r0-r3,ip,lr,v1.其中ip,lr,v1分别作为第5,6,7个实参.
;其中lr,v1会在子程序压栈,出栈
;8个实参:优先使用ip,lr,和二个保存局部变量的寄存器v1,v2,即r0-r3,ip,lr,v1,v2.其中ip,lr,v1,v2分别作为第5,6,7,8个实参.
;其中lr,v1,v2会在子程序压栈,出栈
;如此类推...
;15个参数开始就更复杂了.因为所有的局部变量寄存器全用了.4+2+8=14.以后有时间再来研究.
;8个参数例子,调用C函数例子.
mov v2,#8
mov v1,#7
mov lr,#6
mov ip,#5
stmfd sp!,{v1-v2} ;压栈,先压8,7,也就释放了v1,v2寄存器.add_8里会用到这些寄存器.
stmfd sp!,{ip,lr} ;压栈,再压6,5,也就释放了ip,lr寄存器.add_8里会用到这些寄存器.
mov r3,#4
mov r2,#3
mov r1,#2
mov r0,#1
bl add_8 ;返回值为r0
add sp,sp,#16 ;出栈.
;********************二:C语言中调用汇编函数:***************************
mov v2,#8 ;8个实参进栈.这里就不重复叙述了.
mov v1,#7
mov lr,#6
stmfd sp!,{v1-v2} ;压栈,先压8,7,也就释放了v1,v2寄存器.add_8里会用到这些寄存器.
stmfd sp!,{ip,lr} ;压栈,再压6,5,也就释放了ip,lr寄存器.add_8里会用到这些寄存器.
mov r3,#4
mov r2,#3
mov r1,#2
mov r0,#1
bl main ;bl已经保存了b over 这个地址到lr.
add sp,sp,#16 ;出栈.
ldmfd sp!,{r0-r3}
b over
assembler_add_4 ;子程序直接使用r0-r3这四个实参.
add r0,r0,r1
add r0,r0,r2
add r0,r0,r3
mov pc,lr
assembler_add_5 ;子程序直接使用r0-r3这四个实参和ip这个第5个实参.
add r0,r0,r1
add r0,r0,r2
add r0,r0,r3
add r0,r0,ip
mov pc,lr
;这个8位的汇编函数相当于之前我们讨论的8个参数的被调用C函数的反汇编代码.
assembler_add_8 ;前4个实参,子程序直接使用r0-r3这四个实参;后4个实参分别由ip,lr,v1-v2都是在栈里得来.
stmfd sp!,{v1-v2,lr}
add ip,r13,#0x14
ldmia ip,{v1-v2}
ldr ip,[r13,#0xc]
ldr lr,[r13,#0x10]
add r0,r0,r1
add r0,r0,r2
add r0,r0,r3
add r0,r0,ip
add r0,r0,lr
add r0,r0,v1
add r0,r0,v2
ldmfd sp!,{v1-v2,pc}
over
nop ;无用功.为了编译器不报警
end
2) func.c 源码,EastonWoo procedure
//我的C语言 实验程序
#include <stdio.h>
int add_8(int a,int b,int c, int d, int e, int f,int g, int h)
{
return (a+b+c+d+e+f+g+h);
/*
0x00000000: e92d4030 0@-. STMFD r13!,{r4,r5,r14} ;v1,v2,lr压栈
0x00000004: e59d4014 .@.. LDR r4,[r13,#0x14] ;v1 = 7
0x00000008: e59d5018 .P.. LDR r5,[r13,#0x18] ;v2 = 8
0x0000000c: e59dc00c .... LDR r12,[r13,#0xc] ;ip = 5
0x00000010: e59de010 .... LDR r14,[r13,#0x10] ;ip = 6
0x00000014: e0800001 .... ADD r0,r0,r1 ;r0 = 1 + 2 = 3
0x00000018: e0800002 .... ADD r0,r0,r2 ;r0 = 3 + 3 = 6
0x0000001c: e0800003 .... ADD r0,r0,r3 ;r0 = 6 + 4 = 0x0A
0x00000020: e080000c .... ADD r0,r0,r12 ;r0 = 0x0A + 5 = 0x0F
0x00000024: e080000e .... ADD r0,r0,r14 ;r0 = 0x0F + 6 = 0x15
0x00000028: e0800004 .... ADD r0,r0,r4 ;r0 = 0x15 + 7 = 0x1C
0x0000002c: e0800005 .... ADD r0,r0,r5 ;r0 = 0x1C + 8 = 0x24
0x00000030: e8bd8030 0... LDMFD r13!,{r4,r5,pc} ;把局部寄存器和下一指令出栈,pc 等于 bl add_8的下一条指令地址
*/
}
extern assembler_add_4(int a, int b, int c,int d);
extern assembler_add_5(int a, int b, int c,int d,int e);
extern assembler_add_8(int a,int b,int c, int d, int e, int f,int g, int h);
int main(int a,int b,int c, int d, int e, int f,int g, int h)
{
int sum1 = 0;
int sum2 = 0;
int sum3 = 0;
sum1 = assembler_add_4(a,b,c,d);
sum2 = assembler_add_5(a,b,c,d,e);
sum3 = assembler_add_8(a,b,c,d,e,f,g,h);
return (sum1+sum2+sum3);
/*
0x00000034: e92d4ff0 .O-. STMFD r13!,{r4-r11,r14} ;局部寄存器和lr入栈.运行得sp=0x33ff7fe0,运行后sp=0x33ff7fbc
0x00000038: e24dd014 ..M. SUB r13,r13,#0x14 ;新开辟5个sp空间,共20字节.运行得sp=0x33ff7fbc,运行后sp=0x33ff7fa8
0x0000003c: e59d8038 8... LDR r8,[r13,#0x38] ;r8 = 5;0x33ff7fe0的数据给r8,即进main前的ip
0x00000040: e59d9044 D... LDR r9,[r13,#0x44] ;r9 = 8;0x33ff7fec的数据给r3,即进main前的v2
0x00000044: e1a07003 .p.. MOV r7,r3 ;r7 = r3 = 4 ;r0-r3都是全局寄存器,即进main前的r0-r3
0x00000048: e1a06002 .`.. MOV r6,r2 ;r6 = r2 = 3
0x0000004c: e1a05001 .P.. MOV r5,r1 ;r5 = r1 = 2
0x00000050: e1a04000 .@.. MOV r4,r0 ;r4 = r0 = 1
0x00000054: e1a03007 .0.. MOV r3,r7 ;r3 = r7 = 4 ;不知道这有什么用?像是多此一举.其实这是调用assembler_add_4前的4个实参;前面的r7 = r3只是保护现场.
0x00000058: e1a02006 . .. MOV r2,r6 ;r2 = r6 = 3
0x0000005c: e1a01005 .... MOV r1,r5 ;r1 = r5 = 2
0x00000060: e1a00004 .... MOV r0,r4 ;r0 = r4 = 1
0x00000064: ebfffffe .... BL assembler_add_4 ;调用assembler_add_4函数,已把MOV r10,r0指令地址保存到了lr
0x00000068: e1a0a000 .... MOV r10,r0 ;r10 = r0 = 0x0A;把调用assembler_add_4函数的返回值保存到r10
0x0000006c: e1a03007 .0.. MOV r3,r7 ;r3 = r7 = 4 ;又是一个函数的实参赋值.
0x00000070: e1a02006 . .. MOV r2,r6 ;r2 = r6 = 3
0x00000074: e1a01005 .... MOV r1,r5 ;r1 = r5 = 2
0x00000078: e1a00004 .... MOV r0,r4 ;r0 = r4 = 1
0x0000007c: e58d8000 .... STR r8,[r13,#0] ;r8 入栈.即进main前的ip保存到sp地址0x33ff7fa8里.
0x00000080: ebfffffe .... BL assembler_add_5 ;调用assembler_add_5函数,已把MOV r11,r0指令地址保存到了lr
0x00000084: e1a0b000 .... MOV r11,r0 ;r11 = r0 = 0x0F;把调用assembler_add_5函数的返回值保存到r11
0x00000088: e59d103c <... LDR r1,[r13,#0x3c] ;r1 = 6;0x33ff7fe4的数据给r1,即进main前的lr
0x0000008c: e59d2040 @ .. LDR r2,[r13,#0x40] ;r2 = 7;0x33ff7fe8的数据给r2,即进main前的v1
0x00000090: e98d0206 .... STMIB r13,{r1,r2,r9} ;r1,r2,r9 入栈.即把r1=6,r2=7,r9=8分别放到0x33ff7fac,0x33ff7fb0,0x33ff7fb4里.
0x00000094: e58d8000 .... STR r8,[r13,#0] ;r8 入栈.即进main前的ip保存到sp地址0x33ff7fa8里.
0x00000098: e1a02006 . .. MOV r2,r6 ;r2 = r6 = 3 ;又是一个函数的实参赋值.
0x0000009c: e1a01005 .... MOV r1,r5 ;r1 = r5 = 2
0x000000a0: e1a03007 .0.. MOV r3,r7 ;r3 = r7 = 4
0x000000a4: e1a00004 .... MOV r0,r4 ;r0 = r4 = 1
0x000000a8: ebfffffe .... BL assembler_add_8 ;调用assembler_add_8函数,已把MOV r1,r0指令地址保存到了lr
0x000000ac: e1a01000 .... MOV r1,r0 ;r1 = r0 = 0x24;把调用assembler_add_8函数的返回值保存到r1
0x000000b0: e08a200b . .. ADD r2,r10,r11 ;r2 = r10 + r11 = 0x0A + 0x0F = 0x19;也就是sum1 + sum2
0x000000b4: e0820001 .... ADD r0,r2,r1 ;r0 = r2 + r1 = 0x19 + 0x24 = 0x3D;也就是sum1 + sum2 + sum3
0x000000b8: e28dd014 .... ADD r13,r13,#0x14 ;释放新开辟的栈空间,共20个字节.
0x000000bc: e8bd8ff0 .... LDMFD r13!,{r4-r11,pc} ;出栈.恢复局部变量.add sp,sp,#16的地址赋给了pc.
总的来说,函数里面要做的事分以下5点:
1),保护现场.主要保护要使用到的局部寄存器和下一条指令lr
2),开辟栈空间.方便保存临时数据和再次调用子函数的参数入栈.
3),函数体的逻辑运算.
4),释放新开辟的栈空间.
5),恢复现场.出栈.
这跟我之前写的一篇博文<堆栈在内存中的压栈和弹栈工作原理>原理是差不多的.
*/
}
setmem 0x4a000008,0xffffffff,32
setmem 0x4a00001c,0x00007fff,32
setmem 0x48000000,0x2212d110,32
setmem 0x48000004,0x00000f40,32
setmem 0x48000008,0x00002e50,32
setmem 0x4800000c,0x00002e50,32
setmem 0x48000010,0x00002e50,32
setmem 0x48000014,0x00002e50,32
setmem 0x48000018,0x00002e50,32
setmem 0x4800001c,0x00018005,32
setmem 0x48000020,0x00018005,32
setmem 0x48000024,0x00960542,32
setmem 0x48000028,0x00000032,32
setmem 0x4800002c,0x00000030,32
setmem 0x48000030,0x00000030,32