第二部分很容易解释(正如 Flortify 正确说明的那样):
GDB 显示原始内存内容,而不是断点“字节”。在默认模式下,它实际上甚至在调试器挂起时删除断点并在继续之前重新插入断点。用户通常希望看到他们的代码,而不是用于断点的奇怪修改指令。
使用您的 C 代码,您错过了几个字节的断点。 GDB 在 function prologue 之后设置断点,因为函数序言通常不是 gdb 用户希望看到的。因此,如果您将 break 设置为 foo,则实际断点通常位于之后的几个字节(取决于序言代码本身,它依赖于函数,因为它可能需要也可能不需要保存堆栈指针、帧指针等)。但很容易检查。我使用了这段代码:
#include <stdio.h>
int main()
{
int i,j;
unsigned char *p = (unsigned char*)main;
for (j=0; j<4; j++) {
printf("%p: ",p);
for (i=0; i<16; i++)
printf("%.2x ", *p++);
printf("\n");
}
return 0;
}
如果我们单独运行这个程序,它会打印:
0x40057d: 55 48 89 e5 48 83 ec 10 48 c7 45 f8 7d 05 40 00
0x40058d:c7 45 f4 00 00 00 00 eb 5a 48 8b 45 f8 48 89 c6
0x40059d: bf 84 06 40 00 b8 00 00 00 00 e8 b4 fe ff ff c7
0x4005ad: 45 f0 00 00 00 00 eb 27 48 8b 45 f8 48 8d 50 01
现在我们在 gdb 中运行它(输出重新格式化为 SO)。
(gdb) 中断主要
0x400585 处的断点 1:文件 ../bp.c,第 6 行。
(gdb) 信息中断
Num 类型 Disp Enb 地址 什么
1 个断点将 y 0x0000000000400585 保留在 ../bp.c:6 处
(gdb) disas/r main,+32
从 0x40057d 到 0x40059d 的汇编代码转储:
0x000000000040057d (main+0): 55 push %rbp
0x000000000040057e (main+1): 48 89 e5 mov %rsp,%rbp
0x0000000000400581 (main+4): 48 83 ec 10 sub $0x10,%rsp
0x0000000000400585 (main+8): 48 c7 45 f8 7d 05 40 00 movq $0x40057d,-0x8(%rbp)
0x000000000040058d (main+16): c7 45 f4 00 00 00 00 movl $0x0,-0xc(%rbp)
0x0000000000400594 (main+23): eb 5a jmp 0x4005f0
0x0000000000400596 (main+25): 48 8b 45 f8 mov -0x8(%rbp),%rax
0x000000000040059a (main+29): 48 89 c6 mov %rax,%rsi
汇编程序转储结束。
通过我们验证,该程序正在打印正确的字节。但这也表明断点已插入 0x400585(即函数序言之后),而不是函数的第一条指令。
如果我们现在在 gdb 下运行程序(使用 run),然后在断点命中后“继续”,我们会得到以下输出:
(gdb) 续
继续。
0x40057d: 55 48 89 e5 48 83 ec 10 cc c7 45 f8 7d 05 40 00
0x40058d:c7 45 f4 00 00 00 00 eb 5a 48 8b 45 f8 48 89 c6
0x40059d: bf 84 06 40 00 b8 00 00 00 00 e8 b4 fe ff ff c7
0x4005ad: 45 f0 00 00 00 00 eb 27 48 8b 45 f8 48 8d 50 01
现在显示 0xcc 正在将地址 9 字节打印到 main 中。