stepi进入函数内部,nexti跳过函数调用;两者均单步执行一条汇编指令,但stepi遇call会跳入被调函数首条指令,nexti则执行完整个call后停在下一条指令。

用 stepi 还是 nexti?区别很关键
两者都执行一条汇编指令,但行为不同:stepi(或简写 si)遇到 call 指令会跳进被调函数的第一条汇编指令;nexti(或 ni)则直接执行完整个 call,停在下一条指令——类似 C 层的 next,只是粒度是汇编级。
常见错误是想看函数内部却用了 nexti,结果“跳过”了函数体;或者想快速过掉库函数却误用 stepi,陷入 libc 汇编里出不来。
- 调试自己写的函数逻辑 → 优先用
stepi - 跳过系统调用或标准库函数 → 用
nexti - 不确定当前是不是
call?先x/1i $pc看一眼下一条指令
$pc 和 disassemble 必须配合着看
单步执行汇编时,光靠命令行回显不够直观。不主动查 $pc(程序计数器),你根本不知道刚执行的是哪条指令、下一条在哪。
建议每执行一次 stepi 或 nexti 后,立刻敲:
x/5i $pc
这能显示当前 $pc 开始的 5 条汇编指令,并标出箭头指向即将执行的那条。配合 info registers 查寄存器变化,才能确认指令是否按预期修改了状态。
- 别依赖 GDB 自动刷新反汇编窗口——默认不开启,要手动
set disassemble-next-line on -
disassemble默认只显示当前函数,想看其他地址范围得加参数,比如disassemble *0x400489,+20 - 如果看到
=>箭头消失或位置错乱,大概率是优化导致指令重排,此时需结合-O0 -g编译复现
为什么 stepi 有时“卡住”或跳转异常?
这不是命令失效,而是底层行为真实反映了 CPU 执行流。典型原因有:
- 执行到
syscall指令后,控制权交由内核,GDB 会等系统调用返回才继续——看起来像“卡住”,实际是正常等待 - 遇到未映射内存或非法指令(如
ud2),程序会SIGILL终止,GDB 报Program received signal SIGILL - 开启了 ASLR 且没用
set disable-randomization off,每次加载地址不同,断点可能挂错位置 - 使用了
inline函数或 LTO 编译,源码和汇编边界模糊,stepi可能跨函数跳转而不提示
此时别硬试,先 info proc mappings 确认地址空间,再 x/2i $pc 看当前指令是否合法。
寄存器和栈变化必须手动验证
GDB 不会自动告诉你某次 stepi 改了哪个寄存器——它只执行指令。你得自己比对前后状态。
最简验证方式:
info registers<br>stepi<br>info registers
重点盯 %rax、%rbx、%rsp、%rbp 和标志位(%rflags)。尤其注意 push/pop 对 %rsp 的影响,以及 call 是否自动压入返回地址。
- 想持续观察某个寄存器?用
display $rax,之后每次停顿时自动打印 - 看栈顶内容:
x/4gx $rsp(x86_64 下看 4 个 8 字节) - 别相信“变量名”显示——汇编层没有变量,只有寄存器和内存地址,
p x在优化后可能报Cannot access memory
真正难的不是记住命令,而是在每一步 stepi 后,能准确说出“这条指令干了什么、改了哪些寄存器、栈变了吗、下一条去哪”。这个判断过程没法跳过。


















