Linux内核的栈回溯与妙用
笔者在研究过内核栈回溯成果后,不禁发问,为什么不能用同样的要领对应用措施的瓦解栈回溯呢?不管是内核空间,应用空间,措施的指令是一样的,无非是地点有差别,函数入栈出栈道理是一样的。栈回溯的进口,arm架构是获取瓦解线程/历程的pc、fp、lr寄存器值,mips架构是获取pc、ra、sp寄存器值,有了这些值就能凭证各自的回溯纪律,实现栈回溯。从理论上来说,完满是可以实现的。 4.1 .1 arm架构应用措施栈回溯的实现 当应用措施产生瓦解,与内核一样,体系自动将瓦解时全部的CPU寄存器存入struct pt_regs布局,一样平常瓦解进口函数是do_page_fault,又由于是应用措施瓦解,以是是__do_user_fault函数,这里直接说明__do_user_fault。 在该函数中,tsk就是瓦解的线程,struct pt_regs *regs就指向线程/历程瓦解时的CPU寄存器布局。regs->[29]就是fp寄存器,regs->[30]是lr寄存器, regs->pc的意义很直观。此刻有了瓦解应用线程/历程其时的fp、sp、lr寄存器,就能栈回溯了,完全模拟内核dump_backtrace的要领,请看笔者写在user_thread_ dump_backtrace函数中的演示代码。 与内核栈回溯道理同等,打印瓦解进程每个函数的指令地点,然后在应用措施的反汇编文件中查找,就能找到该指令处于的函数,假如不领略,请看文章前线讲授的内核栈回溯代码与道理。请留意,这不是笔者项目现适用的栈回溯代码,现实的窜改完美了许多,这只是演示道理的示例代码。 尚有一点就是,笔者在3.1.3节提到的,若是瓦解的函数中没有挪用其他函数,那上述栈回溯就会有题目,就不会打印第二级函数,办理要领讲的也有,办理的代码这里就不再列出了。 4.1 .2 mips架构应用措施栈回溯的实现 mips 架构不只内核栈回溯的代码比arm伟大,应用措施的栈回溯更伟大,尚有未知bug,即便这样,照旧讲授一下详细的办理思绪,最后讲一下存在的题目。 先简朴回首一下内核栈回溯的道理,起首按照瓦解函数的pc值,运用内核kallsyms模块,计较出该函数的指令首地点,然后从指令首地点开始说明,找出相同addiu sp,sp,-24和sw ra,20(sp)指令,前者可以找到该函数的栈巨细,栈指针sp加上这个数值,就知道上一级函数的栈顶地点(瓦解时sp指向瓦解函数的栈顶);后者知道函数返回地点在该函数栈中存储的地点,从该地点就能获取该函数的返回地点,就是上一级函数的指令地点,也就知道了上一级函数是哪个(同样行使内核kallsyms模块)。 知道了上一级函数的指令地点和栈顶地点,凭证同样要领,就能知道再上一级的函数……. 题目来了,内核有kallsyms模块记录了每个函数的首地点和函数名字,函数照旧次序排布。应用措施并没有kallsyms模块,即便知道了瓦解函数的pc值,也无法凭证同样的要领找到瓦解函数的指令首地点,真的没有要领?着实尚有一个最简朴的要领。先列出一段一个应用措施函数的汇编代码,如下所示,与内核态的有小的不同。 此刻若是从0X4006a4地点处取指,运行后瓦解了。瓦解产生时,能像arm架构一样获取瓦解前的CPU寄存器值,最重要就是pc、sp、ra值。 pc值就是0X4006a4,然后令一个unsigned long型指针指向该内存地点0X4006a4,每次减一,并取出该地点的指令数据说明,这样必定能说明到addiu sp,sp,-32 和sw ra,28(sp)指令,我想看到这里,读者应该可以清晰要领了。没错,就是以瓦解时pc值作为基地点,每次减1并从对应地点取出指令说明,直到说明出久违的addiu sp,sp,-32 和sw ra,28(sp)相同指令,再团结瓦解时的栈指针值sp,就能计较出该函数的返回地点和上一级函数的栈顶地点。后续的要领,就与内核栈回溯的进程同等了。下方列出演示的代码。 为了同等性,应用措施栈回溯的函数照旧回收名字user_thread_ dump_backtrace。 如上就是mips应用措施栈回溯的示例代码,只是一个演示,笔者现实行使的代码要伟大太多。读者行使时,要基于这个根基道理,多调试,才气应对各类环境,笔者前后调试几周才不变。因为这个要领并不是尺度的,现实行使时照旧会呈现误报函数征象,说明白产生误报的汇编代码及C代码,发明当函数代码伟大时,函数的汇编指令会变得很是伟大,会呈现相似指令等等,读者现实调试时就会发明。这个mips应用措施栈回溯的要领,可以应对大部门瓦解环境,可是有误报的也许,优化的空间很是大,这点请读者留意。 4.2 应用措施double free 内核栈回溯 double free是在C库层产生的,正常环境内核无能为力,可是笔者研究事后,发明照样可以实现对产生double free应用历程的栈回溯。 以arm架构为例,doublefree C库层的代码,概略道理是,当检测到double free(本人尝试时,一片malloc分派的内存free两次就会产生),就会执行kill体系挪用函数,向出题目的历程发送SIGABRT信号,既然是体系挪用,从用户空间进入内核空间时,就会将应用历程用户空间运行时的CPU寄存器pc、sp、lr等生涯到历程的内核栈中,发送信号内核肯定执行send_signal函数。 在该函数中,行使struct pt_regs *regs = task_pt_regs(current)要领就能从当前历程内核栈中获取进入内核空间前,用户空间运行指令的pc、sp、fp等CPU寄存器值,有了这些值,就能凭证用户空间历程瓦解栈回溯要领一样,对double free的历程栈回溯了。好比,A函数double free,A函数->C库函数1-> C库函数2->C库函数3(检测到double free并发送SIGABRT信号,执行体系挪用进入内核空间发送信号)。回溯的功效是:C库函数3 ß C库函数2 ß C库函数1ß A函数。 源码不再列出,信托读者领略的话是可以本身开拓的。个中task_pt_regs函数的行使,必要读者对历程内核栈有必然的相识。 笔者有个领略,当获取某个历程运行指令某一时刻点的CPU寄存器pc、lr、fp的值,就能对该历程举办栈回溯。 4.3 内核发存亡轮回sysrq无效时栈回溯的应用 (编辑:湖南网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |