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