GDB调试器中是否可以“跳过”或“跳转”?

15

在GDB调试时,是否可能跳转至代码/可执行文件中的某个位置或地址?

假设我有类似以下内容的代码:

int main()
{
  caller_f1() {  

   f1();  // breakpoint  
   f2() } // want to skip f2() and jump 

  caller_f2() { // jump to this this location ??       
   f1();  
   f2(); }  
}

2
@CiroSantilli,这个问题已经四年了,请放手吧。谢谢! - newprint
2个回答

23

使用jump(简写:j)以在新地址上恢复执行:

jump LINENUM
jump *ADDRESS

GDB手册建议在跳转之前使用tbreak(临时断点)。

行号可以是任何linespec 表达式,例如下一行的+1

参见@gospes's answer中的相关问题,其中有一个方便的skip宏,可以实现这一点。


仅当代码未经优化(-O0)且仅限于当前函数时,使用jump才是“安全的”。 它修改程序计数器; 它不会更改任何其他寄存器或内存。

只有gcc -O0将每个源语句(或行)编译成一个独立的指令块,从内存加载变量值并存储结果。 这使您可以在任何断点处使用调试器修改变量值,并使机器代码中的行之间的jump操作类似于在C源代码中跳转。 这也是为什么-O0会生成如此缓慢的代码的部分原因:编译器不仅不花时间进行优化,而且需要制作缓慢的代码以在每个语句后溢出 / 重新加载所有内容,以支持变量和甚至程序计数器的异步修改。 (存储/重新加载延迟在典型的x86上约为5个周期,因此1个周期的add-O0构建中需要6个周期)。

gcc的手册建议使用-Og进行通常的编辑-编译-调试周期,但即使是这种轻度的优化水平也会破坏jump和异步修改变量。 如果您不想在调试时这样做,则可以选择它,尤其是对于-O0运行速度非常慢的项目。


要将程序计数器/指令指针设置为新地址而不恢复,请使用以下方法:

set $pc = 0x4005a5

从反汇编窗口 (layout asm / layout reg) 复制/粘贴地址。

这相当于使用 tbreak + jump 命令,但您不能使用行号,只能使用指令地址。(并且您不会收到警告和确认请求,以跳出当前函数)。

然后您可以从那里使用 stepi 命令。 在目标架构中,$pc 是通用的gdb名称,代表着真正被称为 RPI 的寄存器,例如 x86-64。(请参见 标签wiki底部的asm调试技巧)。


很好的解释。有没有一篇文章,其中包含跳转优化代码时可能发生的事情的示例?比如破坏堆栈。我无法想象它不会崩溃或经常出现奇怪的问题,但我记得在实践中通常都没问题。 - Trass3r
1
@Trass3r:我不知道有没有这样的东西;我从未编写过,也从未去寻找过;我从未想过尝试这样做。通常函数在进入时会进行所有堆栈分配。使用推送堆栈参数调用后的堆栈清理通常是与调用相同的“块”,因此跳来跳去不会打破它。(除非您跳转到任意指令而不是C源代码行。) - Peter Cordes
2
很可能会破坏一些逻辑,比如常量传播或其他不变量。例如,在循环体中重新运行i++并不会起作用,如果还有一个ptr++并且它们被优化为一个操作。或者跳入一个循环很可能没有运行循环设置而完全破坏。基本上涉及寄存器值的任何事情都很可能会出问题。 - Peter Cordes


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接