【问题标题】:gdb watchpoint not activatedgdb 观察点未激活
【发布时间】:2017-09-11 16:03:47
【问题描述】:

考虑代码:

#include <stdio.h>
#include <stdlib.h>

int update (int *arr, int size);

#define SIZE 10

int main() { // <---------------------- Breakpoint 1
  int x[SIZE];

  // Initialize array
  for (int c = 0 ; c < SIZE ; c++) {
    x[c] = c * 2;
  }

  // Do some random updates to an array
  update((int*) &x, SIZE);

  // Print the elements
  for (int c = 0 ; c < SIZE ; c++) {
    printf("%d\n", x[c]);
  }

  return EXIT_SUCCESS;
} //            <----------------------Breakpoint 2

int update (int *arr, int size) {
  for (int i = 0 ; i < size ; i++) {
    arr[i] += i;
    update(arr+i, size-1);
  }
  return 1;
}

在断点 1 处运行 info frame 的结果:

Stack level 0, frame at 0x7ffc176b2610:
 rip = 0x56434b0d76b8 in main (array.c:12); saved rip = 0x7f8190fb92b1
 source language c.
 Arglist at 0x7ffc176b2600, args:
 Locals at 0x7ffc176b2600, Previous frame's sp is 0x7ffc176b2610
 Saved registers:
  rbp at 0x7ffc176b2600, rip at 0x7ffc176b2608

在断点 2 处运行 info frame 的结果:

Stack level 0, frame at 0x7ffc176b2610:
 rip = 0x56434b0d771a in main (array.c:24); saved rip = 0x2d28490fd6501
 source language c.
 Arglist at 0x7ffc176b2600, args:
 Locals at 0x7ffc176b2600, Previous frame's sp is 0x7ffc176b2610
 Saved registers:
  rbp at 0x7ffc176b2600, rip at 0x7ffc176b2608

我们看到main()保存的返回地址rip at 0x7ffc176b2608在两个断点之间从0x7f8190fb92b1突变为0x2d28490fd6501

但是,使用watch * 0x7ffc176b2608rip 的地址上设置观察点并重新运行可执行文件不会按预期暂停断点之间的执行。

这怎么可能?

------------编辑-----------

disassemble /s main的输出:

