arm64函数源码和汇编解析(objdump)

简单无参数的例子:

gcc -g a.c

objdump -d a.out

get_xx函数:

复制代码
0000000000400634 <get_xx>:
  400634:	a9be7bfd 	stp	x29, x30, [sp, #-32]!  //stp指令将x29,x30保存在sp-32地址,后面的!,是完成后将sp-32的地址值给sp。意味着sp向地地址移动
  400638:	910003fd 	mov	x29, sp          //将sp的值保存到x29寄存器。
  40063c:	d2800100 	mov	x0, #0x8                   	// #8
  400640:	97ffffac 	bl	4004f0 <malloc@plt>       //调malloc函数,返回的地址保存到x0寄存器。也就是get_xx返回的地址。
  400644:	f9000fe0 	str	x0, [sp, #24]     // 将get_xx函数返回的地址p,放入sp+24位置。  因为后面会用到这个地址,因此需要保存在这。       
  400648:	f9400fe0 	ldr	x0, [sp, #24]     //从sp+24位置取出地址p
  40064c:	52800141 	mov	w1, #0xa        //将0xa也就是10,放入w1寄存器。
  400650:	b9000001 	str	w1, [x0]         //将w1寄存器的值,放入x0寄存器的地址,结构体中的a变量在结构体的起始位置 ,因此刚好设置p->a
  400654:	f9400fe0 	ldr	x0, [sp, #24]     //从sp+24位置取出地址p, 前面把x0存入sp+24就是这个目的。
  400658:	528000a1 	mov	w1, #0x5        //同理将5保存到w1,为了下一步设置p->b           	// #5
  40065c:	b9000401 	str	w1, [x0, #4]      //将w1 存入x0偏移4字节的位置,结构体成员b在结构体中偏移4字节。
  400660:	f9400fe0 	ldr	x0, [sp, #24]     //最后取出指针p到x0
  400664:	a8c27bfd 	ldp	x29, x30, [sp], #32  //将在sp所在的地址的值给x29,x30寄存器,并且sp的值=sp+32 。刚好还原函数开始时的sp。
  400668:	d65f03c0 	ret                  //返回,将x30保存的下一条指令给pc

main函数:

复制代码
000000000040066c <main>:
  40066c:	a9be7bfd 	stp	x29, x30, [sp, #-32]!
  400670:	910003fd 	mov	x29, sp
  400674:	97fffff0 	bl	400634 <get_xx>  //bl指令将下一条指令的地址保存到lr(x30)寄存器,ret的时候会将其设置到pc以继续执行
  400678:	f9000fe0 	str	x0, [sp, #24]    //参考get_xx 说明,将函数返回的地址px保存
  40067c:	f9400fe0 	ldr	x0, [sp, #24]    //参考get_xx 说明,将函数返回的地址px保存
  400680:	b9400001 	ldr	w1, [x0]       //将px指针的4字节内容放入w1。由于a只占4字节,后面的内容不拷贝
  400684:	f9400fe0 	ldr	x0, [sp, #24]    //重新取出px指针
  400688:	b9400400 	ldr	w0, [x0, #4]   //在x0+4字节的地址取出b。放入w0
  40068c:	4b000020 	sub	w0, w1, w0   //减法 w0=w1-w0 ,即p->a - p->b
  400690:	b90017e0 	str	w0, [sp, #20]   //保存结果
  400694:	b94017e0 	ldr	w0, [sp, #20]   //取出结果
  400698:	a8c27bfd 	ldp	x29, x30, [sp], #32  //还原x29,x30,sp
  40069c:	d65f03c0 	ret                //返回

Arm64的所有函数,一开始都是这两句:

400634: a9be7bfd stp x29, x30, [sp, #-32]! //stp指令将x29,x30保存在sp-32地址,后面的!,是完成后将sp-32的地址值给sp。意味着sp向地地址移动

400638: 910003fd mov x29, sp //将sp的值保存到x29寄存器。

最后都有这一句:

400698: a8c27bfd ldp x29, x30, [sp], #32 //还原x29,x30,sp

因此函数调用都过stp和ldp来配对使用,保存和恢复某些寄存器:

每次函数都会开辟自己的栈。这个栈的大小根据函数的本地变量或者参数来决定。

带参数的例子,

带5个参数的函数get_xx:

汇编代码:

复制代码
0000000000400684 <get_xx>:
  400684:	a9bc7bfd 	stp	x29, x30, [sp, #-64]!       //开辟的栈空间加大值64字节,用来存储参数。
  400688:	910003fd 	mov	x29, sp
  40068c:	b9002fe0 	str	w0, [sp, #44]            //其中w0-w4为5个参数,分别存储。
  400690:	b9002be1 	str	w1, [sp, #40]
  400694:	b90027e2 	str	w2, [sp, #36]
  400698:	b90023e3 	str	w3, [sp, #32]
  40069c:	b9001fe4 	str	w4, [sp, #28]
  4006a0:	d2800100 	mov	x0, #0x8                   	// #8
  4006a4:	97ffffa3 	bl	400530 <malloc@plt>
  4006a8:	f9001fe0 	str	x0, [sp, #56]
  4006ac:	f9401fe0 	ldr	x0, [sp, #56]
  4006b0:	52800141 	mov	w1, #0xa                   	// #10
  4006b4:	b9000001 	str	w1, [x0]
  4006b8:	f9401fe0 	ldr	x0, [sp, #56]
  4006bc:	528000a1 	mov	w1, #0x5                   	// #5
  4006c0:	b9000401 	str	w1, [x0, #4]
  4006c4:	b9402fe0 	ldr	w0, [sp, #44]
  4006c8:	7100001f 	cmp	w0, #0x0
  4006cc:	54000081 	b.ne	4006dc <get_xx+0x58>  // b.any
  4006d0:	d2955760 	mov	x0, #0xaabb                	// #43707
  4006d4:	f9001fe0 	str	x0, [sp, #56]
  4006d8:	14000002 	b	4006e0 <get_xx+0x5c>
  4006dc:	d503201f 	nop
  4006e0:	f9401fe0 	ldr	x0, [sp, #56]
  4006e4:	a8c47bfd 	ldp	x29, x30, [sp], #64
  4006e8:	d65f03c0 	ret

00000000004006ec <main>:
  4006ec:	a9bd7bfd 	stp	x29, x30, [sp, #-48]!
  4006f0:	910003fd 	mov	x29, sp
  4006f4:	b9001fe0 	str	w0, [sp, #28]
  4006f8:	f9000be1 	str	x1, [sp, #16]
  4006fc:	b9401fe0 	ldr	w0, [sp, #28]
  400700:	7100081f 	cmp	w0, #0x2
  400704:	1a9fd7e0 	cset	w0, gt
  400708:	12001c00 	and	w0, w0, #0xff
  40070c:	b9002fe0 	str	w0, [sp, #44]
  400710:	52800084 	mov	w4, #0x4                   	// #4  在调用get_xx函数之前准备参数,并依次存储在w0-w4这5个寄存器中
  400714:	52800063 	mov	w3, #0x3                   	// #3
  400718:	52800042 	mov	w2, #0x2                   	// #2
  40071c:	52800021 	mov	w1, #0x1                   	// #1
  400720:	b9402fe0 	ldr	w0, [sp, #44]
  400724:	97ffffd8 	bl	400684 <get_xx>
  400728:	f90013e0 	str	x0, [sp, #32]
  40072c:	f94013e0 	ldr	x0, [sp, #32]
  400730:	aa0003e1 	mov	x1, x0
  400734:	90000000 	adrp	x0, 400000 <_init-0x4f0>
  400738:	911fe000 	add	x0, x0, #0x7f8
  40073c:	97ffff8d 	bl	400570 <printf@plt>
  400740:	52800000 	mov	w0, #0x0                   	// #0
  400744:	a8c37bfd 	ldp	x29, x30, [sp], #48
  400748:	d65f03c0 	ret
  40074c:	00000000 	.inst	0x00000000 ; undefined

复杂函数参数(结构体)

复制代码
#include <syslog.h>
#include <stdio.h>
#include <unistd.h>
struct st
{
	int a;
	int b;
	int c;
	int d;
	int e;
	int f;
	int g;
	int h;
	int i;
	int j;
	int k;
};
static int is_ok(int a,struct st s)
{
	return s.a+s.k+a;
}


int main()
{
	struct st s;
	int x = 99;
	s.a = 1;
	s.b = 2;
	s.c = 3;
	s.d = 4;
	s.e = 5;
	is_ok(x,s);
	return 0;
}

此时,参数将不使用寄存器来传递,因为也没有足够多的寄存器。采用的是栈空间。比如在main函数中,SP会开辟272字节的空间来存储结构体。用寄存器来保存:先看main函数:

is_ok函数:

参考指令:

03_ARMv8指令集介绍加载与存储指令 - Carlos·Wei - 博客园

ARM的六大类指令集---LDR、LDRB、LDRH、STR、STRB、STRH - blogernice - 博客园

https://blog.csdn.net/wmzjzwlzs/article/details/124513127

相关推荐
猫猫的小茶馆1 天前
【Linux 驱动开发】一. 搭建开发环境
linux·汇编·arm开发·驱动开发·stm32·嵌入式硬件·mcu
猫猫的小茶馆1 天前
【Linux 驱动开发】二. linux内核模块
linux·汇编·arm开发·驱动开发·stm32·嵌入式硬件·架构
切糕师学AI2 天前
ARM 中的 SVC 监管调用(Supervisor Call)
linux·c语言·汇编·arm开发
ベadvance courageouslyミ2 天前
硬件基础中断
汇编·硬件·中断
你爱写程序吗(新H)2 天前
基于单片机的洗衣机控制系统设计 单片机洗衣机控制(设计+文档)
c语言·汇编·单片机·嵌入式硬件·matlab
VekiSon3 天前
ARM架构——用汇编语言点亮 LED
汇编·arm开发·嵌入式硬件
JJCar3 天前
汇编文字池(literal pool)
汇编·文字池·literal pool
乾复道4 天前
巧用终端,每天节省2小时
汇编·经验分享·vim
2501_927773075 天前
嵌入式——汇编语言1
汇编
山峰哥5 天前
数据库工程核心:SQL调优让查询效率飙升的实战密码
网络·汇编·数据库·sql·编辑器