关键词: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") >> 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", (