APCS - ARM汇编指令(五)

ARM procedure call standard(APCS),ARM过程调用标准提供了紧凑编写历程的一种机制,方便C语言和汇编或者其他类型的语言之间相互进行调用。基本的ATPCS规则包括寄存器的使用规则、数据栈使用规则、参数传递规则。

1. APCS规则

1.1. 寄存器的使用规则

ARM处理器中有r0 - r15共16个寄存器,寄存器用途给定约定的使用规则,并给这些寄存器命名了相应的别名。寄存器具体的使用规则总结:

  • 子程序通过寄存器r0 ~ r3来传递参数,这是可以使用他们的别名a0 ~ a3,被调用的子程序返回时无需回复r0 ~ r3的内容。函数返回值可以使用r0寄存器来完成。
  • r4 ~ r10寄存器来保存局部变量,如果子程序中大量使用这些寄存器,在子函数中调用子函数也大量使用这些寄存区,这个时候这些局部变量在函数调用时也需要和r11 ~ r15一起压栈。
  • r11 ~ r15寄存器用作函数栈调用,分别去了不同的名字完成特定的功能。
Reg # 寄存器APCS别名意义与使用规则
R0a1工作寄存器,函数调用参数
R1a2"
R2a3"
R3a4"
R4v1必须保护
R5v2"
R6v3"
R7v4"
R8v5"
R9v6"
R10sl栈限制,数据栈限制指针,局部变量寄存器
R11fp桢指针,局部变量信息获取靠这个指针计算移动
R12ip子程序内部调用,用来保存当前函数的栈的最顶端地址
R13sp栈指针,当前函数栈的最低位置地址
R14lr连接寄存器,保存函数调用函数下一条指令地址,用于返回
R15pc程序计数器,记录程序当前执行地址

ATPCS规则寄存器命令与功能说明表

1.2. 数据栈使用规则

数据栈有两个增长方向:向内存地址减小的方向增长时,称为descending栈;向内存地址增加的方向增长时,称为ascending栈。所谓数据栈的增长就是移动栈指针。当栈指针指向栈顶元素(最后一个入栈的数据)时,称为FULL栈;当栈指针指向栈顶元素(最后一个入栈的数据)相邻的一个空的数据单元时,称为empty栈。综合这两个点,数据栈共有4种组合:

  • FD: full descending,满递减
  • ED:empty descending,空递减
  • FA: full ascending,满递增
  • EA: empty ascending,空递减

APCS规定数据栈为FD栈类型,并且对数据栈的操作是8字节对齐的,使用stmdb/ldmia批量内存访问指令来操作FD数据栈。使用stmdb命令往数据占中保存内容时,先递减sp指针,在保存数据,使用ldmia命令从数据栈中恢复数据时,先获取数据,在递增sp指针,sp指针总是指向栈顶元素,这刚好是FD栈的定义。

2. C代码调用分析

代码结构首先通过start.S函数进行启动引导,然后跳转到C代码处理函数中。在汇编引导函数中,必须根据自己实际的硬件情况指定sp寄存器的栈地址,这个地址保证数据是可以实现存取,主要是用来进行压栈和临时变量的存取功能。

.text
.global _start

_start:
	/* 设置内存: sp 栈 */
	ldr sp, =4096  /* nand启动 */
//	ldr sp, =0x40000000+4096  /* nor启动 */

	/* 调用main */
	bl main

halt:
	b halt
int copy_code_to_sdram(unsigned char *buf, unsigned long start_addr, int size)
{
	int ret = 0;
	int i = 0;
	if (0 != buf)
	{
		for (i = 0; i < size; i++)
			*buf = *((char *)start_addr);
	}

	return ret;
}

int main()
{
	unsigned int *pGPFCON = (unsigned int *)0x56000050;
	unsigned int *pGPFDAT = (unsigned int *)0x56000054;

	unsigned int *SDRAM_BASE = (unsigned int *)0x30000000;
	/* 配置GPF4为输出引脚 */
	*pGPFCON = 0x100;

	/* 可执行文件内容拷贝 */
	const long start_addr = 0x0;
	const int file_size = 1024;
	int ret = copy_code_to_sdram((char *)SDRAM_BASE, start_addr, file_size);
	if (ret != 0)
		return ret;

	/* 设置GPF4输出0 */
	*pGPFDAT = 0;

	return 0;
}

产生的汇编代码


led.elf:     file format elf32-littlearm

Disassembly of section .text:

00000000 <_start>:
   0:	e3a0da01 	mov	sp, #4096	; 0x1000
   4:	eb000020 	bl	8c <main>

00000008 <halt>:
   8:	eafffffe 	b	8 <halt>

