首页 > 系统相关 >Linux内核的栈回溯dump_stack原理

Linux内核的栈回溯dump_stack原理

时间:2024-09-03 23:26:12浏览次数:10  
标签:fp x30 dump frame sp x29 Linux include stack


浅析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).
无论中间如何使用,最后会弹出这两个寄存器,还原最开始的值。

Linux内核的栈回溯dump_stack原理_#include

测试:

[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

相关文章

  • Linux C++ 开发7 - GDB常用命令汇总(你想了解的都在这)
    1.运行命令2.设置断点3.查看源码4.打印表达式5.查看运行信息5.1.设置和查看运行参数的Demo6.分割窗口7.参考文档上一篇《LinuxC++开发6-GDB调试》中我们讲解了GDB的调试流程和常用的调试方法。GDB的调试指令众多,我们这里针对常用的指令做一个汇总(按功能......
  • Linux C++ 开发7 - GDB常用命令汇总(你想了解的都在这)
    1.运行命令2.设置断点3.查看源码4.打印表达式5.查看运行信息5.1.设置和查看运行参数的Demo6.分割窗口7.参考文档上一篇《LinuxC++开发6-GDB调试》中我们讲解了GDB的调试流程和常用的调试方法。GDB的调试指令众多,我们这里针对常用的指令做一个汇总(按功能分......
  • 使用LXR搭建Linux Kernel源码索引服务器
    0.测试环境Ubuntu13.10(64位,Kernel为自己编译的3.13.6)1.工具a.Perl在我的Ubuntu里已安装了Perl,版本信息如下:Thisisperl5,version14,subversion2(v5.14.2)builtforx86_64-linux-gnu-thread-multib.ctags使用sudoapt-getinstallctags进行安装,我现在安装好后......
  • Linux之WOL网络唤醒
    Linux之WOL网络唤醒WOL简介WOL(Wake-on-LAN)技术是一种计算机局域网唤醒技术,其主要功能是使处于关机或休眠状态的计算机能够通过局域网(通常是以太网)被远程唤醒并恢复到运行状态。具体来说,当一台计算机进入休眠或关机状态时,可以通过局域网中的另一台设备发送特定的网络广播包(magic......
  • Linux之手动创建WIFI热点
    Linux之手动创建WIFI热点背景之前介绍了Linux下使用无线网卡作为STA手动连接WIFI:Linux手动连接配置wifi今天介绍下Linux下怎么手动建立AP热点。主要分为2大步骤:hostapd建立AP热点DHCP服务分配IP前提:首先要无线网卡是否支持AP模式使用如下命令,查看网卡属性:BASHiwlist如果Sup......
  • Linux Wireless之80211(nl80211, cfg80211, mac80211)
    LinuxWireless之80211(nl80211,cfg80211,mac80211)前言在Linux无线子系统中,cfg80211、mac80211和nl80211是三个关键的组件,它们共同工作以实现对802.11设备的配置和管理。cfg80211负责内核空间的通用API,mac80211负责具体的MAC层实现,而nl80211则作为用户空间与内核空间之间的桥......
  • Linux中网络命名空间基本操作
    Linux中网络命名空间基本操作前言网络命名空间(NetworkNamespace,简写:netns)是Linux内核提供的一种实现网络资源隔离的方法,允许不同的网络命名空间拥有独立的网络协议栈及网络配置,包括IP地址、路由表、网络设备、iptables规则等。docker便是基于netns实现的网络隔离。大多数现代L......
  • Linux手动连接配置wifi
    Linux手动连接配置wifi背景以前在桌面端或是嵌入式手动连接过wifi,但没有深入也没有详细研究,今天系统地记录下。wpa_supplicant连接WIFIwpa_supplicant介绍及背景官网:https://w1.fi/wpa_supplicant/.以下来自man手册,介绍了它的作用、产生背景及由来,保持原汁原味。wpa_supplicant......
  • 云计算概述与Linux系统安装
    typora-copy-images-to:./media云计算概述与Linux系统安装授课原则:​ 做个三好学生(吃好玩好学好)授课流程:​ 1.复习(重点)2.知识点3.小结/总结4.讲/练;5.日总结/周总结(思维导图)学习方法:​ 1.笔记 2.重点多练,多掌握几种方法3.总结4.思维导图总结......
  • MySQL之mysqldump的使用详解
    一、mysqldump简介mysqldump 是 MySQL 自带的逻辑备份工具。它的备份原理是通过协议连接到 MySQL 数据库,将需要备份的数据查询出来,将查询出的数据转换成对应的insert 语句,当我们需要还原这些数据时,只要执行这些 insert 语句,即可将对应的数据还原。二、备份命令2.1命令格......