浅析ARMv8体系结构:Aarch64过程调用标准_aarch64-64-little
(重磅原创)冬之焱: 谈谈Linux内核的栈回溯与妙用-腾讯云开发者社区-腾讯云 (tencent.com)
ARM 架构 dump_stack 实现分析(3.0 printk %pS选项实现)
测试程序:
#include <stdio.h>
int A(int a)
{
}
int B()
{
int a=5;
A(a);
}
int C()
{
B();
}
int main()
{
C();
return 0;
}
objdump -d a.cout:
ARM64中x29寄存器别名FP,栈开始的地址,高地址,X30别名LR寄存器,bl指令自动保存,ret指令自动弹出到pc。
00000000004005e4 <A>:
4005e4: d10043ff sub sp, sp, #0x10
4005e8: b9000fe0 str w0, [sp, #12]
4005ec: d503201f nop
4005f0: 910043ff add sp, sp, #0x10
4005f4: d65f03c0 ret
00000000004005f8 <B>:
4005f8: a9be7bfd stp x29, x30, [sp, #-32]! //将x29,x30保存在sp-16的位置,末尾"!"符号,表示sp自己=sp-32,因此改变sp的值
4005fc: 910003fd mov x29, sp //将sp的值保存在x29寄存器。这个x29将作为被调用函数A的FP(栈帧的起始地址)
400600: 528000a0 mov w0, #0x5 // #5
400604: b9001fe0 str w0, [sp, #28] //内部变量使用SP等寄存器,但不改变SP的值。
400608: b9401fe0 ldr w0, [sp, #28]
40060c: 97fffff6 bl 4005e4 <A> //将PC+4,保存到LR(x30)。跳转到A
400610: d503201f nop
400614: a8c27bfd ldp x29, x30, [sp], #32 //从sp中取出x29,x30, 然后sp=sp+32 。此时对比第一句sp的值恢复一开始的内容。
400618: d65f03c0 ret
000000000040061c <C>:
40061c: a9bf7bfd stp x29, x30, [sp, #-16]! //将x29,x30保存在sp-16的位置,末尾"!"符号,表示sp自己=sp-16,因此改变sp的值
400620: 910003fd mov x29, sp //将sp的值保存在x29寄存器。这个x29将作为被调用函数B的FP(栈帧的起始地址)
400624: 97fffff5 bl 4005f8 <B> //将PC+4,保存到LR(x30)。跳转到B。
400628: d503201f nop
40062c: a8c17bfd ldp x29, x30, [sp], #16 //从sp中取出x29,x30, 然后sp=sp+16 。此时对比第一句sp的值恢复一开始的内容。
400630: d65f03c0 ret
0000000000400634 <main>:
400634: a9bf7bfd stp x29, x30, [sp, #-16]!
400638: 910003fd mov x29, sp
40063c: 97fffff8 bl 40061c <C>
400640: 52800000 mov w0, #0x0 // #0
400644: a8c17bfd ldp x29, x30, [sp], #16
400648: d65f03c0 ret
40064c: 00000000 .inst 0x00000000 ; undefined
调用关系:
C->B->A
C的栈在地址高位。
假如:当前在B函数。B函数一开始在SP-32处前面保存的X29和X30,x29代表调用者A的栈顶,即A函数一开始SP-16的位置。x30代表A函数的nop位置。在进入新函数的时候,x29寄存器(FP)和SP相同(即上一次函数栈末尾,新函数栈起始),都指向栈开始的地址。然后SP减一段空间保存X30(LR)和X29(FP).
无论中间如何使用,最后会弹出这两个寄存器,还原最开始的值。
测试:
[root@localhost test]# cat /proc/kallsyms |grep unwind
ffff80000800e310 T unwind_frame
用printk %pS格式打印某地址+偏移:0xffff80000800e310+4
#include <linux/module.h>
#include <linux/gfp.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/sched/task.h>
#include <linux/arm-smccc.h>
#include <linux/cpumask.h>
static int __init test_init(void)
{
printk("%s %pS\n", "test", (void *)0xffff80000800e314); //
return 0;
}
static void __exit test_exit(void)
{
printk(KERN_INFO "test_exit\n");
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
输出:
[ 232.383519] test unwind_frame+0x4/0x168
dump_backtrace函数解析:
void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk,
const char *loglvl)
{
if (tsk == current) { //如果是对当前运行的函数
start_backtrace(&frame, //设置初始frame的内容。sp和pc
(unsigned long)__builtin_frame_address(0), //gcc内建函数,获取当前函数的栈FP(x29),等于sp:mov x29, sp ,保存到frame->fp
(unsigned long)dump_backtrace); //以dump_backtrace作为追溯起始函数。保存到frame->pc
} else {
/*
* task blocked in __switch_to
*/
start_backtrace(&frame,
thread_saved_fp(tsk),
thread_saved_pc(tsk));
}
printk("%sCall trace:\n", loglvl);
do {
/* skip until specified stack frame */
if (!skip) {
dump_backtrace_entry(frame.pc, loglvl); //走这里打印
} else if (frame.fp == regs->regs[29]) {
skip = 0;
/*
* Mostly, this is the case where this function is
* called in panic/abort. As exception handler's
* stack frame does not contain the corresponding pc
* at which an exception has taken place, use regs->pc
* instead.
*/
dump_backtrace_entry(regs->pc, loglvl);
}
} while (!unwind_frame(tsk, &frame)); //解析栈中的最后16字节中的x29和x30寄存器。保存到frame
}
unwind_frame函数解析:
int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame)
{
unsigned long fp = frame->fp; //取出fp的值,这个是地址。
frame->fp = READ_ONCE_NOCHECK(*(unsigned long *)(fp)); //读取fp。其中保存了上级函数的fp的地址。读取出来,下次循环将再读取。。。
frame->pc = READ_ONCE_NOCHECK(*(unsigned long *)(fp + 8)); //偏移+8地址,其中保存了上级函数的地址x30(lr)
frame->prev_fp = fp;
frame->prev_type = info.type;
}
标签:fp,x30,dump,frame,sp,x29,Linux,include,stack
From: https://blog.51cto.com/u_13451572/11911281