结合上面的知识,看下当内核打印堆栈信息时,都打印了上面信息。下面的打印信息是工作中遇到的一种情况,打印了内核的堆栈信息,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)
内核中函数的调用关系
Linux Kernel 的详细介绍:请点这里
Linux Kernel 的下载地址:请点这里