Найти, какая инструкция сборки вызвала ошибку нелегальной инструкции без отладки
При запуске программы, написанной на сборке, я получаю ошибку Illegal instruction
. Есть ли способ узнать, какая команда вызывает ошибку, без отладки, потому что машина, на которой я запущена, не имеет отладчика или какой-либо системы разработки. Другими словами, я скомпилируюсь на одной машине и запускаю другую. Я не могу проверить свою программу на машине, которую я компилирую, потому что они не поддерживают SSE4.2. Машина, на которой я запускаю программу, все равно поддерживает инструкции SSE4.2.
Я думаю, это может быть потому, что я должен сказать ассемблеру (YASM), чтобы узнать инструкции SSE4.2, так же, как мы делаем с gcc, передав ему флаг -msse4.2
. Или вы считаете, что это не причина? Любая идея, как сказать YASM распознать инструкции SSE4.2?
Может быть, я должен захватить сигнал SIGILL, а затем декодировать SA_SIGINFO, чтобы узнать, какую незаконную операцию выполняет программа.
Ответы
Ответ 1
На самом деле часто вы получаете ошибку незаконной инструкции не потому, что ваша программа содержит недопустимый код операции, а потому, что в вашей программе есть ошибка (например, переполнение буфера), которая заставляет вашу программу переходить в случайный адрес с простыми данными или кодом но не в начале кода операции.
Ответ 2
Недавно я испытал сбой из-за кода статуса выхода 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
. Затем я переключился на макет 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.
Ответ 3
Если вы можете включить дампы ядра в этой системе, просто запустите программу, дайте ей сбой, затем вытащите основной дамп с целевой машины на вашу машину разработки и загрузите ее в GDB, созданный для отладки целевой архитектуры, - которая должна скажите, где именно произошел сбой. Просто используйте команду GDB core
для загрузки основного файла в отладчик.
-
Чтобы включить дампы ядра для цели:
ulimit -c unlimited
-
псевдофайлы, которые управляют тем, как будет называться основной файл (cat, чтобы увидеть текущую конфигурацию, написать им, чтобы изменить конфигурацию):
/proc/sys/kernel/core_pattern
/proc/sys/kernel/core_uses_pid
В моей системе, как только основные дампы будут включены, программа сбоев напишет файл, просто названный "ядром" в рабочем каталоге. Это, вероятно, достаточно хорошо для ваших целей, но изменение имени имени ядра дампа позволяет вам сохранить историю дампов ядра, если это необходимо (возможно, для более прерывистой проблемы).
Ответ 4
Для рукописной сборки я подозреваю, что проблема управления стеком приводит к возврату в никуда. Напишите процедуру отладки распечатки, которая сохраняет каждый регистр и вставляет вызов к ней в верхней части каждой функции.
Затем вы увидите, как далеко вы достигнете...
(BTW, хороший редактор и хорошее понимание синтаксиса макросов ассемблера являются lifesavers при написании машинного кода.)
Ответ 5
Ну... Конечно, вы можете вставить трассировочные отпечатки, чтобы вы могли быстро исключить большие области кода. Когда вы это сделаете, запустите, например,
$ objdump --disassemble my-crashing-program | less
Затем перейдите к, например, функция, которую вы знаете, вызывает ошибку, и читайте код, ища все, что выглядит странно.
Я не совсем уверен, как objdump
отображает незаконные инструкции, но они должны выделяться.