关键词:Illegal Instruction、SIGILL等。

进程在运行过程中会收到SIGILL信号,此类错误是由操作系统发送给进程的。

SIGILL是某个进程中的某一句不能被CPU识别指令,这些指令可能是一些形式错误、未知或者特权指令。

1. SIGILL原因

1.1 错误修改代码段

进程代码段中数据是作为指令运行的,如果不小心代码段被错误覆盖,那么CPU可能无法识别对应的代码,进而造成Illegal Instruction。

同样,如果栈被不小心覆盖了,造成返回地址错误、CPU跳转到错误地址,执行没有意义的内存数据,进而造成Illegal Instruction。

进一步可以认为,任何导致代码段错误的问题都可能带来Illegal Instruction。

1.2 指令集演进

CPU的指令集在不停演进,如果将较新指令集版本的程序在老版本CPU上运行,则老版本CPU运行时会有Illegal Instruction问题。

1.3 工具链Bug

编译器(汇编器或者连接器)自身的bug,有可能生成CPU无法识别的指令。

1.4 内存访问对齐或浮点格式问题

出现错误的指令可能和访存地址指令有关。 另外,浮点数的格式是否符合IEEE的标准也可能会有影响。

2. 错误排查指南

  • 程序中有没有特权指令、或者访问特权寄存器

  • 有没有将在较新CPU上编译得到的可执行文件拿到老CPU上运行------------这种问题是100%复现,只需要查看对应汇编程序即可知道大概。

  • 程序中有没有嵌入式汇编,先检查。-------------------------------------------------编译器bug。

    • 一般编译器很少会生成有这种问题的代码

    • X86平台上要尤其注意64位汇编指令和32位汇编指令的混用问题

  • 程序有在进程代码段空间写数据的机会吗?----------------------------------------下面的分析就是代码段被非法修改。还可能是意见存在问题,DDR中数据正确,从DDR读取的数据经过总线产生数据突变异常。

  • 栈操作够安全吗?--------------------------------------------------------------------------如果异常PC指向栈,那么即是栈被非法修改。

  • 注意程序的ABI是否正确------------------------------------------------------------------100%复现问题,只需要检查ABI说明书即可。

    • 尤其是动态链和静态链是否处理的正确,尽量避免动态链的可执行文件调用错误库的问题(ARM的EABI,MIPS的N32/O32/N64都很可能出这种问题)

  • 用的工具链靠谱吗?

3. Illegal Instruction处理

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") >> ) & 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, );---------------------------------------------发送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) + * 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[], fp->regs[], fp->regs[], fp->regs[]);
pr_info(" r8: 0x%08lx r9: 0x%08lx r10: 0x%08lx r11: 0x%08lx\n",
fp->regs[], fp->regs[], fp->regs[], fp->regs[]);
pr_info("r12: 0x%08lx r13: 0x%08lx r15: 0x%08lx\n",
fp->regs[], fp->regs[], fp->lr);
pr_info("r16: 0x%08lx r17: 0x%08lx r18: 0x%08lx r19: 0x%08lx\n",
fp->exregs[], fp->exregs[], fp->exregs[], fp->exregs[]);
pr_info("r20: 0x%08lx r21: 0x%08lx r22: 0x%08lx r23: 0x%08lx\n",
fp->exregs[], fp->exregs[], fp->exregs[], fp->exregs[]);
pr_info("r24: 0x%08lx r25: 0x%08lx r26: 0x%08lx r27: 0x%08lx\n",
fp->exregs[], fp->exregs[], fp->exregs[], fp->exregs[]);
pr_info("r28: 0x%08lx r29: 0x%08lx r30: 0x%08lx tls: 0x%08lx\n",
fp->exregs[], fp->exregs[], fp->exregs[], 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[], fp->regs[], fp->regs[], fp->regs[]);
pr_info("r10: 0x%08lx r11: 0x%08lx r12: 0x%08lx r13: 0x%08lx\n",
fp->regs[], fp->regs[], fp->regs[], fp->regs[]);
pr_info("r14: 0x%08lx r1: 0x%08lx r15: 0x%08lx\n",
fp->regs[], fp->regs[], fp->lr);
#endif pr_info("\nCODE:");---------------------------------------------------------------加大dump的代码段范围,确认覆盖范围是多少?
tp = ((unsigned char *) fp->pc) - 0x40;
tp += ((int)tp % ) ? : ;
for (sp = (unsigned long *) tp, i = ; (i < 0xc0); i += ) {
if ((i % 0x10) == )
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 = ; (i < 0xc0); i += ) {
if ((i % 0x10) == )
pr_cont("\n%08x: ", (int) (tp + i));
pr_cont("%08x ", (int) *sp++);
}
pr_cont("\n"); show_stack(NULL, (unsigned long *)fp->regs[]);
return;
}

4. Illegal Instruction问题分析(位于Kernel)

Illegal Instruction输出如下:

[ 2343.202217] Illegal instruction Error
[ 2343.205883]
[ 2343.205883] CURRENT PROCESS:
[ 2343.205883]
[ 2343.211728] COMM=syslogd PID=135-----------------------------------发生错误进程的信息,代码数据段。
[ 2343.214963] TEXT=-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
be82bcec: 000c8175 000ea000 be82bd10 8004b002
be82bcfc: be82bd1c bf3b2400 be82be14 be82be14
be82bd0c: be82be08 be82bdbc 8004a418 be82be0c
be82bd1c: 805ce90e 805397de
be82bd2c: 2df4eaf0 bf3b2400 bf3b2400
be82bd3c: be82bdb8 be82be0c bf3b2400
be82bd4c: be82be14 be82be14 be82bdbc be82be08
be82bd5c: be82be04 805397c0
be82bd6c: be82be04 be82be10 be8245b4 0000003b
be82bd7c: 2dc2ae38
be82bd8c: bf3b2454 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之后,如下:

 <__skb_try_recv_datagram>:
