gdb 中的分段错误是否显示物理地址或虚拟地址?

Does a segmentation fault in gdb show the physical or virtual address?

我试过砸栈:

int main (void) {

    int ar[5] = {1,2,3,4,5};
    for(int i =0; i<255 ; i++)
        ar[i] = 10;

    return 0;
}

gcc -fno-stack-protector somefile.c。第一个问题:为什么有(SIGABRT)和没有(SIGSEGV)保护器的故障有区别,当两者都在访问非法内存时(我认为应该有相同的故障)。 第二个当 objdump:

0000000000001125 <main>:
    1125:   55                      push   %rbp
    1126:   48 89 e5                mov    %rsp,%rbp
    1129:   c7 45 e0 01 00 00 00    movl   [=11=]x1,-0x20(%rbp)
...

主地址的开头是虚拟的0000000000001125

但是在没有保护器的情况下编译时:

Program received signal SIGSEGV, Segmentation fault.
0x0000000af7be5b6b in ?? ()

地址(0x0000000af7be5b6b)是什么?虚拟的,物理的?我在反汇编(如上图)文件中看不到它,那么地址来自哪里?

编辑: 带有保护器(因此故障 SIGABRT),我也不理解的输出:

Program received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50

什么是__GI_raise宏?括号 sig=sig@entry=6 中的内容是什么,gcc 是否为错误添加了 table,或者这些来自链接器的标记是什么?

why is there difference with fault with (SIGABRT) and without (SIGSEGV) protector, when both are accessing ilegal memory (so should have the same fault, i think).

当你在没有堆栈保护器的情况下编译时,你最终只会覆盖一些你不允许的内存部分,并且程序会被操作系统通过SIGSEGV信号。

但是,当您使用堆栈保护程序进行编译时,libc 可以检测到错误,在这种情况下会调用 __stack_chk_fail() 函数。你可以通过objdump:

看到这个
 72d:   b8 00 00 00 00          mov    eax,0x0
 732:   48 8b 55 f8             mov    rdx,QWORD PTR [rbp-0x8]
 736:   64 48 33 14 25 28 00    xor    rdx,QWORD PTR fs:0x28
 73d:   00 00
 73f:   74 05                   je     746 <main+0x76>
 741:   e8 3a fe ff ff          call   580 <__stack_chk_fail@plt>
 746:   c9                      leave
 747:   c3                      ret

__stack_chk_fail() 函数然后调用 __fortify_fail_abort(), which calls __libc_message() printing the error message and finally executing abort(), which sends a SIGABRT signal to the process through raise(),终止它。

The start of main address is virtual 0000000000001125

错误。那是 不是 虚拟地址,它只是二进制文件中的 offset,这意味着您看到的代码从字节 0x1125 开始在二进制文件本身。然后执行二进制文件时,会为程序创建一个虚拟内存区域,该区域从某个(通常是随机的)基本虚拟地址开始。然后,基本虚拟地址将确定 main() 的位置和其他所有内容。例如 main() 将在 base_virtual_addr + 0x1125。您无法 确定您的程序在 RAM 中加载的真实物理地址,只有操作系统(即内核)知道这一点,而用户 space 程序不需要。

你可以在GDB下使用命令info proc mappings查看程序在运行时的虚拟内存映射,结果如下:

(gdb) info proc mappings
process 11084
Mapped address spaces:

          Start Addr           End Addr       Size     Offset objfile
      0x555555554000     0x555555555000     0x1000        0x0 /home/marco/Desktop/a.out
      0x555555754000     0x555555755000     0x1000        0x0 /home/marco/Desktop/a.out
      0x555555755000     0x555555756000     0x1000     0x1000 /home/marco/Desktop/a.out
      0x7ffff7a3a000     0x7ffff7bcf000   0x195000        0x0 /lib/x86_64-linux-gnu/libc-2.24.so
      0x7ffff7bcf000     0x7ffff7dcf000   0x200000   0x195000 /lib/x86_64-linux-gnu/libc-2.24.so
      0x7ffff7dcf000     0x7ffff7dd3000     0x4000   0x195000 /lib/x86_64-linux-gnu/libc-2.24.so
      0x7ffff7dd3000     0x7ffff7dd5000     0x2000   0x199000 /lib/x86_64-linux-gnu/libc-2.24.so
      0x7ffff7dd5000     0x7ffff7dd9000     0x4000        0x0
      0x7ffff7dd9000     0x7ffff7dfc000    0x23000        0x0 /lib/x86_64-linux-gnu/ld-2.24.so
      0x7ffff7fcf000     0x7ffff7fd1000     0x2000        0x0
      0x7ffff7ff8000     0x7ffff7ffa000     0x2000        0x0 [vvar]
      0x7ffff7ffa000     0x7ffff7ffc000     0x2000        0x0 [vdso]
      0x7ffff7ffc000     0x7ffff7ffd000     0x1000    0x23000 /lib/x86_64-linux-gnu/ld-2.24.so
      0x7ffff7ffd000     0x7ffff7ffe000     0x1000    0x24000 /lib/x86_64-linux-gnu/ld-2.24.so
      0x7ffff7ffe000     0x7ffff7fff000     0x1000        0x0
      0x7ffffffde000     0x7ffffffff000    0x21000        0x0 [stack]
  0xffffffffff600000 0xffffffffff601000     0x1000        0x0 [vsyscall]

在这种情况下,您可以看到我的程序(/home/marco/Desktop/a.out)从虚拟基地址0x555555554000开始。

What is __GI_raise a macro? and what is in the parenthesis sig=sig@entry=6, does gcc add a table for faults, or what are these marks from linker?

__GI_raise()raise() 函数的一个实现。记住?我刚刚在谈到堆栈保护器时提到过它。在你的例子中,gdb 在程序被 raise(SIGABRT) 杀死后停止,并显示程序死亡的确切点,它在 libc 用来传递的 __GI_raise() 内部函数中信号。

字符串 sig=sig@entry=6 只是 GDB 告诉您调用该函数的唯一参数设置为 6 的好方法,这是 SIGABRT 的信号编号.