在GDB调试时,是否可能跳转至代码/可执行文件中的某个位置或地址?
假设我有类似以下内容的代码:
int main()
{
caller_f1() {
f1(); // breakpoint
f2() } // want to skip f2() and jump
caller_f2() { // jump to this this location ??
f1();
f2(); }
}
使用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。(请参见 x86 标签wiki底部的asm调试技巧)。
i++
并不会起作用,如果还有一个ptr++
并且它们被优化为一个操作。或者跳入一个循环很可能没有运行循环设置而完全破坏。基本上涉及寄存器值的任何事情都很可能会出问题。 - Peter Cordes