【问题标题】:Root cause a segmentation fault根本原因导致分段错误
【发布时间】:2018-11-18 17:12:16
【问题描述】:

背景

我已经使用MSYS2 (x86_64) 在 Windows 机器上构建了qemu-system-x86_64.exe,并且我正在调试尝试运行它时发生的分段错误。
其实我不认为这个问题与 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 生成的代码使用未初始化的寄存器。是什么原因造成的?是否有一些应该运行但没有运行的初始化代码?
  • 有什么想法可以进一步调试此问题吗?

【问题讨论】:

  • 在代码中的每个主要函数处设置一个断点(br 指令)。在进入运行之前包括'main'( r 指令)。在每个断点检查 fs 以确定该变量的确切设置时间
  • gdb 输出的发布跟踪显示代码在停止之前运行了一段时间。所以fs的设置早就过去了
  • @user3629249 请查看 gdb 输出。首先我发出一个starti 命令,它开始运行代码但在第一条指令处停止。接下来,我为 FS 设置了一个观察点。从这一刻起,对 FS 的每次更改都会停止运行。然后我发出c 命令。 gdb 运行代码但检查正在运行的每条指令 是否更改了 FS。它需要一段时间才能到达分段错误。 gdb 直到分段错误才停止在观察点上的事实证明 FS 从运行开始到出现错误都没有改变
  • 寄存器fs 是段寄存器之一。它是在程序开始运行之前设置的

标签: c gcc gdb qemu msys2


【解决方案1】:

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 已设置为“native TLS”,这是错误的,因为 MINGW 不支持“native TLS”并且 FS 未设置为任何有用的值。

解决方法是避免使用 -fstack-protector-strong 编译器选项进行编译。对于 QEMU,您可以通过配置标志 --disable-stack-protector 来做到这一点。

(PS:如果你想知道我是如何确定这个段错误的原因的:我在谷歌上搜索了“qemu-devel sigsegv getpagesize”,它打开了一个邮件列表线程,其他人遇到并报告了这个错误,问题被诊断出来并找到了 GCC 错误的链接。)

【讨论】:

  • 你是对的,当然。我应该花更多时间在谷歌上搜索答案而不是调试它......谢谢。
  • 你是比我优秀的 Google 员工。我不知道怎么找到那个。谢谢。
猜你喜欢
  • 1970-01-01
  • 2020-11-07
  • 2012-06-19
  • 2021-08-25
  • 1970-01-01
  • 1970-01-01
  • 2011-07-12
  • 1970-01-01
  • 2018-09-14
相关资源
最近更新 更多