gdb 观察点未激活

gdb watchpoint not activated

考虑代码:

#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() 保存的 return 地址 rip at 0x7ffc176b2608 在两个断点之间从 0x7f8190fb92b1 突变为 0x2d28490fd6501

但是,使用 watch * 0x7ffc176b2608 和 运行 在 rip 的地址上设置一个观察点,可执行文件不会按预期在断点之间暂停执行。

怎么会这样?

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

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

您的 .gdbinit 中的这一行很可能是您遇到麻烦的根源:

set disable-randomization off

默认情况下,GDB 禁用地址 space 布局随机化 (ASLR)。这意味着 GDB 下的二进制文件从完全相同的地址开始,每次 运行s 时具有完全相同的堆栈指针。这是默认打开的 precisely 所以你可以在给定地址上设置观察点和断点,并让它在每个 运行.

上触发

通过关闭 disable-randomization,您要求 GDB 运行 您的二进制文件,就像在 GDB 之外 运行 一样,即启用 ASLR。现在,堆栈变量(以及您拥有的 PIE binary 的全局变量)的位置将从 运行 运行 更改,并且在给定堆栈地址上设置观察点只会随机且很少起作用。

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

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

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

@Mark Plotnick 的评论阐明了问题并包含了有关如何解决问题的建议。