0000000c <copy_code_to_sdram>:
   c:	e1a0c00d 	mov	ip, sp
  10:	e92dd800 	stmdb	sp!, {fp, ip, lr, pc}
  14:	e24cb004 	sub	fp, ip, #4	; 0x4
  18:	e24dd014 	sub	sp, sp, #20	; 0x14
  1c:	e50b0010 	str	r0, [fp, #-16]
  20:	e50b1014 	str	r1, [fp, #-20]
  24:	e50b2018 	str	r2, [fp, #-24]
  28:	e3a03000 	mov	r3, #0	; 0x0
  2c:	e50b301c 	str	r3, [fp, #-28]
  30:	e3a03000 	mov	r3, #0	; 0x0
  34:	e50b3020 	str	r3, [fp, #-32]
  38:	e51b3010 	ldr	r3, [fp, #-16]
  3c:	e3530000 	cmp	r3, #0	; 0x0
  40:	0a00000d 	beq	7c <copy_code_to_sdram+0x70>
  44:	e3a03000 	mov	r3, #0	; 0x0
  48:	e50b3020 	str	r3, [fp, #-32]
  4c:	e51b2020 	ldr	r2, [fp, #-32]
  50:	e51b3018 	ldr	r3, [fp, #-24]
  54:	e1520003 	cmp	r2, r3
  58:	aa000007 	bge	7c <copy_code_to_sdram+0x70>
  5c:	e51b2010 	ldr	r2, [fp, #-16]
  60:	e51b3014 	ldr	r3, [fp, #-20]
  64:	e5d33000 	ldrb	r3, [r3]
  68:	e5c23000 	strb	r3, [r2]
  6c:	e51b3020 	ldr	r3, [fp, #-32]
  70:	e2833001 	add	r3, r3, #1	; 0x1
  74:	e50b3020 	str	r3, [fp, #-32]
  78:	eafffff3 	b	4c <copy_code_to_sdram+0x40>
  7c:	e51b301c 	ldr	r3, [fp, #-28]
  80:	e1a00003 	mov	r0, r3
  84:	e24bd00c 	sub	sp, fp, #12	; 0xc
  88:	e89da800 	ldmia	sp, {fp, sp, pc}

0000008c <main>:
  8c:	e1a0c00d 	mov	ip, sp
  90:	e92dd800 	stmdb	sp!, {fp, ip, lr, pc}
  94:	e24cb004 	sub	fp, ip, #4	; 0x4
  98:	e24dd01c 	sub	sp, sp, #28	; 0x1c
  9c:	e3a03456 	mov	r3, #1442840576	; 0x56000000
  a0:	e2833050 	add	r3, r3, #80	; 0x50
  a4:	e50b3010 	str	r3, [fp, #-16]
  a8:	e3a03456 	mov	r3, #1442840576	; 0x56000000
  ac:	e2833054 	add	r3, r3, #84	; 0x54
  b0:	e50b3014 	str	r3, [fp, #-20]
  b4:	e3a03203 	mov	r3, #805306368	; 0x30000000
  b8:	e50b3018 	str	r3, [fp, #-24]
  bc:	e51b2010 	ldr	r2, [fp, #-16]
  c0:	e3a03c01 	mov	r3, #256	; 0x100
  c4:	e5823000 	str	r3, [r2]
  c8:	e3a03000 	mov	r3, #0	; 0x0
  cc:	e50b301c 	str	r3, [fp, #-28]
  d0:	e3a03b01 	mov	r3, #1024	; 0x400
  d4:	e50b3020 	str	r3, [fp, #-32]
  d8:	e51b0018 	ldr	r0, [fp, #-24]
  dc:	e51b101c 	ldr	r1, [fp, #-28]
  e0:	e51b2020 	ldr	r2, [fp, #-32]
  e4:	ebffffc8 	bl	c <copy_code_to_sdram>
  e8:	e1a03000 	mov	r3, r0
  ec:	e50b3024 	str	r3, [fp, #-36]
  f0:	e51b3024 	ldr	r3, [fp, #-36]
  f4:	e3530000 	cmp	r3, #0	; 0x0
  f8:	0a000002 	beq	108 <main+0x7c>
  fc:	e51b3024 	ldr	r3, [fp, #-36]
 100:	e50b3028 	str	r3, [fp, #-40]
 104:	ea000004 	b	11c <main+0x90>
 108:	e51b2014 	ldr	r2, [fp, #-20]
 10c:	e3a03000 	mov	r3, #0	; 0x0
 110:	e5823000 	str	r3, [r2]
 114:	e3a03000 	mov	r3, #0	; 0x0
 118:	e50b3028 	str	r3, [fp, #-40]
 11c:	e51b0028 	ldr	r0, [fp, #-40]
 120:	e24bd00c 	sub	sp, fp, #12	; 0xc
 124:	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.

2.2. 具体分析

根据上面的和调用结构,进行函数调用结果的分析。 

  1. 入口首先完成栈pc寄存器4096地址设置,然后通过bl跳转到main函数,bl跳转自动将当前bl指令的下一条指令地址赋值给lr寄存器。
  2. 进入main函数,计算栈之后的一个地址并给ip寄存器,使用降序栈stmdb指令进行pc、lr、ip、fp四个寄存器共16直接数据压栈。
  3. 函数中局部变量栈顶16后字节开始,也就是现在sp寄存器保存的地址开始为局部变量预留空间。main函数中共6个局部变量,其中包含了一个返回值复用的变量同28个字节数值。这个就是入口sp = sp-28原因。
  4. 中间调用copy_code_to_sdram子函数,子函数的sp指针在4096 -16 - 28位置,使用bl将当前指令下一条指令放在当前的lr寄存器。
  5. 之后子函数copy_codeto_sdram开始压栈,共16个字节。子函数共3个入参,之后是2个局部变量,共20字节数据,这个函数调用过程中sp寄存器现在计算结果是4096 - 44 - 36字节位置。
  6. 进行完逻辑处理以后,将返回值赋值给r0寄存器,然后将子函数的栈保存数据恢复给寄存器fp,sp,pc,这个和开始压栈命令是前三个进行数据对应。也就是入口位置的pc寄存器保存的内容不需要了。由于pc指针恢复成立了main函数调用是的lr寄存器值,所以进入main函数中调用copy_code_to_sdram的下一条指令进行代码执行。
  7. main函数继续往下执行,执行结束类似于子函数一样进行栈恢复,然后跳转到start.S文件中bl main函数下一条指令进行执行。

另外一幅比较经典的调用关系图像

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值