Linux核心調試之Oops資訊
Oops這個英文單詞的意思是“哎呀”,當核心出錯時(比如訪問非法地址),輸出的資訊就成為Oops資訊,下面用一個例子來介紹一下Oops資訊:
例子:為了測試Oops資訊,這裡我們可以任意選擇一個核心檔案做測試,我以i2c為例:
1.
修改linux原始碼/drivers/i2c/i2c-core.c
在函數static int __init i2c_init(void)的開頭添加如下語句:
int * ptest = NULL;*ptest = 0xabcd;
如下:
Linux核心執行到這裡就會出錯,產生Oops資訊
2.
然後在menuconfig中將i2c編譯進核心:
make menuconfig
在Device Drivers中選擇I2C support,把I2C編譯進核心
3.
重新編譯核心,然後用該核心啟動
4.
果然出現了Oops資訊啦~~如:
中的從第7行Bug開始到最後就是Oops資訊
5.
為了將Oops資訊中的機器碼地址和源檔案相對應,我們還要將編譯好的核心鏡像反組譯碼以便對照,但由於我們之前用make bzImage命令來產生bzImage核心壓縮鏡像,無法反組譯碼,所以我們運行如下命令:
make vmlinux 表示對產生的核心鏡像不壓縮
這樣產生的核心鏡像大約150M!
6.
將第5步產生的無壓縮的核心鏡像反組譯碼:
objdump -D vmlinux > vmlinux.dis
產生的vmlinux.dis大約500M!呵呵,如下:
vmlinux: file format elf32-i386Disassembly of section .text:c1000000 <_text>:c1000000: f6 86 11 02 00 00 40 testb $0x40,0x211(%esi)c1000007: 75 14 jne c100001d <_text+0x1d>c1000009: 0f 01 15 16 4e 6a 01 lgdtl 0x16a4e16c1000010: b8 18 00 00 00 mov $0x18,%eaxc1000015: 8e d8 mov %eax,%dsc1000017: 8e c0 mov %eax,%esc1000019: 8e e0 mov %eax,%fsc100001b: 8e e8 mov %eax,%gsc100001d: fc cldc100001e: 31 c0 xor %eax,%eaxc1000020: bf 00 90 7a 01 mov $0x17a9000,%edic1000025: b9 5c b8 83 01 mov $0x183b85c,%ecxc100002a: 29 f9 sub %edi,%ecxc100002c: c1 e9 02 shr $0x2,%ecxc100002f: f3 ab rep stos %eax,%es:(%edi)c1000031: bf e0 99 73 01 mov $0x17399e0,%edic1000036: b9 00 04 00 00 mov $0x400,%ecxc100003b: fc cldc100003c: f3 a5 rep movsl %ds:(%esi),%es:(%edi)c100003e: 8b 35 08 9c 73 01 mov 0x1739c08,%esic1000044: 21 f6 and %esi,%esic1000046: 74 0c je c1000054 <_text+0x54>c1000048: bf 00 73 73 01 mov $0x1737300,%edic100004d: b9 00 02 00 00 mov $0x200,%ecxc1000052: f3 a5 rep movsl %ds:(%esi),%es:(%edi)c1000054: 66 81 3d e6 9b 73 01 cmpw $0x207,0x1739be6c100005b: 07 02c100005d: 72 1c jb c100007b <default_entry>c100005f: a1 1c 9c 73 01 mov 0x1739c1c,%eaxc1000064: 3d 04 00 00 00 cmp $0x4,%eaxc1000069: 73 0e jae c1000079 <lguest_entry>c100006b: 8b 04 85 c0 72 73 01 mov 0x17372c0(,%eax,4),%eaxc1000072: 2d 00 00 00 c0 sub $0xc0000000,%eaxc1000077: ff e0 jmp *%eax ..................................................
下面開始詳細介紹Oops資訊:
BUG: unable to handle kernel NULL pointer dereference at (null)
這是Oops資訊的字串說明,說明我們訪問了NULL指標
IP: [<c172f60b>] i2c_init+0x9/0x63
<c172f60b>表示核心錯誤處的ip指標,在vmlinux.dis中搜尋“c172f60b”,找到如下:
c172f5fe: 5e pop %esic172f5ff: 5f pop %edic172f600: 5d pop %ebpc172f601: c3 retc172f602 <i2c_init>:c172f602: 55 push %ebpc172f603: b8 20 c0 6d c1 mov $0xc16dc020,%eaxc172f608: 89 e5 mov %esp,%ebpc172f60a: 53 push %ebxc172f60b: c7 05 00 00 00 00 cd movl $0xabcd,0x0c172f612: ab 00 00c172f615: e8 c6 92 bd ff call c13088e0 <bus_register>c172f61a: 85 c0 test %eax,%eaxc172f61c: 89 c3 mov %eax,%ebxc172f61e: 75 40 jne c172f660 <i2c_init+0x5e>c172f620: b8 67 ee 63 c1 mov $0xc163ee67,%eaxc172f625: e8 36 ab bd ff call c130a160 <class_compat_register>c172f62a: 85 c0 test %eax,%eaxc172f62c: a3 d4 76 83 c1 mov %eax,0xc18376d4c172f631: 74 1e je c172f651 <i2c_init+0x4f>
這正好是在i2c_init函數中。
i2c_init+0x9/0x63
表示出錯的是在i2c_init函數的起始地址(0xc172f602)位移0x9的位置,而i2c_init函數的大小為0x63個位元組,出錯處的ip=c172f60b,正好等於0xc172f602+0x9
Pid: 1
發生錯誤的進程ID
[ 0.992003] EIP: 0060:[<c172f60b>] EFLAGS: 00010246 CPU: 0 [ 0.992003] EIP is at i2c_init+0x9/0x63 [ 0.992003] EAX: c16dc020 EBX: c1785e34 ECX: 00000000 EDX: 00000000 [ 0.992003] ESI: c16fe164 EDI: 00000000 EBP: df071f98 ESP: df071f94 [ 0.992003] DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068 [ 0.992003] Process swapper (pid: 1, ti=df070000 task=df068000 task.ti=df070)
發生錯誤時的各個寄存器的值,其中的CPU: 0表示發生錯誤的CPU編號,對於單一處理器系統來說,編號為0
[ 1.240004] Call Trace:[ 1.240004] [<c1001272>] ? do_one_initcall+0x32/0x1a0[ 1.240004] [<c172f602>] ? i2c_init+0x0/0x63[ 1.240004] [<c17018d9>] ? kernel_init+0x12d/0x183[ 1.240004] [<c17017ac>] ? kernel_init+0x0/0x183[ 1.240004] [<c1003e67>] ? kernel_thread_helper+0x7/0x10
發生錯誤時的堆棧調用,最下面的是最上層的調用
Code: 89 18 83 7d ec 00 0f 85 64 ff ff ff 31 db b8 00 c0 6d c1 e8
出錯指令附近的指令的機器碼
利用上面這些資訊,我們就可以很快的找到錯誤的位置啦~~
完成!