關鍵詞:Illegal Instruction、SIGILL等。linux
進程在運行過程當中會收到SIGILL信號,此類錯誤是由操做系統發送給進程的。編程
SIGILL是某個進程中的某一句不能被CPU識別指令,這些指令多是一些形式錯誤、未知或者特權指令。安全
進程代碼段中數據是做爲指令運行的,若是不當心代碼段被錯誤覆蓋,那麼CPU可能沒法識別對應的代碼,進而形成Illegal Instruction。app
一樣,若是棧被不當心覆蓋了,形成返回地址錯誤、CPU跳轉到錯誤地址,執行沒有意義的內存數據,進而形成Illegal Instruction。函數
進一步能夠認爲,任何致使代碼段錯誤的問題均可能帶來Illegal Instruction。工具
CPU的指令集在不停演進,若是將較新指令集版本的程序在老版本CPU上運行,則老版本CPU運行時會有Illegal Instruction問題。ui
編譯器(彙編器或者鏈接器)自身的bug,有可能生成CPU沒法識別的指令。spa
出現錯誤的指令可能和訪存地址指令有關。 另外,浮點數的格式是否符合IEEE的標準也可能會有影響。操作系統
程序中有沒有特權指令、或者訪問特權寄存器unix
有沒有將在較新CPU上編譯獲得的可執行文件拿到老CPU上運行------------這種問題是100%復現,只須要查看對應彙編程序便可知道大概。
程序中有沒有嵌入式彙編,先檢查。-------------------------------------------------編譯器bug。
通常編譯器不多會生成有這種問題的代碼
X86平臺上要尤爲注意64位彙編指令和32位彙編指令的混用問題
程序有在進程代碼段空間寫數據的機會嗎?----------------------------------------下面的分析就是代碼段被非法修改。還多是意見存在問題,DDR中數據正確,從DDR讀取的數據通過總線產生數據突變異常。
棧操做夠安全嗎?--------------------------------------------------------------------------若是異常PC指向棧,那麼便是棧被非法修改。
注意程序的ABI是否正確------------------------------------------------------------------100%復現問題,只須要檢查ABI說明書便可。
尤爲是動態鏈和靜態鏈是否處理的正確,儘可能避免動態鏈的可執行文件調用錯誤庫的問題(ARM的EABI,MIPS的N32/O32/N64都極可能出這種問題)
用的工具鏈靠譜嗎?
CK異常向量VEC_ILLEGAL對應非法指令錯誤,出現問題的時候內核輸出「Illegal instruction Error」,而後輸出寄存去、相關代碼段、棧等信息;最後發送SIGILL信號給進程。
asmlinkage void trap_c(struct pt_regs *regs) { int sig; unsigned long vector; siginfo_t info; vector = (mfcr("psr") >> 16) & 0xff; switch (vector) { ... case VEC_ILLEGAL: #ifndef CONFIG_CPU_NO_USER_BKPT if (*(uint16_t *)instruction_pointer(regs) != 0x1464) #endif { sig = SIGILL; pr_err("Illegal instruction Error\n"); show_regs(regs); break; } ... } send_sig(sig, current, 0);---------------------------------------------發送SIGILL給當前進程。 } void show_regs(struct pt_regs *fp) { unsigned long *sp; unsigned char *tp; int i; pr_info("\nCURRENT PROCESS:\n\n"); pr_info("COMM=%s PID=%d\n", current->comm, current->pid); if (current->mm) { pr_info("TEXT=%08x-%08x DATA=%08x-%08x BSS=%08x-%08x\n", (int) current->mm->start_code, (int) current->mm->end_code, (int) current->mm->start_data, (int) current->mm->end_data, (int) current->mm->end_data, (int) current->mm->brk); pr_info("USER-STACK=%08x KERNEL-STACK=%08x\n\n", (int) current->mm->start_stack, (int) (((unsigned long) current) + 2 * PAGE_SIZE)); } pr_info("PC: 0x%08lx (%pS)\n", (long)fp->pc, (void *)fp->pc); pr_info("LR: 0x%08lx (%pS)\n", (long)fp->lr, (void *)fp->lr); pr_info("SP: 0x%08lx \n", (long)fp); pr_info("orig_a0: 0x%08lx\n", fp->orig_a0); pr_info("PSR: 0x%08lx\n", (long)fp->sr); pr_info(" a0: 0x%08lx a1: 0x%08lx a2: 0x%08lx a3: 0x%08lx\n", fp->a0, fp->a1, fp->a2, fp->a3); #if defined(__CSKYABIV2__) pr_info(" r4: 0x%08lx r5: 0x%08lx r6: 0x%08lx r7: 0x%08lx\n", fp->regs[0], fp->regs[1], fp->regs[2], fp->regs[3]); pr_info(" r8: 0x%08lx r9: 0x%08lx r10: 0x%08lx r11: 0x%08lx\n", fp->regs[4], fp->regs[5], fp->regs[6], fp->regs[7]); pr_info("r12: 0x%08lx r13: 0x%08lx r15: 0x%08lx\n", fp->regs[8], fp->regs[9], fp->lr); pr_info("r16: 0x%08lx r17: 0x%08lx r18: 0x%08lx r19: 0x%08lx\n", fp->exregs[0], fp->exregs[1], fp->exregs[2], fp->exregs[3]); pr_info("r20: 0x%08lx r21: 0x%08lx r22: 0x%08lx r23: 0x%08lx\n", fp->exregs[4], fp->exregs[5], fp->exregs[6], fp->exregs[7]); pr_info("r24: 0x%08lx r25: 0x%08lx r26: 0x%08lx r27: 0x%08lx\n", fp->exregs[8], fp->exregs[9], fp->exregs[10], fp->exregs[11]); pr_info("r28: 0x%08lx r29: 0x%08lx r30: 0x%08lx tls: 0x%08lx\n", fp->exregs[12], fp->exregs[13], fp->exregs[14], fp->tls); pr_info(" hi: 0x%08lx lo: 0x%08lx \n", fp->rhi, fp->rlo); #else pr_info(" r6: 0x%08lx r7: 0x%08lx r8: 0x%08lx r9: 0x%08lx\n", fp->regs[0], fp->regs[1], fp->regs[2], fp->regs[3]); pr_info("r10: 0x%08lx r11: 0x%08lx r12: 0x%08lx r13: 0x%08lx\n", fp->regs[4], fp->regs[5], fp->regs[6], fp->regs[7]); pr_info("r14: 0x%08lx r1: 0x%08lx r15: 0x%08lx\n", fp->regs[8], fp->regs[9], fp->lr); #endif pr_info("\nCODE:");---------------------------------------------------------------加大dump的代碼段範圍,確認覆蓋範圍是多少? tp = ((unsigned char *) fp->pc) - 0x40; tp += ((int)tp % 4) ? 2 : 0; for (sp = (unsigned long *) tp, i = 0; (i < 0xc0); i += 4) { if ((i % 0x10) == 0) pr_cont("\n%08x: ", (int) (tp + i)); pr_cont("%08x ", (int) *sp++); } pr_cont("\n"); pr_info("\nKERNEL STACK:"); tp = ((unsigned char *) fp) - 0x40; for (sp = (unsigned long *) tp, i = 0; (i < 0xc0); i += 4) { if ((i % 0x10) == 0) pr_cont("\n%08x: ", (int) (tp + i)); pr_cont("%08x ", (int) *sp++); } pr_cont("\n"); show_stack(NULL, (unsigned long *)fp->regs[4]); return; }
Illegal Instruction輸出以下:
[ 2343.202217] Illegal instruction Error [ 2343.205883] [ 2343.205883] CURRENT PROCESS: [ 2343.205883] [ 2343.211728] COMM=syslogd PID=135-----------------------------------發生錯誤進程的信息,代碼數據段。 [ 2343.214963] TEXT=00008000-000c68cc DATA=000c7f1c-000c8175 BSS=000c8175-000ea000 [ 2343.222278] USER-STACK=7f89ae80 KERNEL-STACK=be826580 [ 2343.222278] [ 2343.228906] PC: 0x805397de (__skb_try_recv_datagram+0x4e/0x2d8)----0x80000000以上的地址表示內核空間。 [ 2343.234837] LR: 0x805ce90e (unix_dgram_recvmsg+0xa2/0x56c) [ 2343.240327] SP: 0xbe82bd1c [ 2343.243124] orig_a0: 0xbf3b2400 [ 2343.246269] PSR: 0x80040340 [ 2343.249070] a0: 0xbf3b2400 a1: 0x00000000 a2: 0xbe82bdb8 a3: 0x00000000 [ 2343.256301] r4: 0xbe82be0c r5: 0xbf3b2400 r6: 0xbe82be14 r7: 0xbe82be14 [ 2343.263531] r8: 0xbe82bdbc r9: 0xbe82be08 r10: 0xbe82be04 r11: 0x00000000 [ 2343.270761] r12: 0x80100340 r13: 0x805397c0 r15: 0x805ce90e [ 2343.276514] r16: 0xbe82be04 r17: 0xbe82be10 r18: 0xbe8245b4 r19: 0x0000003b [ 2343.283745] r20: 0x00000000 r21: 0x00000000 r22: 0x00000038 r23: 0x2dc2ae38 [ 2343.290974] r24: 0xbf3b2454 r25: 0x00000001 r26: 0x8004f940 r27: 0x000000ff [ 2343.298204] r28: 0x2abf5000 r29: 0x00000000 r30: 0x00000000 tls: 0x00000001 [ 2343.305433] hi: 0x007838aa lo: 0x33ee4b1f [ 2343.309793] CODE:------------------------------------------------------------------PC指向的代碼段,非法指令即在此處。發生在內核的地址比較固定。若是在用戶空間,則須要代碼段映射的其實地址。 805397c0: 4820c400 4831c402 e4486dcf d8681003 805397d0: b260200b 2040d860 da086d43 e560200c 805397e0: e923fe53 e4610403 e4480040 e5210107 805397f0: b2602002 20001047 20002070 6026d900 [ 2343.328840] KERNEL STACK: be82bcdc: be82bcdc 808fec04 00000004 00000001 be82bcec: 000c8175 000ea000 be82bd10 8004b002 be82bcfc: be82bd1c bf3b2400 be82be14 be82be14 be82bd0c: be82be08 be82bdbc 8004a418 be82be0c be82bd1c: 00000001 805ce90e 805397de 80040340 be82bd2c: 2df4eaf0 bf3b2400 bf3b2400 00000000 be82bd3c: be82bdb8 00000000 be82be0c bf3b2400 be82bd4c: be82be14 be82be14 be82bdbc be82be08 be82bd5c: be82be04 00000000 80100340 805397c0 be82bd6c: be82be04 be82be10 be8245b4 0000003b be82bd7c: 00000000 00000000 00000038 2dc2ae38 be82bd8c: bf3b2454 00000001 8004f940 000000ff [ 2343.382450] Call Trace: [<805ce90e>] unix_dgram_recvmsg+0xa2/0x56c [<8052b6f0>] sock_recvmsg+0x40/0x50 [<8052b786>] sock_read_iter+0x86/0xac [<80134de8>] __vfs_read+0xc0/0x108 [<80135c14>] vfs_read+0x94/0x128 [<80136d12>] SyS_read+0x52/0xd4 [<8004a246>] csky_systemcall+0x96/0xe0
地址0x805397c0經過csky-abiv2-linux-objudmp -D -S vmlinux以後,以下:
80539790 <__skb_try_recv_datagram>: ... 805397c0: c4004820 lsli r0, r0, 0 805397c4: c4024831 lsli r17, r2, 0 805397c8: 6dcf mov r7, r3 805397ca: e4481003 subi r2, r8, 4 805397ce: d868200b ld.w r3, (r8, 0x2c) 805397d2: b260 st.w r3, (r2, 0) 805397d4: d8602040 ld.w r3, (r0, 0x100) 805397d8: 6d43 mov r5, r0 805397da: da08200c ld.w r16, (r8, 0x30) 805397de: e5600053 addi r11, r0, 84-----------------------------非法指令異常現場。 805397e2: e9230103 bnez r3, 0x805399e8 // 805399e8 <_end+0xffb857e8> 805397e6: e4612040 andi r3, r1, 64 805397ea: e4481007 subi r2, r8, 8 805397ee: e5212002 andi r9, r1, 2 805397f2: b260 st.w r3, (r2, 0) 805397f4: d9472000 ld.w r10, (r7, 0) 805397f8: dd702000 st.w r11, (r16, 0) 805397fc: c0006026 mfcr r6, cr<0, 0> ...
將log dump出來的代碼段按照objdump順序排列以下,能夠看出紅色加粗部分的不一致:
c4004820
c4024831
6dcf
e4481003
d868200b
b260
d8602040
6d43
da08200c
e560fe53-------------------------------------------------------------------------------PC指向的異常代碼點。
e9230403
e4610040
e4480107
e5212002
b260
10472000
20702000
d9006026
而後在對log dump數據按照地址排列,能夠發現紅色加粗部分存在必定規律:突變的8bit位置固定。
805397c0: 4820c400 4831c402 e4486dcf d8681003 805397d0: b260200b 2040d860 da086d43 e560200c 805397e0: e923fe53 e4610403 e4480040 e5210107 805397f0: b2602002 20001047 20002070 6026d900
能夠看出從DDR到CPU的數據明顯的出錯規律。
這種類型的錯誤不像是上面錯誤排查裏面的任一種,而相似硬件異常致使的。
須要對SIGILL進行處理,打印bin代碼段以及庫代碼段。
signal(SIGILL,sigillDeal);----------------------------------註冊SIGILL對應的處理函數 void sigillDeal(int sig) { if(sig == SIGILL) { printf("\nGot SIGILL(Illegal Instruction)\n"); system("cat /proc/`pidof AiApp`/maps");-----------------獲取進程的maps信息。 raise(SIGSEGV);-----------------------------------------將當前進程的內存存入coredump中,便於後續經過gdb分析導出內存內容。 } }
若是內核產生SIGILL信號,用戶空間收到信號就會記錄當前進程maps,而且將進程內存保存到core中。
後面再core中可使用dump memory處處代碼段和bin文件進行對比。
dump memory app.bin 0x8000 0x590000
能夠經過hexdump將app.bin和對應代碼段對比。
bin文件和庫文件都經過mmap到進程的地址空間,在進程的/proc/xxx/map_files中存在一地址範圍爲名稱的文件。
經過hexdump讀取這些文件,能夠知道文件在內存中的值。
而後和對應的bin、庫文件對比便可。
lr-------- 1 root root 64 Jan 1 08:17 2aaa8000-2aac5000 -> /lib/ld-2.28.9000.so lr-------- 1 root root 64 Jan 1 08:17 2aac5000-2aac6000 -> /lib/ld-2.28.9000.so lr-------- 1 root root 64 Jan 1 08:17 2aac6000-2aac7000 -> /lib/ld-2.28.9000.so lr-------- 1 root root 64 Jan 1 08:17 2aaca000-2aacb000 -> /usr/lib/libtestdevice.so lr-------- 1 root root 64 Jan 1 08:17 2aacb000-2aacc000 -> /usr/lib/libtestdevice.so lr-------- 1 root root 64 Jan 1 08:17 2aacc000-2aacd000 -> /usr/lib/libtestdevice.so
具體地址範圍對應的是代碼段仍是數據段能夠經過/proc/xxx/maps獲取:
2aaa8000-2aac5000 r-xp 00000000 b3:01 524478 /lib/ld-2.28.9000.so 2aac5000-2aac6000 r--p 0001c000 b3:01 524478 /lib/ld-2.28.9000.so 2aac6000-2aac7000 rw-p 0001d000 b3:01 524478 /lib/ld-2.28.9000.so 2aac7000-2aac8000 r-xp 00000000 00:00 0 [vdso] 2aac8000-2aaca000 rw-p 00000000 00:00 0 2aaca000-2aacb000 r-xp 00000000 b3:01 1180187 /usr/lib/libtestdevice.so 2aacb000-2aacc000 r--p 00000000 b3:01 1180187 /usr/lib/libtestdevice.so 2aacc000-2aacd000 rw-p 00001000 b3:01 1180187 /usr/lib/libtestdevice.so
hexdump 2aaa8000-2aac5000 -n 128 -s 4096和hexdump ld-2.28.9000.so -n 128 -s 4096結果對好比下:
參考文檔:《Illegal Instruction錯誤小談》