Linux内核的栈回溯与妙用
当A函数中瓦解时,先从fp寄存器中获取A函数栈的第二片内存地点,从中取出B函数栈的第二片内存地点,再从A函数栈的第一片内存取出A函数的返回地点,也就是B函数中的指令地点,这样就推导出B函数挪用了A函数,同理推导出C函数挪用了B函数。 演示的代码很简答,可是这个说明是合用于伟大函数的,已经现实行证过。 3.1.3 arm 内核栈回溯的“bug” 这个不是我骇人听闻,是现实测出来的。好比如下代码: 这个函数挪用流程在内核瓦解了,内核栈回溯是不会打印上边的b函数,有arm 64体系的读者可以验证一下,我多次验证得出的结论是,假如瓦解的函数没有执行其他函数,就会打乱栈回溯法则,为什么呢?请转头看上一节的代码演示 汇编代码是 可以发明,test_a_函数前两条指令不是stpx29, x30,[sp,#-16]和mov x29,sp,这两条指令然则栈回溯的要害环节。怎么办理呢?细心说明的话,是可以办理的。 一样平常环境,函数瓦解,fp寄存器生涯的数据是当前函数栈的第二片内存地点,当前函数栈的第一片内存地点生涯的是函数返回地点,从该地点取出的数据与lr寄存器的数据应是同等的,由于lr寄存器生涯的也是函数返回地点,假如不沟通,声名该函数中没有执行stp x29, x30,[sp,#-16]指令,此时应行使lr寄存器的值作为函数返回地点,而且此时fp寄存器自己就是上一级函数栈的第二片内存地点,有了这个数据就能凭证前文的要领栈回溯了。办理要领就是这样,读者可以细心领会一下我的说明。 3.2 mips 栈回溯进程 前文说过,mips内核瓦解处理赏罚流程是
打印瓦解函数流程是在show_backtrace()函数。 3.2.1 mips 架构内核栈回溯道理说明
可以发明,与arm架构栈回溯流程根基同等。函数开头是对sp、ra、pc寄存器器赋值,sp和pc与arm架构同等,ra相等于arm架构的lr寄存器,没有arm架构的fp寄存器。print_ip_sym函数就是按照pc值打印形如[ 如下是mips架构内核驱动ko文件的 C代码和汇编代码。 C代码 汇编代码 这里声名一下,驱动ko反汇编出来的指令是从0地点开始的,为了论述利便,笔者加了0x80000000,现实的汇编代码不是这样的。 这里直接先容按照笔者的说明,总结mips架构内核栈回溯的道理,说明完后再团结源码验证。mips架构没有fp寄存器,假设在test_c函数中0X80000048地点处指令瓦解了,起首操作内核的kallsyms模块,按照瓦解时的指令地点找出该指令是哪个函数的指令,而且找出该指令地点相对函数指令首地点的偏移ofs,在本案例中ofs = 0X10(0X80000048 – 0X80000038 =0X10),这样就能算出test_c函数的指令首地点是 0X80000048 - 0X10 = 0X80000038。然后就从地点0X80000038开始,依次取出每条指令,找到addiu sp,sp,-24 和sw ra,20(sp),内核有尺度函数可以判定出这两条指令,下文可以看到。 addiu sp,sp,-24是test_c函数的第一条指令,栈指针向下偏移24个字节,笔者以为是为test_c函数分派栈巨细( 24个字节);sw ra,20(sp)指令将test_c函数返回地点存入sp +20 内存地点处,此时sp指向的是test_c函数的栈顶,sp+20就是test_c函数栈的第二片内存,该函数栈巨细24字节,一共24/4=6片内存。 按照sw ra,20(sp)指令知道test_c函数返回地点在test_c函数栈的存储位置,取出该地点的数据,就知道是test_a函数的指令地点,虽然就知道是test_a函数挪用了test_c函数。并按照addiu sp,sp,-24指令知道test_c函数栈总计24字节,由于test_c函数瓦解时,栈指针sp指向test_c函数栈顶,sp+24就是test_a函数的栈顶,由于test_a函数挪用了test_c函数,两个函数的栈必是紧挨着的。 凭证上述揣度,起首知道了test_a函数中的指令地点了,行使内核kallsyms成果就推算出test_a函数的指令首地点,同时也计较出test_a函数的栈顶,就能凭证上述纪律找出谁挪用了test_a函数,以及该函数的栈顶。依次就能找出全部函数挪用相关。 关于内核的kallsyms,笔者的领略是:执行过cat /proc/kallsyms呼吁的读者,应该相识过,该呼吁会打印内核全部的函数的首地点和函数名称,尚有内核编译后天生的System.map文件,记录内核函数、变量的名称与内存地点等等,kallsyms也是记录了这些内容,当执行kallsyms_lookup_size_offset(0X80000048, &size,&ofs)函数,就能按照0X80000048指令地点计较出处于test_c函数,并将相对付test_c函数指令首地点的偏移0X10存入ofs,test_c函数指令总字节数存入size。 笔者没有研究过kallsyms模块,可是可以领略到,内核的全部函数都是凭证分派的地点,次序排布。假如记录了每个函数的首地点和名称,当知道函数的任何一条指令地点,就能在个中搜刮比对,找到该指令处于按个函数,计较出函数首地点,该指令的偏移。 3.2.2 mips 架构内核栈回溯焦点源码说明 3.2.1具体报告了mips栈回溯的道理,接着讲授栈回溯的焦点函数unwind_stack_by_address。 上述源码已经在要害点做了具体注释,着实就是对3.2.1节栈回溯道理的完美,请读者本身说明,这里不再赘述。可是有一点请留意,就是蓝色注释,这是针对瓦解的函数没有执行其他函数的环境,此时该函数没有相同汇编指令sw ra,20(sp) 将函数返回地点生涯到栈中,计较要领就变了,要直接行使ra寄存器的值作为函数返回地点,计较上一级函数栈顶的要领照旧同等的,后续栈回溯的要领与前文沟通。 4 linux内核栈回溯的应用 文章最开头说过,笔者在现实项目开拓进程,已经总结出了3个内核栈回溯的应用: 1 应用措施瓦解,像内核栈回溯一样打印整个瓦解进程,应用函数的挪用相关 2 应用措施产生double free,像内核栈回溯一样打印double free进程,应用函数的挪用相关 3 内核陷入死轮回,sysrq的内核线程栈回溯成果无法施展浸染时,在体系按时钟间断函数中对卡死线程栈回溯,找出卡死位置 下文一一讲授。 4.1 应用措施瓦解栈回溯 (编辑:湖南网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |