东莞家具网站建设,营销策略ppt模板,学校网站建设调研报告,上海建站 seo文章目录 前言一、栈帧简介二、demo演示 前言
请参考#xff1a;ARM64架构栈帧以及帧指针FP
一、栈帧简介
假设下列函数调用#xff1a;
funb()
{func()
}funa()
{funb()
}main()
{funa()
}main函数#xff0c;funa函数#xff0c;funb函数都不是叶子函数#xff0c;其… 文章目录 前言一、栈帧简介二、demo演示 前言
请参考ARM64架构栈帧以及帧指针FP
一、栈帧简介
假设下列函数调用
funb()
{func()
}funa()
{funb()
}main()
{funa()
}main函数funa函数funb函数都不是叶子函数其栈布局如下所示 LR 和FP寄存器保存在每个函数栈帧的栈顶 FP SP 0 LR SP 8 根据这两个寄存器就可以反推出所有函数的调用栈。
FP栈帧指针X29指向保存在栈上的上一个栈帧的帧指针。在它之后存储了保存的LRX30。链中的最后一个帧指针应设置为0。
知道FP寄存器就能得到每个函数的栈帧基地址。而知道每个函数的栈帧基地址的条件下可通过当前函数栈帧保存的LR获得当前函数的Entry地址和函数名。 通过FP还可以知道上一级的FP栈帧基地址。
在ARM64体系结构中函数调用栈以单链表形式组织其中每个栈帧都包含两个地址用于构建这个链表。这种链表通常被称为调用链或链式栈。
在链式栈中每个栈帧都有两个64位宽的地址 1低地址栈顶存放了指向上一个栈帧的基地址通常使用FPFrame Pointer寄存器来保存。类似于链表中的prev指针它指向上一个栈帧的基地址以便在函数返回时回到调用者的上下文。
2高地址存放了LRLink Register寄存器的值它保存了当前函数的返回地址。LR寄存器中的值指向了调用当前函数的下一条指令的地址。当函数执行完毕时该地址将被用于恢复程序控制流并返回到调用者的位置。
通过这种方式每个栈帧都可以通过链表中的prev指针链接在一起形成一个完整的函数调用链。当函数返回时可以使用prev指针获取上一个栈帧的基地址并利用LR寄存器中的返回地址将控制流传递给调用者。
ARM64栈回溯 在AAPC64中栈指针SP指向当前栈帧的顶部其中包含了上一级函数的LR和FP寄存器现场。通过查看SP所指向的地址可以找到保存的上一级函数的LR和FP寄存器值。
对于LR寄存器根据(LR-4)可以找到上一级函数所在的地址减去4是因为ARM64指令集中的跳转指令例如BL会将要跳转到的地址加上4。因此在栈上保存的LR值实际上是要跳转到的下一条指令的地址而不是当前指令的地址。所以为了找到上一级函数所在的地址需要减去4。
上一级函数的FP寄存器实际上等于上一级函数使用的栈顶地址。通过保存上一级FP寄存器现场的位置可以在栈上找到上一级函数的栈帧。同样该栈帧中也会保存更上一级函数的LR和FP寄存器现场以此类推形成函数调用链。
通过链式保存的方式可以回溯整个函数的调用流程从当前函数一直追溯到最外层的调用者。这种方式使得在函数返回时可以按照相反的顺序恢复各个函数的现场并正确返回到调用者的位置。
二、demo演示
C语言示例
int fund(int g, int h)
{return g h;
}int func(int e, int f)
{int ret e f;ret fund(e, ret);return ret;
}int funb(int c, int d)
{int ret c d;ret func(c, ret);return ret;}int funa(int a, int b)
{int ret a b ;ret funb(a, ret);return ret;
}int main(void)
{int i 1, j 2;int ret funa(i,j);return 0;
}(gdb) b main
(gdb) b funa
(gdb) b funb
(gdb) b func
(gdb) r(1) main
(gdb) disassemble
Dump of assembler code for function main:......0x0000005555555810 32: bl 0x55555557b4 funa0x0000005555555814 36: str w0, [sp, #28]......x29 0x7ffffff400
x30 0x7ff7e5c1102 funa
(gdb) c
(gdb) disassemble
Dump of assembler code for function funa:......0x00000055555557dc 40: bl 0x5555555778 funb0x00000055555557e0 44: str w0, [sp, #44]......(gdb) info registers
x29 0x7ffffff3d0
x30 0x5555555814可以看到x30寄存器的值就是main函数 bl funa 下一条指令的地址。
根据x29寄存器得到funa栈帧基地址
0x7ffffff3d0读取该地址的值(x29寄存器FP存放了指向上一个栈帧的基地址)
(gdb) x/1xg 0x7ffffff3d0
0x7ffffff3d0: 0x0000007ffffff400那么可以得到main函数的栈帧基地址0x0000007ffffff400 这个值就等于执行main函数时x29寄存器的值。
将main函数的栈帧基地址8然后读取获取main的返回地址 这里 8 的原因LR FP 8
0x0000007ffffff400 8 0x0000007ffffff408(gdb) x/1xg 0x0000007ffffff408
0x7ffffff408: 0x0000007ff7e5c110main的返回地址0x0000007ff7e5c110
将main的返回地址 - 4 就可以获取 BL main这条函数跳转指令的地址 这里 - 4 的原因执行BL指令时将下一条指令的地址即返回地址写入X30寄存器中这里我们已经获取到了返回地址那么 -4 就获取到了 BL 指令的地址。
0x0000007ff7e5c110 - 4 0x0000007ff7e5c10c那么其上一条调用main的指令地址就是0x0000007ff7e5c10c
(gdb) x/i 0x0000007ff7e5c10c0x7ff7e5c10c __libc_start_main228: blr x3
(gdb) x/2i 0x0000007ff7e5c10c0x7ff7e5c10c __libc_start_main228: blr x30x7ff7e5c110 __libc_start_main232: bl 0x7ff7e71a40 exit可以看到是__libc_start_main函数调用 main 函数。
3 funb
(gdb) disassemble
Dump of assembler code for function funb:......0x00000055555557a0 40: bl 0x555555573c func0x00000055555557a4 44: str w0, [sp, #44]......(gdb) info registers
x29 0x7ffffff3a0
x30 0x55555557e0可以看到x30寄存器的值就是 funa bl funb下一条指令的地址。
根据x29寄存器得到funb栈帧基地址
0x7ffffff3a0读取该地址的值(x29寄存器FP存放了指向上一个栈帧funa的基地址)
(gdb) x/1xg 0x7ffffff3a0
0x7ffffff3a0: 0x0000007ffffff3d0这个值就等于执行funa函数时x29寄存器的值。
将funa函数的栈帧基地址8然后读取获取funa的返回地址
0x0000007ffffff3d0 8 0x0000007ffffff3d8(gdb) x/1xg 0x0000007ffffff3d8
0x7ffffff3d8: 0x0000005555555814funa的返回地址0x0000005555555814
将funa的返回地址 - 4 就可以获取 main BL funa这条函数跳转指令的地址
0x0000005555555814- 4 0x0000005555555810那么其上一条调用funa的指令地址就是0x0000005555555810
(gdb) x/i 0x00000055555558100x5555555810 main32: bl 0x55555557b4 funa
(gdb) x/2i 0x00000055555558100x5555555810 main32: bl 0x55555557b4 funa0x5555555814 main36: str w0, [sp, #28]可以看到是main函数调用 funa 函数。
4 func
(gdb) disassemble
Dump of assembler code for function func:......0x0000005555555764 40: bl 0x555555571c fund0x0000005555555768 44: str w0, [sp, #44]......(gdb) info registers
x29 0x7ffffff370
x30 0x55555557a4可以看到x30寄存器的值就是 funb bl func下一条指令的地址。
根据x29寄存器得到func栈帧基地址
0x7ffffff370读取该地址的值(x29寄存器FP存放了指向上一个栈帧funb的基地址)
(gdb) x/1xg 0x7ffffff370
0x7ffffff370: 0x0000007ffffff3a0这个值就等于执行funb函数时x29寄存器的值。
将funb函数的栈帧基地址8然后读取获取funb的返回地址
0x0000007ffffff3a0 8 0x0000007ffffff3a8(gdb) x/1xg 0x0000007ffffff3a8
0x7ffffff3a8: 0x00000055555557e0funb的返回地址0x00000055555557e0
将funb的返回地址 - 4 就可以获取 funaBL funb这条函数跳转指令的地址
0x00000055555557e0 - 4 0x00000055555557dc那么其上一条调用funb的指令地址就是0x00000055555557dc
(gdb) x/i 0x00000055555557dc0x55555557dc funa40: bl 0x5555555778 funb
(gdb) x/2i 0x00000055555557dc0x55555557dc funa40: bl 0x5555555778 funb0x55555557e0 funa44: str w0, [sp, #44]可以看到是funa函数调用 funb 函数。