Dump of assembler code for function main:
array.c:
8   int main() {
   0x000056434b0d76b0 <+0>: push   rbp
   0x000056434b0d76b1 <+1>: mov    rbp,rsp
   0x000056434b0d76b4 <+4>: sub    rsp,0x30

9     int x[SIZE];
10  
11    // Initialize array
12    for (int c = 0 ; c < SIZE ; c++) {
   0x000056434b0d76b8 <+8>: mov    DWORD PTR [rbp-0x4],0x0
   0x000056434b0d76bf <+15>:    jmp    0x56434b0d76d4 <main+36>

13      x[c] = c * 2;
   0x000056434b0d76c1 <+17>:    mov    eax,DWORD PTR [rbp-0x4]
   0x000056434b0d76c4 <+20>:    lea    edx,[rax+rax*1]
   0x000056434b0d76c7 <+23>:    mov    eax,DWORD PTR [rbp-0x4]
   0x000056434b0d76ca <+26>:    cdqe   
   0x000056434b0d76cc <+28>:    mov    DWORD PTR [rbp+rax*4-0x30],edx

12    for (int c = 0 ; c < SIZE ; c++) {
   0x000056434b0d76d0 <+32>:    add    DWORD PTR [rbp-0x4],0x1
   0x000056434b0d76d4 <+36>:    cmp    DWORD PTR [rbp-0x4],0x9
   0x000056434b0d76d8 <+40>:    jle    0x56434b0d76c1 <main+17>

14    }
15  
16    // Do some random updates to an array
17    update((int*) &x, SIZE);
   0x000056434b0d76da <+42>:    lea    rax,[rbp-0x30]
   0x000056434b0d76de <+46>:    mov    esi,0xa
   0x000056434b0d76e3 <+51>:    mov    rdi,rax
   0x000056434b0d76e6 <+54>:    call   0x56434b0d7721 <update>

18  
19    // Print the elements
20    for (int c = 0 ; c < SIZE ; c++) {
   0x000056434b0d76eb <+59>:    mov    DWORD PTR [rbp-0x8],0x0
   0x000056434b0d76f2 <+66>:    jmp    0x56434b0d7714 <main+100>

21      printf("%d\n", x[c]);
   0x000056434b0d76f4 <+68>:    mov    eax,DWORD PTR [rbp-0x8]
   0x000056434b0d76f7 <+71>:    cdqe   
   0x000056434b0d76f9 <+73>:    mov    eax,DWORD PTR [rbp+rax*4-0x30]
   0x000056434b0d76fd <+77>:    mov    esi,eax
   0x000056434b0d76ff <+79>:    lea    rdi,[rip+0x12e]        # 0x56434b0d7834
   0x000056434b0d7706 <+86>:    mov    eax,0x0
   0x000056434b0d770b <+91>:    call   0x56434b0d7560 <printf@plt>

20    for (int c = 0 ; c < SIZE ; c++) {
   0x000056434b0d7710 <+96>:    add    DWORD PTR [rbp-0x8],0x1
   0x000056434b0d7714 <+100>:   cmp    DWORD PTR [rbp-0x8],0x9
   0x000056434b0d7718 <+104>:   jle    0x56434b0d76f4 <main+68>

22    }
23  
24    return EXIT_SUCCESS;
=> 0x000056434b0d771a <+106>:   mov    eax,0x0

25  }
   0x000056434b0d771f <+111>:   leave  
   0x000056434b0d7720 <+112>:   ret    
End of assembler dump.

disassemble /s update 的输出:

Dump of assembler code for function update:
array.c:
27  int update (int *arr, int size) {
   0x000056434b0d7721 <+0>: push   rbp
   0x000056434b0d7722 <+1>: mov    rbp,rsp
   0x000056434b0d7725 <+4>: sub    rsp,0x20
   0x000056434b0d7729 <+8>: mov    QWORD PTR [rbp-0x18],rdi
   0x000056434b0d772d <+12>:    mov    DWORD PTR [rbp-0x1c],esi

28    for (int i = 0 ; i < size ; i++) {
   0x000056434b0d7730 <+15>:    mov    DWORD PTR [rbp-0x4],0x0
   0x000056434b0d7737 <+22>:    jmp    0x56434b0d7793 <update+114>

29      arr[i] += i;
   0x000056434b0d7739 <+24>:    mov    eax,DWORD PTR [rbp-0x4]
   0x000056434b0d773c <+27>:    cdqe   
   0x000056434b0d773e <+29>:    lea    rdx,[rax*4+0x0]
   0x000056434b0d7746 <+37>:    mov    rax,QWORD PTR [rbp-0x18]
   0x000056434b0d774a <+41>:    add    rax,rdx
   0x000056434b0d774d <+44>:    mov    edx,DWORD PTR [rbp-0x4]
   0x000056434b0d7750 <+47>:    movsxd rdx,edx
   0x000056434b0d7753 <+50>:    lea    rcx,[rdx*4+0x0]
   0x000056434b0d775b <+58>:    mov    rdx,QWORD PTR [rbp-0x18]
   0x000056434b0d775f <+62>:    add    rdx,rcx
   0x000056434b0d7762 <+65>:    mov    ecx,DWORD PTR [rdx]
   0x000056434b0d7764 <+67>:    mov    edx,DWORD PTR [rbp-0x4]
   0x000056434b0d7767 <+70>:    add    edx,ecx
   0x000056434b0d7769 <+72>:    mov    DWORD PTR [rax],edx

30      update(arr+i, size-1);
   0x000056434b0d776b <+74>:    mov    eax,DWORD PTR [rbp-0x1c]
   0x000056434b0d776e <+77>:    lea    edx,[rax-0x1]
   0x000056434b0d7771 <+80>:    mov    eax,DWORD PTR [rbp-0x4]
   0x000056434b0d7774 <+83>:    cdqe   
   0x000056434b0d7776 <+85>:    lea    rcx,[rax*4+0x0]
   0x000056434b0d777e <+93>:    mov    rax,QWORD PTR [rbp-0x18]
   0x000056434b0d7782 <+97>:    add    rax,rcx
   0x000056434b0d7785 <+100>:   mov    esi,edx
   0x000056434b0d7787 <+102>:   mov    rdi,rax
   0x000056434b0d778a <+105>:   call   0x56434b0d7721 <update>

28    for (int i = 0 ; i < size ; i++) {
   0x000056434b0d778f <+110>:   add    DWORD PTR [rbp-0x4],0x1
   0x000056434b0d7793 <+114>:   mov    eax,DWORD PTR [rbp-0x4]
   0x000056434b0d7796 <+117>:   cmp    eax,DWORD PTR [rbp-0x1c]
   0x000056434b0d7799 <+120>:   jl     0x56434b0d7739 <update+24>

31    }
32    return 1;
   0x000056434b0d779b <+122>:   mov    eax,0x1

33  }
   0x000056434b0d77a0 <+127>:   leave  
   0x000056434b0d77a1 <+128>:   ret    
End of assembler dump.

~/.gdbinit的内容

# Security
set auto-load safe-path /

# Misc
set disassembly-flavor intel
set disable-randomization off
set pagination off
set follow-fork-mode child

# History
set history filename ~/.gdbhistory
set history save
set history expansion

disp/10i $pc

handle SIGXCPU SIG33 SIG35 SIGPWR nostop noprint

set tui enable

【问题讨论】:

  • 代码是如何编译的?代码是如何链接的?你使用了gcc 编译器吗?您是否使用了-g(或更好的-ggdb)选项?给gdb的命令是什么?
  • @user3629249 和 gcc-6 -std=c11 -Wall -Werror -Wextra -pedantic -g array.c。给出的命令在文本中描述(b mainb 25 用于断点)。
  • 你能展示反汇编(disas /s in gdb)吗? gcc 倾向于在每个平台上配置不同的默认选项,这会影响代码生成和数据布局。当我运行你的程序时,我在观察点上得到了大量的命中,因为对 update(arr+i, size-1); 的调用最终破坏了堆栈的一部分。 (应该是update(arr+i, size-i);?)
  • @MarkPlotnick 我为main()update() 添加了反汇编。
  • @Einar 你的~/.gdbinit 有什么东西吗?您能否展示您的整个 GDB 会话,包括版本横幅? (我也无法重现缺少观察点触发的情况。)

标签: c gdb watchpoint


【解决方案1】:

.gdbinit中的这行很可能是你麻烦的根源:

set disable-randomization off

默认情况下,GDB 禁用地址空间布局随机化 (ASLR)。这意味着 GDB 下的二进制文件从完全相同的地址开始,每次运行时都使用完全相同的堆栈指针。这是默认开启的

通过关闭disable-randomization,您要求 GDB 以与在 GDB 外部运行相同的方式运行您的二进制文件,即启用 ASLR。现在堆栈变量的位置(以及您拥有的 PIE binary 的全局变量)将随着运行而变化,并且在给定的堆栈地址上设置观察点只会随机且很少起作用。

您可以通过多次发出info framerun 来确认这就是原因。您会观察到寄存器保存的位置在运行之间发生了变化。

TL;DR:不要将您不完全理解的设置放入您的.gdbinit

【讨论】:

  • OP 询问代码的单次运行,所以这不是问题的根源。有关问题的实际来源,请参阅@Mark Plotnick 的评论。
  • 非常感谢。实际上我把set disable-randomization off 放在那里是为了禁用ASLR,但是当时双重否定一定让我感到困惑。我还没有完全理解。即使地址在运行之间发生变化,我还是专门用info frame 探测rip 的地址,并在该地址上放置一个观察点。这个地址上的内容确实被修改了,所以我还是觉得应该触发watchpoint,不管这个地址是不是随机选择的。
  • 实际上它完全按照预期运行。我必须在最初设置观察点后使用run 而不是继续。
【解决方案2】:

代码的问题是堆栈的破坏。

@Mark Plotnick 的评论澄清了问题,并提出了如何解决问题的建议。

【讨论】:

  • 这并没有回答为什么观察点没有触发的问题。
  • @EmployedRussian,实际上,gdb 的命令是设置断点,而不是观察点。在我的电脑上,它可以完美运行。但是,我建议在return 语句而不是最后的右大括号'}' 上设置断点,因为执行已经在return 语句处退出程序。 (对我来说,它有效,因为两个语句都在同一个地址。
  • 实际上,问题是关于观察点的:“但是,使用 watch * 0x7ffc176b2608 在 rip 的地址上设置观察点并重新运行可执行文件不会按预期暂停断点之间的执行。”断点对于问题的要点是次要的。
  • @EmployedRussian,OP 表示他们使用“b”作为命令。这设置了一个断点。使用 watchawatchrwatch 命令设置观察点
  • 我不确定“使用watch * 0x7ffc176b2608 设置观察点 ...”的哪一部分您不理解。无论如何,这里的进一步讨论是没有意义的,我会避免它。
猜你喜欢
  • 1970-01-01
  • 2011-11-15
  • 2019-01-07
  • 1970-01-01
  • 2011-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多