查找哪个汇编指令在未经debugging的情况下导致非法指令错误

在运行程序集时,我得到Illegal instruction错误。 有没有办法知道哪个指令导致错误,没有debugging,因为我正在运行的机器没有debugging器或任何开发系统。 换句话说,我在一台机器上编译并运行另一台机器。 我不能在我正在编译的机器上testing我的程序,因为它们不支持SSE4.2。 我正在运行程序的机器确实支持SSE4.2指令。

我认为这可能是因为我需要告诉汇编程序(YASM)识别SSE4.2指令,就像我们通过将-msse4.2标志传递给gcc一样。 或者你认为它不是原因? 任何想法如何告诉YASM识别SSE4.2指令?

也许我应该捕获SIGILL信号,然后解码SA_SIGINFO,看看程序做了什么样的非法操作。

实际上,你经常得到一个非法的指令错误,不是因为你的程序包含了一个非法的操作码,而是因为你的程序中有一个错误(例如缓冲区溢出),使你的程序跳转到一个随机地址,操作码的开始。

最近我因132退出状态码(128 + 4:程序被一个信号+非法指令信号中断)而遇到了崩溃。 以下是我如何找出导致崩溃的指令。

首先,我启用了核心转储:

 $ ulimit -c unlimited 

有趣的是,我运行二进制文件的文件夹包含一个名为core的文件夹。 我不得不告诉Linux将PID添加到核心转储:

 $ sudo sysctl -w kernel.core_uses_pid=1 

然后我运行我的程序,得到一个名为core.23650的核心。 我用gdb加载了二进制文件和核心文件。

 $ gdb program core.23650 

一旦我进入gdb,它显示了以下信息:

 Program terminated with signal SIGILL, Illegal instruction. #0 0x00007f58e9efd019 in ?? () 

这意味着我的程序因0x00007f58e9efd019地址内存的非法指令而0x00007f58e9efd019 。 然后我切换到asm布局来检查最后执行的指令:

 (gdb) layout asm >|0x7f58e9efd019 vpmaskmovd (%r8),%ymm15,%ymm0 |0x7f58e9efd01e vpmaskmovd %ymm0,%ymm15,(%rdi) |0x7f58e9efd023 add $0x4,%rdi |0x7f58e9efd027 add $0x0,%rdi 

这是导致vpmaskmovd导致错误。 显然,我试图在缺乏AVX2指令集支持的系统上运行针对AVX2架构的程序。

 $ cat /proc/cpuinfo | grep avx2 

最后,我确认vpmaskmovd是一个AVX2唯一的指令 。

如果你可以在这个系统上启用核心转储,那就运行这个程序,让它崩溃,然后把核心转储从目标机器上拖到你的开发机器上,并加载到一个为调试目标架构而构建的GDB中 – 事故发生的地方。 只需使用GDB的core命令将核心文件加载到调试器中即可。

  • 在目标上启用核心转储:

     ulimit -c unlimited 
  • 伪文件,控制核心文件将如何命名(cat这些看到当前的配置,写入他们改变配置):

     /proc/sys/kernel/core_pattern /proc/sys/kernel/core_uses_pid 

在我的系统上,一旦启用核心转储,崩溃程序将在工作目录中写一个简单名为“core”的文件。 这可能已经足够满足您的需要了,但改变核心转储文件的命名方式可以让您保留核心转储的历史记录(如果这是必要的话)。

对于手写汇编,我会怀疑堆栈管理问题导致无法返回。 编写一个调试打印输出例程,保存每个寄存器并在每个函数的顶部插入一个调用。

那么你会看到你有多远…

(顺便说一下,一个好的编辑器和编译器宏语法的良好理解是编写机器码时的救星。)

那么…你当然可以插入跟踪打印输出,所以你可以快速排除大部分的代码。 一旦你完成了,运行例如

 $ objdump --disassemble my-crashing-program | less 

然后跳转到例如你知道导致错误的功能,并阅读代码,寻找任何看起来很奇怪的东西。

我不完全确定objdump如何显示非法指令,但他们应该脱颖而出。