关键词: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错误小谈

非法指令(Illegal Instruction)问题定位的更多相关文章

  1. Illegal instruction错误的定位---忽略编译期警告的代价

    在原计算机的linux c++程序可以正确跑起来,但是换了一台机器运行时出现致命错误,程序直接当掉,错误提示如下: Illegal instruction (core dumped) 造成改错的主要原 ...

  2. Dev 日志 | 一次 Segmentation Fault 和 GCC Illegal Instruction 编译问题排查 NebulaGraph

    摘要 笔者最近在重新整理和编译 Nebula Graph 的第三方依赖,选出两个比较有意思的问题给大家分享一下. Flex Segmentation Fault--Segmentation fault ...

  3. 链接了dpdk的进程启动core在 Illegal instruction

    失败后的core栈像下面这样: Program terminated with signal SIGILL, Illegal instruction. # 0x00000000036a3fdd in ...

  4. 服务器安装tensorflow导入模块报错Illegal instruction (core dumped)

    在ubuntu上安装tensorflow后导入模块显示Illegal instruction (core dumped) 服务器的版本是Ubuntu 16.04.5 降低版本,成功导入模块 pip3 ...

  5. Program received signal SIGILL, Illegal instruction

    Program received signal SIGILL, Illegal instruction 这个错误,发现是直接在printf 的%s中直接使用string类型,而没有使用c字符串格式造成 ...

  6. Illegal instruction 问题的解决方法

    写的程序在一些arm板子上可以运行, 可在一些板子上出现 Illegal instruction 这个一般是 arm指令不匹配的问题. 在编译参数中, 加上  -march=armv4t  就可以解决 ...

  7. Illegal instruction报错 c/c++

    报错 # ./agent Illegal instruction# 原因 myLog(log4cplus::INFO_LOG_LEVEL, g_p_debugLog, "sendLog ip ...

  8. k8s-nginx二进制报Illegal instruction (core dumped)

    1.环境 系统:CentOS 7.3 内核:x86 环境:虚拟机 2.问题 收到一个现场问题,k8s环境中nginx的pod都启动异常. #kubectl get pod |grep nginx ng ...

  9. Error response:/usr/bin/tf_serving_entrypoint.sh: line 3: 6 Illegal instruction (core dumped) ...

    用docker部署tensorflow-serving:gpu时,参照官方文档:https://tensorflow.google.cn/tfx/serving/docker 本应该是很简单的部署,没 ...

随机推荐

  1. JS---DOM---总结获取元素的方式

    总结获取元素的方式 1. 根据id属性的值获取元素,返回来的是一个元素对象 document.getElementById("id属性的值"); document.getEleme ...

  2. SQLserver、MySQL、ORCAL查询数据库、表、表中字段以及字段类型

    一.SQLServer命令 1.查询SQLServer中的每个数据库 SELECT * from sysdatabases 2.查询SQLServer中指定数据库的所有表名 select name f ...

  3. My97DatePicker-WdatePicker日历日期插件详细示例

    <!DOCTYPE html> <html> <head> <title>排行</title> <meta charset=" ...

  4. 为Dynamics 365 USD设置打开调试面板的自定义快捷键

    我是微软Dynamics 365 & Power Platform方面的工程师罗勇,也是2015年7月到2018年6月连续三年Dynamics CRM/Business Solutions方面 ...

  5. linux下安装make工具

    安装make工具 make工具依赖gcc ,automake,autoconf,libtool,make 这些安装包 可以一起安装 center os系统 运行如下命令yum install gcc ...

  6. SQL Server重建索引与重组索引会更新统计信息吗?

    在SQL Server中重建索引(Rebuild Index)与重组索引(Reorganize Index)会触发统计信息更新吗? 那么我们先来测试.验证一下: 我们以AdventureWorks20 ...

  7. 如何计算Data Guard环境中Redo所需的网络带宽传输 (Doc ID 736755.1)

    How To Calculate The Required Network Bandwidth Transfer Of Redo In Data Guard Environments (Doc ID ...

  8. SQL Server 索引分析开关

    set statistics io onset statistics profile on

  9. Yii2中$model->load($data)一直返回false问题

    上次使用$model->load()方法时一直返回false,数据添加不成功,这里记录一下: 出错代码: $data = [ 'name' => 'test', 'phone' => ...

  10. C++入门到理解阶段二核心篇(1)——c++面向对象概述、内存分析、引用

    1.c++内存分区模型 c++程序在运行的过程中,内存会被划分为以下四个分区 代码区:程序的所有程序的二进制代码,包括注释会被放到此区 全局区:存放静态变量.全局变量.常量(字符串常量和const修饰 ...