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

相关推荐
一品人家3 天前
win32汇编环境,得到当前日期时间的例子
汇编
试试看1684 天前
自制操作系统第三天
汇编·系统架构
0xCC说逆向6 天前
Windows逆向工程入门之汇编位运算
c语言·汇编·windows·安全·逆向
矮油0_o6 天前
30天开发操作系统 第 20 天 -- API
c语言·汇编·操作系统
charlie1145141917 天前
从0开始的操作系统手搓教程 4:做好准备,跳到加载器(Loader)
汇编·学习·操作系统·调试·mbr·手搓教程·bochs
官子无敌刘小路9 天前
汇编简介&常用语法
汇编
二进制怪兽13 天前
[笔记] 汇编杂记(持续更新)
汇编
bae-唯一14 天前
关于32位和64位程序的传参方法及虚拟机调试工具总结
汇编·学习
E_han15 天前
7.list
开发语言·汇编·数据结构·c++·算法·list
二进制怪兽15 天前
[笔记] x86汇编语言:从实模式到保护模式之第一章 十六进制计数法
汇编