Отладка SIGBUS на x86 Linux

Что может вызвать SIGBUS (ошибка шины) в общем приложении x86 userland в Linux? Все обсуждения, которые я смог найти в интернете, касаются ошибок выравнивания памяти, которые, как я понимаю, на самом деле не относятся к x86.

(мой код работает на жеода, в случае, если есть какие-либо соответствующие особенности процессора там.)

7 ответов


вы можете получить SIGBUS из unaligned access, если вы включите ловушку unaligned access, но обычно это выключено на x86. Вы также можете получить его от доступа к памяти устройства, если есть какая-то ошибка.

лучше всего использовать отладчик для идентификации инструкции сбоя (SIGBUS является синхронным) и пытается увидеть, что он пытался сделать.


SIGBUS может произойти в Linux по нескольким причинам, кроме ошибок выравнивания памяти - например, если вы попытаетесь получить доступ к mmap область за пределами конца сопоставленного файла.

вы используете что-нибудь вроде mmap, области общей памяти или аналогичные?


SIGBUS на x86 (включая x86_64) Linux-редкий зверь. Он может появиться при попытке доступа после конца mmaped файл или некоторые другие ситуации, описанные POSIX.

но от аппаратных неисправностей нелегко получить SIGBUS. А именно, несогласованный доступ из любой инструкции-будь то SIMD или нет - обычно приводит к SIGSEGV. Переполнение стека приводит к SIGSEGV. Даже доступ к адресам не в канонической форме приводит к SIGSEGV. Все это из-за # GP поднимается, что почти всегда сопоставляется с SIGSEGV.

теперь, вот несколько способов получить SIGBUS из-за исключения CPU:

  1. включить бит переменного тока в EFLAGS, после этого сделайте unaligned доступ любой инструкцией чтения или записи памяти. См.эта дискуссия для сведения.

  2. сделать каноническое нарушение через регистр указателя стека (rsp или rbp), генерируя #SS. Вот пример для GCC (compile with gcc test.c -o test -masm=intel):

int main()
{
    __asm__("mov rbp,0x400000000000000\n"
            "mov rax,[rbp]\n"
            "ud2\n");
}

О да, есть еще один странный способ получить SIGBUS.

если ядро не может открыть страницу на кодовой странице из-за давления памяти (убийца OOM должен быть отключен) или сбой запроса ввода-вывода, SIGBUS.


Это было кратко упомянуто выше как "неудачный запрос ввода-вывода", но я немного расширю его.

частый случай, когда вы лениво выращиваете файл с помощью ftruncate, отображаете его в память, начинаете записывать данные, а затем заканчиваете пространство в вашей файловой системе. Физическое пространство для сопоставленного файла выделяется на сбоях страницы, если их не осталось, процесс получает SIGBUS.

Если вам нужно, чтобы ваше приложение правильно восстановилось от этой ошибки, имеет смысл явно зарезервируйте пространство перед mmap с помощью fallocate. Обработка ENOSPC в errno после вызова fallocate намного проще, чем работа с сигналами, особенно в многопоточном приложении.


распространенной причиной ошибки шины в x86 Linux является попытка разыменования чего-то, что на самом деле не является указателем или диким указателем. Например, неспособность инициализировать указатель или присвоение указателю произвольного целого числа, а затем попытка разыменования обычно приводит к ошибке сегментации или ошибке шины.

выравнивание применяется к x86. Даже если память на x86 байт-адресуема (поэтому вы можете иметь указатель char на любой адрес), если у вас есть например, указатель на 4-байтовое целое число, что указатель должен быть выровнен.

вы должны запустить свою программу в gdb и определить, какой доступ к указателю генерирует ошибку шины для диагностики проблемы.


Это немного в стороне от проторенного пути, но вы можете получить SIGBUS от несоосной нагрузки SSE2 (m128).