Основная причина ошибки сегментации - PullRequest
0 голосов
/ 18 ноября 2018

Фон

Я собрал qemu-system-x86_64.exe на компьютере с Windows, используя MSYS2 (x86_64), и я отлаживаю сегментациюошибка, которая возникает, когда я пытаюсь ее запустить.
На самом деле я не думаю, что проблема связана с QEMU или MSYS2, это проблема отладки ошибки сегментации и, возможно, неправильной генерации кода.

ОтладкаОшибка сегментации

Программа вылетает с ошибкой segmentation fault в самом начале.
При работе с GDB я обнаружил следующее:

Starting program: C:\msys64\home\Administrator\qemu\x86_64-softmmu\qemu-system-x86_64.exe
[New Thread 4656.0x1194]

Program received signal SIGSEGV, Segmentation fault.
0x00000000007d3254 in getpagesize () at util/oslib-win32.c:535
535     {

(gdb) bt
#0  0x00000000007d3254 in getpagesize () at util/oslib-win32.c:535
#1  0x000000000086dd39 in init_real_host_page_size () at util/pagesize.c:16
#2  0x00000000007ea1b2 in __do_global_ctors ()
    at C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/gccmain.c:67
#3  0x00000000007ea20f in __main ()
    at C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/gccmain.c:83
#4  0x000000000040137f in __tmainCRTStartup ()
    at C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:329
#5  0x00000000004014db in WinMainCRTStartup ()
    at C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:195

Это странно.
Сбой программы при запуске __do_global_ctors и вызове init_real_host_page_size(), который вызывает getpagesize().Это действительно простые функции:

uintptr_t qemu_real_host_page_size;
intptr_t qemu_real_host_page_mask;

static void __attribute__((constructor)) init_real_host_page_size(void)
{
    qemu_real_host_page_size = getpagesize();
    qemu_real_host_page_mask = -(intptr_t)qemu_real_host_page_size;
}

...

int getpagesize(void)
{
    SYSTEM_INFO system_info;

    GetSystemInfo(&system_info);
    return system_info.dwPageSize;
}

getpagesize() аварийно завершает работу в самом начале функции, прежде чем она даже вызывает GetSystemInfo.
Вот разборка этого фрагмента кода и значений регистров:

(gdb) disassem
Dump of assembler code for function getpagesize:
   0x00000000007d3250 <+0>:     sub    $0x68,%rsp
=> 0x00000000007d3254 <+4>:     mov    %fs:0x0,%rax
   0x00000000007d325d <+13>:    mov    %rax,0x58(%rsp)
   0x00000000007d3262 <+18>:    xor    %eax,%eax
   0x00000000007d3264 <+20>:    lea    0x20(%rsp),%rcx
   0x00000000007d3269 <+25>:    callq  *0x68e8b9(%rip)        # 0xe61b28 <__imp_GetSystemInfo>
   0x00000000007d326f <+31>:    mov    0x24(%rsp),%eax
   0x00000000007d3273 <+35>:    mov    0x58(%rsp),%rdx
   0x00000000007d3278 <+40>:    xor    %fs:0x0,%rdx
   0x00000000007d3281 <+49>:    jne    0x7d3288 <getpagesize+56>
   0x00000000007d3283 <+51>:    add    $0x68,%rsp
   0x00000000007d3287 <+55>:    retq
   0x00000000007d3288 <+56>:    callq  0x85bde0 <__stack_chk_fail>
   0x00000000007d328d <+61>:    nop
End of assembler dump.
(gdb) info registers
rax            0x6f4b868           116701288
rbx            0x86ec10            8842256
rcx            0x6f4b8b8           116701368
rdx            0xe5a780            15050624
rsi            0x86e220            8839712
rdi            0x6f4ad50           116698448
rbp            0x6f4ad10           0x6f4ad10
rsp            0x22fd80            0x22fd80
r8             0x0                 0
r9             0x0                 0
r10            0x5000016b          1342177643
r11            0x22f9d8            2292184
r12            0x0                 0
r13            0x10                16
r14            0x0                 0
r15            0x0                 0
rip            0x7d3254            0x7d3254 <getpagesize+4>
eflags         0x10202             [ IF RF ]
cs             0x33                51
ss             0x2b                43
ds             0x2b                43
es             0x2b                43
fs             0x53                83
gs             0x2b                43

Похоже, что-то не так с доступом к памяти mov %fs:0x0,%rax.
Кто устанавливает FS на 83?

(gdb) starti
Starting program: C:\msys64\home\Administrator\qemu\x86_64-softmmu\qemu-system-x86_64.exe
[New Thread 3508.0x14b0]

Program stopped.
0x00000000778b6fb1 in ntdll!CsrSetPriorityClass ()
   from C:\Windows\SYSTEM32\ntdll.dll
(gdb) p $fs
$1 = 83
(gdb) watch $fs
Watchpoint 1: $fs
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x00000000007d3254 in getpagesize () at util/oslib-win32.c:535
535     {

Никто не устанавливает FS!

Вопросы

  • Код, сгенерированный GCC, который использует неинициализированный регистр.Что может вызвать это?Был ли какой-то код инициализации, который должен был быть запущен, но не запустился?
  • Есть идеи, как можно отладить эту проблему?

1 Ответ

0 голосов
/ 19 ноября 2018

FS - это сегментный регистр x86. Обычно они не устанавливаются пользовательской программой, а устанавливаются ОС или библиотеками времени выполнения для различных специальных целей. Например, в Windows x86-64 GS используется для указания на блок данных для каждого потока: https://en.wikipedia.org/wiki/Win32_Thread_Information_Block (и FS не используется).

В этом случае проблема заключается в используемой вами компиляторе GCC 8: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86832

В некоторых ситуациях этот компилятор генерирует код, который предполагает, что FS был настроен для "собственного TLS", что неправильно, поскольку MINGW не поддерживает "собственный TLS", а для FS не установлено ничего полезного.

Обходной путь - избегать компиляции с опцией -fstack-protector-strong compiler. Для QEMU вы можете сделать это, передав конфигурационный флаг --disable-stack-protector.

(PS: если вы хотите узнать, как я идентифицировал причину этого segfault: я погуглил для 'qemu-devel sigsegv getpagesize', который вызывает поток в списке рассылки, где кто-то другой столкнулся и сообщил об ошибке, проблема был диагностирован и найдена ссылка на ошибку GCC.)

...