linux内核 cpu_die,Linux内核Crash分析

结合上面的知识,看下当内核打印堆栈信息时,都打印了上面信息。下面的打印信息是工作中遇到的一种情况,打印了内核的堆栈信息,PC指针在dev_get_by_flags中,不能访问的内核虚地址为45685516,内核中一般可访问的地址都是以0xCXXXXXXX开头的地址。

Unableto handle kernel paging request atvirtualaddress45685516

pgd=c65a4000

[45685516]*pgd=00000000

Internalerror:Oops:1[#1]

lastsysfs file:/sys/devices/form/tpm/cfg_l3/l3_rule_add

Moduleslinkedin:splic mmp(P)

CPU:0Tainted:P(2.6.32.11#42)

PCisat dev_get_by_flags+0xfc/0x140

LRisat dev_get_by_flags+0xe8/0x140

pc:[]lr:[]psr:20000013

sp:c07e9c28 ip:00000000fp:c07e9c64

r10:c6bcc560 r9:c646a220 r8:c66a0000

r7:c6a00000 r6:c0204e56 r5:30687461r4:45685516

r3:00000000r2:00000010r1:c0204e56 r0:ffffffff

Flags:nzCvIRQsonFIQsonModeSVC_32 ISA ARMSegmentkernel

Control:0005397fTable:065a4000DAC:00000017

Processswapper(pid:0,stack limit=0xc07e8270)

Stack:(0xc07e9c28to0xc07ea000)

9c20:c0204e56 c6a0000045685516c69ffff0 c69ffff0 c69ffff0

9c40:c6a0000030687461c66a0000 c6a0000000000007c64b210c c07e9d24 c07e9c68

9c60:c071f764 c06bed38 c66a0000 c66a0000 c6a00000 c6a00000 c66a0000 c6a00000

9c80:c07e9cfc c07e9c90 c03350d4 c0334b2c000000340000000600000100c64b2104

9ca0:0000c4fbc0243ece c66a0000 c0beed04 c033436c c646a220 c07e9cf400000000

9cc0:c66a000000000003c0bee8e8 c0beed04 c07e9d24 c07e9ce0 c06e4f5c00004c68

9ce0:00000000faa9fea9 faa9fea90000000000000000c6bcc560 c0335138 c646a220

9d00:c66a0000 c64b2104 c085ffbc c66a0000 c0bee8e800000000c07e9d54 c07e9d28

9d20:c071f9a0 c071ebc000000000c071ebb08000000000000007c67fb460 c646a220

9d40:c0bee8c800000608c07e9d94 c07e9d58 c002a100 c071f84c c0029bb880000000

9d60:c07e9d84 c0beee0c c0335138 c66a0000 c646a22000000000c4959800 c4959800

9d80:c67fb46000000000c07e9dc4 c07e9d98 c078f0f4 c0029bc800000000c0029bb8

9da0:80000000c07e9dbc c6b8d340 c66a052000000000c646a220 c07e9dec c07e9dc8

9dc0:c078f450 c078effc00000000c67fb460 c6b8d34000000000c67fb460 c64b20f2

9de0:c07e9e24 c07e9df0 c078fb60 c078f13000000000c078f12080000000c0029a94

9e00:00000806c6b8d340 c0bee8180000000100000000c4959800 c07e9e64 c07e9e28

9e20:c002a030 c078f804 c64b207000000000c64b2078 ffc45000 c64b20c2 c085c2dc

9e40:00000000c085c2c000000000c081739800086c2ec085c2c4 c07e9e9c c07e9e68

9e60:c06c2684 c0029bc8000000010000004000000000c085c2dc c085c2c000000001

9e80:0000012c00000040c085c2d0 c0bee818 c07e9ed4 c07e9ea0 c00284e0 c06c2608

9ea0:bf00da5c00086c300000000000000001c097e7d4 c07e800000000100c08162d8

9ec0:00000002c097e7a0 c07e9f14 c07e9ed8 c00283d0 c00284785625131100023c88

9ee0:c07e9f0c00000003c08187ac000000180000000001000000c07ebc7000023cbc

9f00:5625131100023c88c07e9f24 c07e9f18 c03391e8 c0028348 c07e9f3c c07e9f28

9f20:c0028070 c03391b0 ffffffff0000001fc07e9f94 c07e9f40 c002d4d0 c0028010

9f40:0000000000000001c07e9f8860000013c07e8000 c07ebc78 c0868784 c07ebc70

9f60:00023cbc5625131100023c88c07e9f94 c07e9f98 c07e9f88 c025c3e4 c025c3f4

9f80:60000013ffffffff c07e9fb4 c07e9f98 c025c578 c025c3cc00000000c0981204

9fa0:c0025ca0 c0d01140 c07e9fc4 c07e9fb8 c0032094 c025c528 c07e9ff4 c07e9fc8

9fc0:c0008918 c0032048 c00083880000000000000000c0025ca00000000000053975

9fe0:c0868834 c00260a400000000c07e9ff800008034c00087080000000000000000

Backtrace:

[](dev_get_by_flags+0x0/0x140)from[](arp_process+0xbb4/0xc74)

r7:c64b210c r6:00000007r5:c6a00000 r4:c66a0000

(1)首先,看看这段堆栈信息是在内核中那个文件中打印出来的,在fault.c文件中,__do_kernel_fault函数,在上面的打印中Unable to handle kernel paging request at virtual address 45685516,该地址是内核空间不可访问的地址。

staticvoid__do_kernel_fault(structmm_struct*mm,unsignedlongaddr,unsignedintfsr,structpt_regs*regs)

{

/*

* Are we prepared to handle this kernel fault?

*/

if(fixup_exception(regs))

return;

/*

* No handler, we'll have to terminate things with extreme prejudice.

*/

bust_spinlocks(1);

printk(KERN_ALERT

"Unable to handle kernel %s at virtual address %08lx\n",

(addr

show_pte(mm,addr);

die("Oops",regs,fsr);

bust_spinlocks(0);

do_exit(SIGKILL);

}

(2) 对于下面的两个信息,在函数show_pte中进行了打印,下面的打印涉及到了页全局目录,页表的知识,暂时先不分析,后续补上。

pgd=c65a4000

[45685516]*pgd=00000000

voidshow_pte(structmm_struct*mm,unsignedlongaddr)

{

pgd_t*pgd;

if(!mm)

mm=&init_mm;

printk(KERN_ALERT"pgd = %p\n",mm->pgd);

pgd=pgd_offset(mm,addr);

printk(KERN_ALERT"[%08lx] *pgd=%08lx",addr,pgd_val(*pgd));

……………………

}

(3) die函数中调用在die函数中取得thread_info结构体的地址。

structthread_info*thread=current_thread_info();

staticinlinestructthread_info*current_thread_info(void){

registerunsignedlongspasm("sp");

return(structthread_info*)(sp&~(THREAD_SIZE-1));

}

Sp: 0xc07e9c28 通过current_thread_info得到 thread_info的地址

(0xc07e9c28 & 0xffffe000) = 0xC07E8000(thread_info的地址,也就是栈底的地址)

(4)下面的打印信息在__die函数中打印

Internalerror:Oops:1[#1]

lastsysfs file:/sys/devices/form/tpm/cfg_l2/l2_rule_add

Moduleslinkedin:splic mmp(P)

CPU:0Tainted:P(2.6.32.11#42)

PCisat dev_get_by_flags+0xfc/0x140

LRisat dev_get_by_flags+0xe8/0x140

pc:[]lr:[]psr:20000013

sp:c07e9c28 ip:00000000fp:c07e9c64

r10:c6bcc560 r9:c646a220 r8:c66a0000

r7:c6a00000 r6:c0204e56 r5:30687461r4:30687461

r3:00000000r2:00000010r1:c0204e56 r0:ffffffff

Flags:nzCvIRQsonFIQsonModeSVC_32 ISA ARMSegmentkernel

Control:0005397fTable:065a4000DAC:00000017

Processswapper(pid:0,stack limit=0xc07e8270)

Stack:(0xc07e9c28to0xc07ea000)

函数的调用关系:die("Oops", regs, fsr);---à __die(str, err, thread, regs);

下面是__die函数的定义:

staticvoid__die(constchar*str,interr,structthread_info*thread,structpt_regs*regs){

structtask_struct*tsk=thread->task;

staticintdie_counter;

/*Internal error: Oops: 1 [#1]*/

printk(KERN_EMERG"Internal error: %s: %x [#%d]"S_PREEMPT S_SMP"\n",

str,err,++die_counter);

/*last sysfs file: /sys/devices/form/tpm/cfg_l2/l2_rule_add*/

sysfs_printk_last_file();

/*内核中加载的模块信息Modules linked in: splic mmp(P) */

print_modules();

/*打印寄存器信息*/

__show_regs(regs);

/*Process swapper (pid: 0, stack limit = 0xc07e8270) tsk->comm task_struct结构体中的comm表示的是除去路径后的可执行文件名称,这里的swapper为idle进程,进程号为0,创建内核进程init;其中stack limit = 0xc07e8270 指向thread_info的结束地址。*/

printk(KERN_EMERG"Process %.*s (pid: %d, stack limit = 0x%p)\n",

TASK_COMM_LEN,tsk->comm,task_pid_nr(tsk),thread+1);

/* dump_mem 函数打印从栈顶到当前sp之间的内容*/

if(!user_mode(regs)||in_interrupt()){

dump_mem(KERN_EMERG,"Stack: ",regs->ARM_sp,THREAD_SIZE+(unsignedlong)task_stack_page(tsk));

dump_backtrace(regs,tsk);

dump_instr(KERN_EMERG,regs);

}

}

在上面的函数中,主要使用了thread_info,task_struct,sp之间的指向关系。task_struct结构体的成员stack是栈底,也是对应thread_info结构体的地址。堆栈数据是从栈底+8K的地方开始向下存的。SP指向的是当前的栈顶。(unsigned long)task_stack_page(tsk),

#define task_stack_page(task) ((task)->stack) ,该宏根据task_struct得到栈底,也就是thread_info地址。

#define task_thread_info(task) ((struct thread_info *)(task)->stack),该宏根据task_struct得到thread_info指针。

(5)dump_backtrace函数

该函数用于打印函数的调用关系。Fp为帧指针,用于追溯程序的方式,方向跟踪调用函数。该函数主要是fp进行检查,看看能否进行backtrace,如果可以就调用汇编的c_backtrace,在arch/arm/lib/backtrace.S函数中。

staticvoiddump_backtrace(structpt_regs*regs,structtask_struct*tsk)

{

unsignedintfp,mode;

intok=1;

printk("Backtrace: ");

if(!tsk)

tsk=current;

if(regs){

fp=regs->ARM_fp;

mode=processor_mode(regs);

}elseif(tsk!=current){

fp=thread_saved_fp(tsk);

mode=0x10;

}else{

asm("mov %0, fp":"=r"(fp)::"cc");

mode=0x10;

}

if(!fp){

printk("no frame pointer");

ok=0;

}elseif(verify_stack(fp)){

printk("invalid frame pointer 0x%08x",fp);

ok=0;

}elseif(fp

printk("frame pointer underflow");

printk("\n");

if(ok)

c_backtrace(fp,mode);

}

(6)dump_instr

根据PC指针和指令mode, 打印出当前执行的指令码

Code: 0a000008 e5944000 e2545000 0a000005 (e4153010)

9f4a8b033023d2e8b066cd807ed0a978.png

内核中函数的调用关系

Linux Kernel 的详细介绍:请点这里

Linux Kernel 的下载地址:请点这里

0b1331709591d260c1c78e86d0c51c18.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值