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

Linux内核的栈回溯与妙用

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

1 媒介

提及linux内核的栈回溯成果,我想这对每个Linux内核或驱动开拓职员来说,太常见了。如下演示的是linux内核瓦解的一个栈回溯打印,有了这个瓦解打印我们能很快定位到在内核哪个函数瓦解,或许在函数什么位置,大大简化了题目排查进程。

网上或多或少都能找到栈回溯的一些文章,可是讲的都并不完备,没有将内核栈回溯的成果用于现实的内核、应用措施调试,这是本篇文章的焦点:尽也许引导读者将栈回溯的成果用于现实项目调试,栈回溯的成果很强盛。

Linux内核的栈回溯与妙用

本文具体讲授了基于mips、arm架构linux内核栈回溯道理,通过不少例子,尽也许全面给读者展示各类栈回溯的道理,祈望读者领略透彻栈回溯。在这个基本上,讲授笔者近几年项目开拓进程中行使linux内核栈回溯成果的几处重点应用。

1 当内核某处陷入死轮回,偶然运行sysrq的内核线程栈回溯成果可以排查,但并不合用所用环境,笔者现实项目碰着过。最后是在体系按时钟间断函数,对死轮回线程栈回溯20多级终于找到死轮回的函数。

2 当应用措施段错误,内核捕获到瓦解,对瓦解的应用空间历程/线程栈回溯,像内核栈回溯一样,打印应用段错误历程/线程的层层函数挪用相关。固然运用core文件说明可能gdb也很轻盈排查应用瓦解题目,可是对付不轻易复现、测试部偶先的、客户现场偶先的,这二者就很难施展浸染。

尚有就是假如瓦解产生在C库中,CPU的pc和lr(arm架构)寄存器指向的函数指令在C库的用户空间,很难找到应用的代码那边挪用了C库的函数。arm架构网上能找到应用层栈回溯的例子,可是编译较贫困,代码并不轻易领略,何况mips能在应用层实现吗?照旧在内核实现应用措施栈回溯较量利便。

3 应用措施产生double free,运用内核的栈回溯成果,找到应用代码那边产生了double free。double free是C库层发明并截获该变乱,然后向当前历程/线程发送SIGABRT历程终止信号,后续就是内核逼迫整理该历程/线程。double free比应用措施段错误更贫困,后者内核还会打印堕落历程/线程名字、pid、pc和lr寄存器值,double free这些打印全没有。

笔者做过的一个项目,宣布前,碰着一例double free瓦解题目,极难复现,当初要是把double free内查对出题目历程/线程栈回溯的成果做进内核,就能找到出题目的应用函数了。

4 当应用措施呈现锁死题目,对应用全部线程栈回溯,说明每个线程的函数执行流程,对查找锁死题目有辅佐。

以上几例应用,在笔者所做的项目中,内核已经合入相干代码,成果获得验证。

2 栈回溯的道理表明

2.1 基于fp栈帧寄存器情势的栈回溯

笔者最初进修栈回溯,起首看到的资料就是arm架构基于fp寄存器的栈回溯,这种资料网上较量多,这里凭证本身领略再描写一遍。

这种情势的栈回溯相对来说并不伟大,也轻易领略,遵循APCS(ARM Procedure Call Standard)类型, APCS类型了arm寄存器的行使、函数挪用进程出栈和入栈的约定。如下图所示,是一个传统的arm架构下函数栈数据漫衍,函数栈由fp和sp寄存器别离指向栈底和栈顶(这里举的例子函数无形参,无局部变量,利便领略)。

通过fp寄存器就可以找到存储在栈中lr寄存器数据,这个数据就是函数返回地点。同时也可以找到生涯在函数栈中的上一级函数fp寄存器数据,这个数据指向了上一级函数的栈底,云云就可以凭证同样的要领找出上一级函数栈中存储的lr和fp数据,就知道哪个函数挪用了上一级函数以及这个函数的栈底地点。

这样就组成了一个栈回溯进程,整个流程以fp为焦点,依次找出每个函数栈中存储的lr和fp数据,计较出函数返回地点和上一级函数栈底地点,从而找出每一级函数挪用相关。

为了使读者领略更充实,举一个简朴的例子。以C函数挪用了B函数为例,两个函数无形参,无局部变量,此时的入栈环境最简朴。两个函数以伪代码的情势列出,演示入栈进程,寄存器的入栈及赋值,与现实的汇编代码有毛病。

假设C函数的栈底地点是0x7fff001c,C函数的前5条入栈指令执行后,pc等寄存器的值生涯到C函数栈中,此时fp寄存器的值是C函数栈底地点0x7fff001c。

然后C函数跳转到B函数,B函数前5条指令执行后,pc、lr、fp寄存器的值依次生涯到B函数栈中:B函数栈的第二片内存生涯的就是lr值,即B函数的返回地点;第四片内存生涯的是fp值,就是C函数栈底地点0x7fff001c(在开始执行B函数指令前,fp寄存器的值是C函数的栈底地点,B函数的第4条指令又是令fp寄存器入栈);B函数第五条指令执行后,fp寄存器已经更新,其数据是B函数栈的栈底地点0x7fff000c。

当B函数产生瓦解,按照fp寄存器找到B函数栈底地点,从B函数栈第二片内存取出的数据就是lr,即B函数返回地点,第4片内存取出的数据就是fp,即C函数栈底地点。有了C函数栈底地点,就能凭证上述要领找出C函数栈中生涯的的lr和fp,实现栈回溯…..

2.2 unwind 情势的栈回溯

在arm架构下,不少32位体系用的是unwind情势的栈回溯,这种栈回溯要伟大许多。起首必要措施有一个非凡的段.ARM.unwind_idx 可能.ARM.unwind_tab,linux内核自己由多段构成,好比内核驱动初始化函数的init段。在System.map文件可以搜刮到__start_unwind_idx,这就是ARM.unwind_idx段的起始地点。

这个unwind段中存储着跟函数入栈相干的要害数据。当函数执行入栈指令后,在unwind段会生涯跟入栈指令逐一对应的编码数据,按照这些编码数据,就能计较出当前函数栈巨细和cpu的哪些寄存器入栈了,在栈中什么位置。

当栈回溯时,起首按照当前函数中的指令地点,就可以计较出函数unwind段的地点,然后从unwind段取出跟入栈有关的编码数据,按照这些编码数据就能计较出当前函数栈的巨细以及入栈时lr寄存器数据在栈中的存储地点。这样就可以找到lr寄存器数据,就是当前函数返回地点,也就是上一级函数的指令地点。

此时sp一样平常指向的函数栈顶,sp+函数栈巨细就是上一级函数的栈顶。这样就完成了一次栈回溯,而且知道了上一级函数的指令地点和栈顶地点,凭证同样的要领就能对上一级函数栈回溯,类推就能实现整个栈回溯流程。为了利便领略,下方举一个现实调试的示例。该示例中起首列出栈回溯进程每个函数unwind段的编码数据和栈数据。

(编辑:河北网)

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

热点阅读