根本原因是分段错误
Root cause a segmentation fault
背景
我已经构建了 qemu-system-x86_64.exe on a Windows machine using 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 [=12=]x68,%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 [=12=]x68,%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
= 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 生成的代码使用了未初始化的寄存器。是什么原因造成的?是否有一些初始化代码应该有 运行 但没有?
- 关于如何进一步调试此问题的任何想法?
FS 是一个 x86 段寄存器。这些通常不是由用户程序设置的,而是由 OS 或运行时库设置的,用于各种特殊目的。例如,在 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
在某些情况下,此编译器生成的代码假定已为 "native TLS" 设置了 FS,这是错误的,因为 MINGW 不支持 "native TLS" 且 FS 未设置为任何有用的值。
解决方法是避免使用 -fstack-protector-strong 编译器选项进行编译。对于 QEMU,您可以通过配置标志 --disable-stack-protector 来做到这一点。
(PS:如果你想知道我是如何确定这个段错误的原因的:我在谷歌上搜索了 'qemu-devel sigsegv getpagesize',它会显示一个邮件列表线程,其他人 运行进入并报告了错误,问题被诊断出来,并且 link 找到了 GCC 错误。)
背景
我已经构建了 qemu-system-x86_64.exe on a Windows machine using 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 [=12=]x68,%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 [=12=]x68,%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
= 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 生成的代码使用了未初始化的寄存器。是什么原因造成的?是否有一些初始化代码应该有 运行 但没有?
- 关于如何进一步调试此问题的任何想法?
FS 是一个 x86 段寄存器。这些通常不是由用户程序设置的,而是由 OS 或运行时库设置的,用于各种特殊目的。例如,在 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
在某些情况下,此编译器生成的代码假定已为 "native TLS" 设置了 FS,这是错误的,因为 MINGW 不支持 "native TLS" 且 FS 未设置为任何有用的值。
解决方法是避免使用 -fstack-protector-strong 编译器选项进行编译。对于 QEMU,您可以通过配置标志 --disable-stack-protector 来做到这一点。
(PS:如果你想知道我是如何确定这个段错误的原因的:我在谷歌上搜索了 'qemu-devel sigsegv getpagesize',它会显示一个邮件列表线程,其他人 运行进入并报告了错误,问题被诊断出来,并且 link 找到了 GCC 错误。)