x命令是唯一能按地址和字节粒度直接读栈内存的调试工具,不依赖调试信息,适用于无变量名的栈数据;其格式x/nuw中n为单元数、u为单位大小(b/h/w/g)、f为显示格式(x/d/u/s/i)。

用 x 命令直接读栈内存,别用 print
print 只能解析变量名或表达式,它依赖调试信息和类型系统,对裸栈地址无效。而栈上很多数据(比如未命名的临时值、溢出覆盖区、返回地址附近)根本没变量名,必须用 x —— 它是唯一能按地址、按字节粒度读内存的命令。
-
x不关心符号表,只认地址和格式 - 栈指针寄存器(
$rsp或$rbp)是你最常用来定位的起点 - 比如
x/8xw $rsp表示:从当前栈顶开始,以 4 字节为单位,显示 8 个值,用十六进制
x/nuw $rsp 中的 n、u、w 分别控制什么
这三个参数决定“读多少、怎么读、读多大”:
-
n:要显示的单元数量,不是字节数。例如x/5xg是 5 个 8 字节单元,共 40 字节 -
u:单位大小,常用:b(1 字节)、h(2 字节)、w(4 字节)、g(8 字节) -
f:显示格式,调试栈时最常用:x(十六进制)、d(有符号十进制)、u(无符号十进制)、s(字符串)、i(反汇编指令)
常见组合:
-
x/16xb $rsp:查看栈顶 16 字节原始内容(排查越界写入) -
x/8xg $rbp:查看以$rbp为基址的 8 个 8 字节值(常用于找调用者返回地址、旧$rbp) -
x/4su $rsp:把栈顶起 4 个“字符串单元”当 C 字符串打印(注意s默认按char*解析,遇 \0 截断)
为什么 info locals 看不到变量,但 x 能看到
info locals 依赖编译器生成的 DWARF 调试信息,一旦开启优化(如 -O2),局部变量可能被寄存器化、内联或完全删除——此时它就为空。而 x 绕过所有抽象层,直接读物理内存:
- 即使变量被优化掉,只要它曾被写入栈(比如未优化前的调试版),其残留值仍在那
- 如果你怀疑栈被破坏,
x/32xb $rsp比info locals更可信 - 注意:栈增长方向是向下(高地址 → 低地址),所以
$rsp是栈顶最低地址,往低地址走才是“向上”看函数参数或返回地址
查栈时最容易忽略的两个细节
-$rsp 和 $rbp 的值在函数入口/出口处变化剧烈,单步到 call 或 ret 前后立刻 info registers 对比,否则地址可能已失效
- x 默认按“当前架构字长”解释内存(x86_64 是 8 字节),但如果你读的是 char buf[10],用 x/10cb &buf 才能看到每个字符,用 x/10xb &buf 会把字节顺序搞反(小端序下低位在前)
栈不是黑盒,它是可测量的内存区域。只要地址对、单位对、格式对,x 就不会骗你。


















