定位Oops的具体代码行 (转载)

来源:互联网 发布:中国银行网上银行软件 编辑:程序博客网 时间:2024/05/15 08:08
(2008-12-01 14:33)
分类: LinuxKernel


(
来自Linus Torvalds的讨论:
[url]https://groups.google.com/group/linux.kernel/browse_thread/thread/b70bffe9015a8c41/ed9c0a0cfcd31111[/url]
又,[url]http://kerneltrap.org/Linux/Further_Oops_Insights[/url]
)
    
       例如这样的一个Oops:
             Oops: 0000[#1] PREEMPT SMP  
             Moduleslinked in: capidrv kernelcapi isdn slhc ipv6 loop dm_multipathsnd_ens1371 gameport snd_rawmidi snd_ac97_codec ac97_bussnd_seq_dummy snd_seq_oss snd_seq_midi_event snd_seq snd_seq_devicesnd_pcm_oss snd_mixer_oss snd_pcm snd_timer snd parport_pc floppyparport pcnet32 soundcore mii pcspkr snd_page_alloc ac i2c_piix4i2c_core button power_supply sr_mod sg cdrom ata_piix libatadm_snapshot dm_zero dm_mirror dm_mod BusLogic sd_mod scsi_mod ext3jbd mbcache uhci_hcd ohci_hcd ehci_hcd

             Pid: 1726,comm: kstopmachine Not tainted (2.6.24-rc3-module #2)
             EIP:0060:[<c04e53d6>] EFLAGS: 00010092CPU: 0
             EIP is atlist_del+0xa/0x61
             EAX:e0c3cc04 EBX: 00000020 ECX: 0000000e EDX: dec62000
             ESI:df6e8f08 EDI: 000006bf EBP: dec62fb4 ESP: dec62fa4
             DS: 007b ES:007b FS: 00d8 GS: 0000 SS: 0068 
             Processkstopmachine (pid: 1726, ti=dec62000 task=df8d2d40task.ti=dec62000)
             Stack:000006bf dec62fb4 c04276c7 00000020 dec62fbc c044ab4c dec62fd0c045336c 
                 df6e8f08c04532b4 00000000 dec62fe0 c043deb0 c043de75 0000000000000000 
                 c0405cdfdf6e8eb4 00000000 00000000 00000000 0000000000000000 
             CallTrace:
             [<c0406081>]show_trace_log_lvl+0x1a/0x2f
             [<c0406131>]show_stack_log_lvl+0x9b/0xa3
             [<c04061dc>]show_registers+0xa3/0x1df
             [<c0406437>]die+0x11f/0x200
             [<c0613cba>]do_page_fault+0x533/0x61a
             [<c06123ea>]error_code+0x72/0x78
             [<c044ab4c>]__unlink_module+0xb/0xf
             [<c045336c>]do_stop+0xb8/0x108
             [<c043deb0>]kthread+0x3b/0x63
             [<c0405cdf>]kernel_thread_helper+0x7/0x10
             =======================
             Code: 6b c0e8 2e 7e f6 ff e8 d1 16 f2 ff b8 01 00 00 00 e8 aa 1c f4 ff 89 d883 c4 10 5b 5d c3 90 90 90 55 89 e5 53 83 ec 0c 8b 48 04<8b> 11 39 c2 74 18 89 54 24 08 89 4424 04 c7 04 24 be 32 6bc0  
             EIP:[<c04e53d6>] list_del+0xa/0x61 SS:ESP0068:dec62fa4
             note:kstopmachine[1726] exited with preempt_count 1
    
       1,有自己编译的vmlinux: 使用gdb
    
         编译时打开compliewith debug info选项。 

         注意这行: 
    
             EIP is atlist_del+0xa/0x61
    
         这告诉我们,list_del函数有0x61这么大,而Oops发生在0xa处。那么我们先看一下list_del从哪里开始:

             # greplist_del /boot/System.map-2.6.24-rc3-module
             c10e5234 Tplist_del
             c10e53cc Tlist_del
             c120feb6 Tklist_del
             c12d6d34 r__ksymtab_list_del
             c12dadfc r__ksymtab_klist_del
             c12e1abd r__kstrtab_list_del
             c12e9d03 r__kstrtab_klist_del

         于是我们知道,发生Oops时的EIP值是:

             c10e53cc +0xa  == c10e53d6

         然后用gdb查看:

             # gdb/home/arc/build/linux-2.6/vmlinux
             (gdb) b*0xc10e53d6
             Breakpoint1 at 0xc10e53d6: file /usr/src/linux-2.6.24-rc3/lib/list_debug.c,line 64.

         看,gdb直接就告诉你在哪个文件、哪一行了。

         gdb中还可以这样:

             # gdbSources/linux-2.6.24/vmlinux
             (gdb) l*do_fork+0x1f
             0xc102b7acis in do_fork (kernel/fork.c:1385).
             1380
            1381    static intfork_traceflag(unsigned clone_flags)
            1382    {
            1383          if(clone_flags & CLONE_UNTRACED)
            1384               return0;
            1385          else if(clone_flags & CLONE_VFORK) {
            1386               if(current->ptrace &PT_TRACE_VFORK)
            1387                     returnPTRACE_EVENT_VFORK;
            1388          } else if((clone_flags & CSIGNAL) != SIGCHLD) {
            1389               if(current->ptrace &PT_TRACE_CLONE)
             (gdb)

          也可以直接知道linenumber。

          或者:

             (gdb) l*(0xffffffff8023eaf0 +0xff)  



       2,没有自己编译的vmlinux: TIPS

         如果在lkml或bugzilla上看到一个Oops,而自己不能重现,那就只能反汇编以"Code:"开始的行。这样可以尝试定位到
         源代码中。

         注意,Oops中的Code:行,会把导致Oops的第一条指令,也就是EIP的值的第一个字节,用尖括号<>括起来。 但是,有些
         体系结构(例如常见的x86)指令是不等长的(不一样的指令可能有不一样的长度),所以要不断的尝试(trial-and-error)。

         Linus通常使用一个小程序,类似这样:

             const chararray[] = "\xnn\xnn\xnn...";
             intmain(int argc, char *argv[])
             {
                 printf("%p\n", array);
                  *(int *)0 =0;
             }

e.g.
#include <stdio.h>
#include <stdlib.h>


const char array[]="\x6b\xc0\xe8\x2e\x7e\xf6\xff\xe8\xd1\x16\xf2\xff\xb8\x01\x00\x00\x00\xe8\xaa\x1c\xf4\xff\x89\xd8\x83\xc4\x10\x5b\x5d\xc3\x90\x90\x90\x55\x89\xe5\x53\x83\xec\x0c\x8b\x48\x04\x8b\x11\x39\xc2\x74\x18\x89\x54\x24\x08\x89\x44\x24\x04\xc7\x04\x24\xbe\x32\x6b\xc0";
int main(int argc, char *argv[])
{
       printf("%p\n",array);
       *(int *)0 =0;
}




         用gcc-g编译,在gdb里运行它:

            [arc@dhcp-cbjs05-218-251 ~]$ gdb hello
             GNU gdbFedora (6.8-1.fc9)
             Copyright(C) 2008 Free Software Foundation, Inc.
             LicenseGPLv3+: GNU GPL version 3 or later<[url]http://gnu.org/licenses/gpl.html[/url]>
             This isfree software: you are free to change and redistribute it.
             There is NOWARRANTY, to the extent permitted bylaw.  Type "show copying"
             and "showwarranty" for details.
             This GDBwas configured as "x86_64-redhat-linux-gnu"...
             (nodebugging symbols found)
             (gdb)r
             Startingprogram: /home/arc/hello
            0x80484e0

             Programreceived signal SIGSEGV, Segmentation fault.

         注意,这时候就可以反汇编0x80484e0这个地址:

             (gdb)disassemble 0x80484e0
             Dump ofassembler code for function array:
             0x080484e0<array+0>:  imul  $0xffffffe8,�x,�x
             0x080484e3<array+3>:  jle,pn 0x80484dc<__dso_handle+20>
             0x080484e6<array+6>:  ljmp  *<internal disassemblererror>
             0x080484e8<array+8>:  rcll  (%esi)
             0x080484ea<array+10>:  repnz(bad)
             0x080484ec<array+12>:  mov   $0x1,�x
             0x080484f1<array+17>:  call  0x7f8a1a0
             0x080484f6<array+22>:  mov   �x,�x
             0x080484f8<array+24>:  add   $0x10,%esp
             0x080484fb<array+27>:  pop   �x
             0x080484fc<array+28>:  pop   �p
             0x080484fd<array+29>:  ret
             0x080484fe<array+30>:  nop
             0x080484ff<array+31>:  nop
             0x08048500<array+32>:  nop
             0x08048501<array+33>:  push  �p
             0x08048502<array+34>:  mov   %esp,�p
             0x08048504<array+36>:  push  �x
             0x08048505<array+37>:  sub   $0xc,%esp
             0x08048508<array+40>:  mov   0x4(�x),�x
             0x0804850b<array+43>:  mov   (�x),�x
             0x0804850d<array+45>:  cmp   �x,�x
             0x0804850f<array+47>:  je    0x8048529
             0x08048511<array+49>:  mov   �x,0x8(%esp)
             0x08048515<array+53>:  mov   �x,0x4(%esp)
             0x08048519<array+57>:  movl  $0xc06b32be,(%esp)
             0x08048520<array+64>:  add   %ah,0xa70
             End ofassembler dump.
             (gdb)

         OK,现在你知道出错的那条指令是array[43],也就是mov   (�x),�x,也就是说,(�x)指向了一个错误内存地址。

补充:

为了使汇编代码和C代码更好的对应起来, Linux内核的Kbuild子系统提供了这样一个功能:任何一个C文件都可以单独编译成汇编文件,例如:

make path/to/the/sourcefile.s

例如我想把kernel/sched.c编译成汇编,那么:

make kernel/sched.s V=1

或者:

make kernel/sched.lst V=1

        编译出*.s文件
         
         有时侯需要对*.s文件进行分析,以确定BUG所在的位置。 对任何一个内核build目录下的*.c文件,都可以
         直接编译出*.s文件。

                # make drivers/net/e100.s V=1
         
         而对于自己写的module,就需要在Makefile中有一个灵活的target写法:
                
              # cat Makefile
              obj-m := usb-skel.o
              KDIR := /lib/modules/`uname-r`/build
              traget := modules

              default:
                     make -C$(KDIR) M=$(shell pwd) $(target)
              clean:
                     rm -f *.o*.ko .*.cmd *.symvers *.mod.c
                     rm -rf.tmp_versions


              # make target=usb-skel.sV=1
         
         这样,kbuild系统才知道你要make的目标不是modules,而是usb-skel.s。




另外, 内核源代码目录的./scripts/decodecode文件是用来解码Oops的:

./scripts/decodecode < Oops.txt

(我没用过,就只提一下。)

0 0
原创粉丝点击