C函数调用


title: C函数调用
tags: ARM
date: 2018-10-14 16:37:10

C函数调用

设置SP

C函数启动需要设置堆栈,因为局部变量都是存在堆栈的,函数调用也需要栈

但是2440中NAND启动和NOR启动的时候,片内RAM的地址是不一样的.

  • NOR,0x4000,0000+4K
  • NAND,0+4K

SP分析

从NAND启动的代码分析

//////////////////////////start.S//////////////////////
.text
.global _start
_start:
	/* 设置内存: sp 栈 */
	ldr sp, =4096  				/* nand启动 */
	//ldr sp, =0x40000000+4096  /* nor启动 */
	/* 调用main */
	bl main
halt:
	b halt
///////////////////main.c///////////////////////////
int main()
{
	unsigned int *pGPFCON = (unsigned int *)0x56000050;
	unsigned int *pGPFDAT = (unsigned int *)0x56000054;

	/* 配置GPF4为输出引脚 */
	*pGPFCON = 0x100;
	
	/* 设置GPF4输出0 */
	*pGPFDAT = 0;

	return 0;
}
//////////////makefile/////////////////////////////////
all:
	arm-linux-gcc -c -o led.o led.c
	arm-linux-gcc -c -o start.o start.S
	arm-linux-ld -Ttext 0 start.o led.o -o led.elf
	arm-linux-objcopy -O binary -S led.elf led.bin
	arm-linux-objdump -D led.elf > led.dis
clean:
	rm *.bin *.o *.elf *.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.

main函数的流程

  1. 保存sp
  2. 保存lr和pc,以及会被改变的寄存器的值
  3. 数据处理完后把寄存器恢复

汇编解析

LDMED    LDMIB    预先增加装载
LDMFD    LDMIA    过后增加装载
LDMEA    LDMDB    预先减少装载
LDMFA    LDMDA    过后减少装载

STMFA    STMIB    预先增加存储
STMEA    STMIA    过后增加存储
STMFD    STMDB    预先减少存储
STMED    STMDA    过后减少存储

mark

区分NAND和NOR启动

NAND启动的时候,代码是在NAND中,但实际上是上电自动copy代码到片内RAM,也就是实际是在片内RAM运行,可读可写.通过判断0地址是否可写即可判断

//1.读出0地址的值备份到r0
mov r1,#0
ldr r0,[r1]

//r1=0
//2.在0地址写入0
//把r1的值写入[r1]所在内存
str r1,[r1]

//3.读出0地址的值
ldr r2,[r1]

//4.比较 写入的值和读出的值
cmp r2,r1

ldr sp, =0x40000000+4096 /* 先假设是nor启动 */

//如果相等则是nand启动
moveq sp, #4096  /* nand启动 */
streq r0, [r1]   /* 恢复原来的值 */

完整调用如下

.text
.global _start

_start:

	/* 关闭看门狗 */
	ldr r0, =0x53000000
	ldr r1, =0
	str r1, [r0]

	/* 设置内存: sp 栈 */
	/* 分辨是nor/nand启动
	 * 写0到0地址, 再读出来
	 * 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动
	 * 否则就是nor启动
	 */
	mov r1, #0
	ldr r0, [r1] /* 读出原来的值备份 */
	str r1, [r1] /* 0->[0] */ 
	ldr r2, [r1] /* r2=[0] */
	cmp r1, r2   /* r1==r2? 如果相等表示是NAND启动 */
	ldr sp, =0x40000000+4096 /* 先假设是nor启动 */
	moveq sp, #4096  /* nand启动 */
	streq r0, [r1]   /* 恢复原来的值 */
	

	bl main

halt:
	b halt

参数调用

遵循ATPCS原则,r0~r4存放参数,lr为返回地址,参考ATPCS规则.md

原文地址:https://www.cnblogs.com/zongzi10010/p/10023525.html