Linux内核Crash分析(1)(2)
(3) die函数中调用在die函数中取得thread_info结构体的地址。
struct thread_info *thread = current_thread_info(); static inline struct thread_info *current_thread_info(void){ register unsigned long sp asm ("sp"); return (struct thread_info *)(sp &~(THREAD_SIZE - 1)); }
Sp: 0xc07e9c28通过current_thread_info得到 thread_info的地址
(0xc07e9c28 &0xffffe000) = 0xC07E8000thread_info的地址,也就是栈底的地址)
(4)下面的打印信息在__die函数中打印
Internal error: Oops: 1 [#1] last sysfs file: /sys/devices/form/tpm/cfg_l2/l2_rule_add Modules linked in: splic mmp(P) CPU: 0 Tainted: P (2.6.32.11 #42) PC is at dev_get_by_flags+0xfc/0x140 LR is at dev_get_by_flags+0xe8/0x140 pc : [<c06bee24>] lr : [<c06bee10>] psr: 20000013 sp : c07e9c28 ip : 00000000 fp : c07e9c64 r10: c6bcc560 r9 : c646a220 r8 : c66a0000 r7 : c6a00000 r6 : c0204e56 r5 : 30687461 r4 : 30687461 r3 : 00000000 r2 : 00000010 r1 : c0204e56 r0 : ffffffff Flags: nzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel Control: 0005397f Table: 065a4000 DAC: 00000017 Process swapper (pid: 0, stack limit = 0xc07e8270) Stack: (0xc07e9c28 to 0xc07ea000)
函数的调用关系:die("Oops", regs, fsr);---à__die(str, err, thread, regs);
下面是__die函数的定义:
static void __die(const char *str, int err, struct thread_info *thread, struct pt_regs *regs){ struct task_struct *tsk = thread->task; static int die_counter; /*Internal error: Oops: 1 [#1]*/ printk(KERN_EMERG "Internal error: %s: %x [#%d]" S_PREEMPT S_SMP " ", 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) ", 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 + (unsigned long)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函数中。
static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) { unsigned int fp, mode; int ok = 1; printk("Backtrace: "); if (!tsk) tsk = current; if (regs) { fp = regs->ARM_fp; mode = processor_mode(regs); } else if (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; } else if (verify_stack(fp)) { printk("invalid frame pointer 0x%08x", fp); ok = 0; } else if (fp <(unsigned long)end_of_stack(tsk)) printk("frame pointer underflow"); printk(" "); if (ok) c_backtrace(fp, mode); }
6)dump_instr
根据PC指针和指令mode, 打印出当前执行的指令码
Code: 0a000008 e5944000 e2545000 0a000005 (e4153010)
内核中函数的调用关系
原文链接:http://blog.chinaunix.net/uid-20788636-id-4377271.html
评论暂时关闭