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
的信号编号.
我试过砸栈:
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
的信号编号.