Может ли случиться так, что программа выдает ошибку сегмента и останавливается в первый раз, а во второй раз может выдать ошибку шины и выйти?
Да, даже для одной и той же ошибки: Вот серьезный, но упрощенный пример из macOS, который может вызвать как ошибку сегментации (SIGSEGV), так и ошибку шины (SIGBUS) по индексам вне границ массива в детерминированный путь. Упомянутый выше не выровненный доступ не является проблемой для macOS. (Этот пример не вызовет никакого SIGBUS, если он работает внутри отладчика, lldb
в моем случае!)
bus_segv.c:
#include <stdlib.h>
char array[10];
int main(int argc, char *argv[]) {
return array[atol(argv[1])];
}
Пример берет целое число из командной строки, которое служит индексом для массива. Это некоторые значения индекса (даже вне массива), которые не будут вызывать никакого сигнала. (Все приведенные значения зависят от стандартных размеров сегментов / секций. Я использовал clang-902.0.39.1 для создания двоичного файла на Mac OS X High Sierra 10.13.5, i5-4288U CPU @ 2.60GHz.)
Индекс выше 77791 и ниже -4128 вызовет ошибку сегментации (SIGSEGV). 24544 вызовет ошибку шины (SIGBUS). Вот полная карта:
$ ./bus_segv -4129
Segmentation fault: 11
$ ./bus_segv -4128
...
$ ./bus_segv 24543
$ ./bus_segv 24544
Bus error: 10
...
$ ./bus_segv 28639
Bus error: 10
$ ./bus_segv 28640
...
$ ./bus_segv 45023
$ ./bus_segv 45024
Bus error: 10
...
$ ./bus_segv 53215
Bus error: 10
$ ./bus_segv 53216
...
$ ./bus_segv 69599
$ ./bus_segv 69600
Bus error: 10
...
$ ./bus_segv 73695
Bus error: 10
$ ./bus_segv 73696
...
$ ./bus_segv 77791
$ ./bus_segv 77792
Segmentation fault: 11
Если вы посмотрите на разобранный код, вы увидите, что границы диапазонов с ошибками шины не так странны, как появляется индекс:
$ otool -tv bus_segv
bus_segv:
(__TEXT,__text) section
_main:
0000000100000f60 pushq %rbp
0000000100000f61 movq %rsp, %rbp
0000000100000f64 subq $0x10, %rsp
0000000100000f68 movl $0x0, -0x4(%rbp)
0000000100000f6f movl %edi, -0x8(%rbp)
0000000100000f72 movq %rsi, -0x10(%rbp)
0000000100000f76 movq -0x10(%rbp), %rsi
0000000100000f7a movq 0x8(%rsi), %rdi
0000000100000f7e callq 0x100000f94 ## symbol stub for: _atol
0000000100000f83 leaq 0x96(%rip), %rsi
0000000100000f8a movsbl (%rsi,%rax), %eax
0000000100000f8e addq $0x10, %rsp
0000000100000f92 popq %rbp
0000000100000f93 retq
К leaq 0x96(%rip), %rsi
rsi становится (ПК относительно
определен) адрес начального адреса массива:
rsi = 0x100000f8a + 0x96 = 0x100001020
rsi - 4128 = 0x100000000 (below segmentation fault)
rsi + 24544 = 0x100007000 (here and above bus error)
rsi + 28640 = 0x100008000 (below bus error)
rsi + 45024 = 0x10000c000 (here and above bus error)
rsi + 53216 = 0x10000e000 (below bus error)
rsi + 69600 = 0x100012000 (here and above bus error)
rsi + 73696 = 0x100013000 (below bus error)
rsi + 77792 = 0x100014000 (here and above segmentation fault)
lldb
, вероятно, настраивает процесс с различными ограничениями страницы. Я не смог воспроизвести ошибки шины в сеансе отладки. Таким образом, отладчик мог бы быть обходным путем для двоичных файлов с ошибками шины.
Andreas