...
805397c0: c4004820 lsli r0, r0,
805397c4: c4024831 lsli r17, r2,
805397c8: 6dcf mov r7, r3
805397ca: e4481003 subi r2, r8,
805397ce: d868200b ld.w r3, (r8, 0x2c)
805397d2: b260 st.w r3, (r2, )
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,
805397ea: e4481007 subi r2, r8,
805397ee: e5212002 andi r9, r1,
805397f2: b260 st.w r3, (r2, )
805397f4: d9472000 ld.w r10, (r7, )
805397f8: dd702000 st.w r11, (r16, )
805397fc: c0006026 mfcr r6, cr<, >
...

将log dump出来的代码段按照objdump顺序排列如下,可以看出红色加粗部分的不一致:

c4004820
c4024831
6dcf
e4481003
d868200b
b260
d8602040
6d43
da08200c
e560fe53-------------------------------------------------------------------------------PC指向的异常代码点。
e92303
e4610040
e4480107
e5212002
b260
10472000
702000
d9006026

然后在对log dump数据按照地址排列,可以发现红色加粗部分存在一定规律:突变的8bit位置固定。

805397c0: 4820c400 4831c402 e4486dcf d8681003
805397d0: b260200b 2040d860 da086d43 e560200c
805397e0: e923fe53 e4610403 e4480040 e5210107
805397f0: b2602002 200047 20002070 6026d900

可以看出从DDR到CPU的数据明显的出错规律。

这种类型的错误不像是上面错误排查里面的任一种,而类似硬件异常导致的。

4. Illegal Instruction问题定位(位于userspace)

4.1 用户空间代码段定位

需要对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和对应代码段对比。

4.2 hexdump对比进程内存和代码段

bin文件和库文件都通过mmap到进程的地址空间,在进程的/proc/xxx/map_files中存在一地址范围为名称的文件。

通过hexdump读取这些文件,可以知道文件在内存中的值。

然后和对应的bin、库文件对比即可。

lr--------     root     root           Jan   : 2aaa8000-2aac5000 -> /lib/ld-2.28..so
lr-------- root root Jan : 2aac5000-2aac6000 -> /lib/ld-2.28..so
lr-------- root root Jan : 2aac6000-2aac7000 -> /lib/ld-2.28..so
lr-------- root root Jan : 2aaca000-2aacb000 -> /usr/lib/libtestdevice.so
lr-------- root root Jan : 2aacb000-2aacc000 -> /usr/lib/libtestdevice.so
lr-------- root root Jan : 2aacc000-2aacd000 -> /usr/lib/libtestdevice.so

具体地址范围对应的是代码段还是数据段可以通过/proc/xxx/maps获取:

2aaa8000-2aac5000 r-xp  b3:      /lib/ld-2.28..so
2aac5000-2aac6000 r--p 0001c000 b3: /lib/ld-2.28..so
2aac6000-2aac7000 rw-p 0001d000 b3: /lib/ld-2.28..so
2aac7000-2aac8000 r-xp : [vdso]
2aac8000-2aaca000 rw-p :
2aaca000-2aacb000 r-xp b3: /usr/lib/libtestdevice.so
2aacb000-2aacc000 r--p b3: /usr/lib/libtestdevice.so
2aacc000-2aacd000 rw-p b3: /usr/lib/libtestdevice.so

hexdump 2aaa8000-2aac5000 -n 128 -s 4096和hexdump ld-2.28.9000.so  -n 128 -s 4096结果对比如下:

参考文档:《Illegal Instruction错误小谈

最新文章

  1. 随机生成长度为len的密码,且包括大写、小写英文字母和数字
  2. Java:回调机制
  3. Python:装饰器
  4. CentOS7设置IP地址
  5. 单点登录CAS使用记(一):前期准备以及为CAS-Server配置SSL协议
  6. zabbix log(logrt) key的使用
  7. debugging python with IDLE
  8. jTDS Java连接SQL Server 2000数据库
  9. ubuntu解压时中文出现乱码
  10. Mawawa CSS 学习之旅 Display
  11. redis 运维手册
  12. 【Android】Android WebView 网页输入框获取焦点
  13. sessionid固定与session混淆的一些随想
  14. [No0000131]WCF压缩传输方案整理
  15. [No0000125]WCF安全体系
  16. 通过 DDNS 解决宽带拨号 ip 变化问题
  17. Java7/8 HashMap ConcurrentHashMap
  18. 17. Letter Combinations of a Phone Number (backtracking)
  19. php使用sftp上传文件
  20. gitlab 本地建库配置 config

热门文章

  1. Netty如何监控内存泄露
  2. jvm前奏篇
  3. (转)LSTM神经网络介绍
  4. Leetcode 之 Mysql(day01)
  5. Vue结合后台导入导出Excel问题详解后续
  6. NuGet修改默认包保存的位置
  7. 关于字符串的格式化----format与%
  8. SpringBoot 日志系统
  9. 重启宝塔面板后提示-ModuleNotFoundError: No module named &#39;geventwebsocket&#39;
  10. layui多个时间选择器出现闪退问题