加入收藏 | 设为首页 | 会员中心 | 我要投稿 河北网 (https://www.hebeiwang.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 业界 > 正文

Linux内核的栈回溯与妙用

发布时间:2018-11-14 12:40:53 所属栏目:业界 来源:今日头条
导读:1 媒介 提及linux内核的栈回溯成果,我想这对每个Linux内核或驱动开拓职员来说,太常见了。如下演示的是linux内核瓦解的一个栈回溯打印,有了这个瓦解打印我们能很快定位到在内核哪个函数瓦解,或许在函数什么位置,大大简化了题目排查进程。 网上或多或少

指令接着执行,由B函数跳转到A函数,A函数前三条指令与B函数执行环境相同,重点就三处,A函数栈的第一片内存存储A函数的返回地点,A函数栈的第二片内存存储B函数栈的第二片内存地点,当A函数执行到指令5后,fp寄存器生涯的是A函数栈的第二片内存地点,表示图中所有标出。当A函数执行指令6瓦解,怎么栈回溯?

A函数瓦解时,凭证上文的说明,fp寄存器生涯的数据是A函数栈的第二片内存首地点0X1000。0X1000地点中存储的数据就是B函数的栈地点0x1008(就是B函数的栈的第二片内存),0x1000+4=0X1004地点就是A函数栈的第一片内存,存储的数据是A函数的返回地点0X0030,这个指令地点就是B函数的指令6地点,这样就知道了时B函数挪用了A函数。

由于此时已经知道了B函数栈的第二片内存地点,该地点的数据就是C函数栈的第二片内存地点,B函数栈的第一片内存地点中的数据是B函数的返回地点0X0048(C函数的指令6内存地点)。这样就倒着推出函数挪用相关:A函数ßB函数ßC函数。

笔者以为,这种环境栈回溯的焦点是:每个函数栈的第二片内存地点存储的数据是上一级函数栈的第二片内存地点,每个函数栈的第一片内存地点存储的数据是函数返回地点。只要获取到瓦解函数栈的第二片内存地点(此时就是fp寄存器的数据),就能轮回计较出每一级挪用的函数。

3.1.1 内核源码说明

假如读者对上一节的演示领略的话,领略下方的源码就较量轻易。

  1. arch/arm64/kerneltraps.c 

内核瓦解时,发生非常,内核的非常处理赏罚措施自动将瓦解时的CPU寄存器存入struct pt_regs布局体,并传入该函数,相干代码不再列出。这样栈回溯的要害环节就是赤色标注的代码,先对frame.fp,frame.sp,frame.pc赋值。

下方进入while轮回,先执行unwind_frame(&frame) 找出瓦解进程的每个函数中的汇编指令地点,存入frame.pc(第一次while轮回是直接where = frame.pc赋值,这就是当前瓦解函数的瓦解指令地点),下次轮回存入where变量,再传入dump_backtrace_entry函数,在该函数中打印诸如[] chrdev_open+0x12/0x4B1 的字符串。

这个打印的着实是在print_ip_sym函数中做的,将ip凭证%pS情势打印,就能打印出该函数指令地址的函数,以及相对函数首指令的偏移。栈回溯的重点是在unwind_frame函数。

在正式贴出代码前,先先容一下栈回溯进程的三个焦点CPU寄存器:pc、lr、fp。pc指向运行的汇编指令地点;sp指向函数栈;fp是栈帧指针,差异架构环境差异,但笔者以为它是栈回溯进程中,接洽两个有挪用相关函数的纽带,下面的说明就能浮现出来。

  1. arch/arm64/kernel/stacktrace.c 

起首声名一下,这是arm64位体系,一个long型数据8个字节巨细。为了论述利便,假设内核代码的瓦解函数流程照旧 C函数->B函数->A函数,在A函数瓦解,最后在unwind_frame函数中栈回溯。

接着针对代码先容栈回溯的道理。第一次执行unwind_frame函数时,第二行,frame->fp生涯的就是瓦解时CPU的fp寄存器的值,就是A函数栈第二片内存地点,frame->sp = fp + 0x10赋值后,frame->sp就是A函数的栈底地点;frame->fp= *(unsigned long *)(fp)获取的是存储在A函数栈第二片内存中的数据,就是挪用A函数的B函数的栈的第二片内存地点;frame->pc = *(unsigned long *)(fp + 8)是获取A函数栈的第一片内存中的数据,就是A函数的返回地点(就是B函数中指令地点),这样就知道了是B函数挪用了A函数;颠末一次unwind_frame函数挪用,就知道了A函数的返回地点和B函数的栈的第二片内存地点,有了B函数栈的第二片内存地点,就能凭证上述进程推出B函数的返回地点(C函数的指令地点)和C函数栈的第二片内存地点,这样就知道了时C函数挪用了B函数,云云轮回,不管有几多级函数挪用,都能凭证这个纪律找出函数挪用相关。虽然这里的相关是是AßBßC。

为什么栈回溯的道理是这样?起首这个道理笔者都是现实行证过的,仔细的读者应该会发明,这个栈回溯的流程跟前文第2节演示的简朴栈回溯道理一样。是的,第2节就是笔者凭证本身对arm 64位体系栈回溯的领略,用简朴的情势表达出来,还附了演示图,这里不相识的读者可以回到第2节说明一下。

3.1.2 arm架构从汇编代码角度表明栈回溯的道理

为了使读者领略的更充实,下文列出一段应用层C说话代码和反汇编后的代码

C代码

汇编代码

说明test_2函数的汇编代码,第一条指令stpx29, x30,[sp,#-16],x29就是fp寄存器,x30就是lr寄存器,指令执行进程:将x30(lr)、x29(fp)寄存器的值跟着栈指针sp向下偏移依次入栈,栈指针sp共偏移两次8+8=16个字节(arm 64位体系栈指针sp减一偏移8个字节,而且栈是向下增添,以是指令是-16)。

mov x29,sp 指令就是将栈指针赋予fp寄存器,此时sp就指向test_2函数栈的第二片内存,由于sp偏移了两次,fp寄存器的值就是test_2函数栈的第二片内存地点。

去除不相干的指令,直接从test_2函数跳转到test_1函数开始说明,看test_1函数的第一条指令stp x29, x30,[sp,#-16],起首栈指针sp减一,将x30(lr)寄存器的数据存入test_1函数栈的第一片内存,这就是test_1函数的返回地点,接着栈指针sp减一,将x29(fp)寄存器值入栈,存入test_1函数的第二片内存,此时fp寄存器的置魅正是test_2函数栈的第二片内存地点,本质就是将test_2函数栈的第二片内存地点存入test_1函数栈的第二片内存中。接着执行mov x29,sp 指令,就是将栈指针sp赋予fp寄存器,此时sp指向test_1函数栈的第二片内存…..

这样就与上一小结的说明同等了, 这里就对arm栈回溯的一样平常进程,做个较为体系的总结:当C函数跳转的B函数时,先将B函数的返回地点存入B函数栈的第一片内存,然后将C函数栈的第二片内存地点存入B函数栈的第二片内存,接着将B函数栈的第二片内存地点存入fp寄存器,B函数跳转到A函数流程也是这样。

(编辑:河北网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读