第 18 章 在机器指令级调试
本章介绍如何在机器指令级使用事件管理和进程控制命令、如何显示指定地址处的内存内容以及如何显示源代码行及其相应的机器指令。next 命令、step 命令、stop 命令和 trace 命令分别支持相应的机器指令级变体:nexti 命令、stepi 命令、stopi 命令和 tracei 命令。可使用 regs 命令输出多个机器寄存器的内容,也可使用 print 命令输出各个寄存器的内容。
本章由以下部分组成:
检查内存的内容
可使用地址以及 examine 或 x 命令检查内存位置的内容及打印每个地址处的汇编语言指令。使用从 adb(1)(汇编语言调试器)派生的命令,可以查询:
address,使用 =(等号)字符,或
某地址处存储的内容,使用 /(斜线)字符。
可使用 dis 命令和 listi 命令打印汇编命令。(请参见dis 命令用法和listi 命令用法。)
examine 或 x 命令用法
可使用 examine 命令或其别名 x 显示内存内容或地址。
下列语法用于以 format 格式显示始于 address 的 count 项内存内容。缺省的 address 为先前显示的最后一个地址后的下一个地址。缺省 count 为 1。缺省 format 与在先前的 examine 命令中使用的相同;如果这是给出的第一个命令,则为 X。
examine 命令的语法如下:
examine [address] [/ [count] [format]]
要以 format 格式显示 address1 到 address2(首末地址包含在内)的内存内容,请键入:
examine address1, address2 [/ [format]]
要以给定格式显示地址而不是地址内容,请键入:
examine address = [format]
要打印 examine 最后显示的地址后的下一个地址处存储的值,请键入:
examine +/ i
要打印表达式的值,请以地址形式输入表达式:
examine address=format
examine address=
地址
address 是求值结果为地址或可用作地址的任何表达式。可用 +(加号)替换 address,它以缺省格式显示下一个地址的内容。
例如,下面所示都是有效地址:
0xff99绝对地址
main函数地址
main+20与函数地址的偏移
&errno变量地址
str指向字符串的指针值变量
用于显示内存的符号地址的名称前有和号 (&)。函数名称前可以不带和号,例如,&main 等同于 main。寄存器名称前有美元符号 ($)。
格式
format 是 dbx 用来显示查询结果的地址显示格式。产生的输出取决于当前显示 format。要更改显示格式,应提供不同的 format 代码。
每个 dbx 会话开始时的缺省格式设置为 X,即以十六进制 32 位字显示地址或值。以下内存显示格式为合法格式。
i显示为汇编指令。
d显示为十进制 16 位(2 字节)。
D显示为十进制 32 位(4 字节)。
o显示为八进制 16 位(2 字节)。
O显示为八进制 32 位(4 字节)。
x显示为十六进制 16 位(2 字节)。
X显示为十六进制 32 位(4 字节)。(缺省格式)
b显示为八进制字节。
c显示为字符。
w显示为宽字符。
s显示为以空字节终止的字符串。
W显示为宽字符。
f显示为单精度浮点数。
F, g显示为双精度浮点数。
E显示为扩展精度浮点数。
ld, lD显示为十进制 32 位(4 字节)(与 D 相同)。
lo, lO显示为八进制 32 位(4 字节)(与 O 相同)。
lx, LX显示为十六进制 32 位(4 字节)(与 X 相同)。
Ld, LD显示为十进制 64 位(8 字节)。
Lo, LO显示为八进制 64 位(8 字节)。
Lx, LX显示为十六进制 64 位(8 字节)。
Count
count 为十进制重复计数。增量大小取决于内存显示格式。
使用地址的示例
下面的示例说明如何使用地址以及 count 和 format 选项来显示始于当前停止点的五个连续的反汇编指令。
对于基于 SPARC 的系统:
(dbx) stepi
stopped in main at 0x108bc
0x000108bc: main+0x000c: st %l0, [%fp - 0x14]
(dbx) x 0x108bc/5i
0x000108bc: main+0x000c: st %l0, [%fp - 0x14]
0x000108c0: main+0x0010: mov 0x1,%l0
0x000108c4: main+0x0014: or %l0,%g0, %o0
0x000108c8: main+0x0018: call 0x00020b90 [unresolved PLT 8: malloc]
0x000108cc: main+0x001c: nop
对于基于 x86 的系统:
(dbx) x &main/5i
0x08048988: main : pushl %ebp
0x08048989: main+0x0001: movl %esp,%ebp
0x0804898b: main+0x0003: subl $0x28,%esp
0x0804898e: main+0x0006: movl 0x8048ac0,%eax
0x08048993: main+0x000b: movl %eax,-8(%ebp)
dis 命令用法
dis 命令等同于以 i 作为缺省显示格式的 examine 命令。
dis 命令的语法如下。
dis [address] [address1, address2] [/count]
dis 命令:
不使用参数时显示以 + 开始的 10 个指令。
只使用 address 参数时,反汇编始于 address 的 10 个指令。
使用 address 参数和 count 时,反汇编始于 address 的 count 个指令。
使用 address1 和 address2 参数时,反汇编从 address1 到 address2 的指令。
只使用 count 时,显示始于 + 的 count 个指令。
listi 命令用法
要显示源代码行及其对应的汇编指令,请使用 listi 命令,它等同于命令 list -i。请参见打印源码列表中有关 list -i 的讨论内容。
对于基于 SPARC 的系统:
(dbx) listi 13, 14
13 i = atoi(argv[1]);
0x0001083c: main+0x0014: ld [%fp + 0x48], %l0
0x00010840: main+0x0018: add %l0, 0x4, %l0
0x00010844: main+0x001c: ld [%l0], %l0
0x00010848: main+0x0020: or %l0, %g0, %o0
0x0001084c: main+0x0024: call 0x000209e8 [unresolved PLT 7: atoi]
0x00010850: main+0x0028: nop
0x00010854: main+0x002c: or %o0, %g0, %l0
0x00010858: main+0x0030: st %l0, [%fp - 0x8]
14 j = foo(i);
0x0001085c: main+0x0034: ld [%fp - 0x8], %l0
0x00010860: main+0x0038: or %l0, %g0, %o0
0x00010864: main+0x003c: call foo
0x00010868: main+0x0040: nop
0x0001086c: main+0x0044: or %o0, %g0, %l0
0x00010870: main+0x0048: st %l0, [%fp - 0xc]
对于基于 x86 的系统:
(dbx) listi 13, 14
13 i = atoi(argv[1]);
0x080488fd: main+0x000d: movl 12(%ebp),%eax
0x08048900: main+0x0010: movl 4(%eax),%eax
0x08048903: main+0x0013: pushl %eax
0x08048904: main+0x0014: call atoi <0x8048798>
0x08048909: main+0x0019: addl $4,%esp
0x0804890c: main+0x001c: movl %eax,-8(%ebp)
14 j = foo(i);
0x0804890f: main+0x001f: movl -8(%ebp),%eax
0x08048912: main+0x0022: pushl %eax
0x08048913: main+0x0023: call foo <0x80488c0>
0x08048918: main+0x0028: addl $4,%esp
0x0804891b: main+0x002b: movl %eax,-12(%ebp)
在机器指令级单步执行和跟踪
机器指令级命令与其对应的源码级命令的功能相同,只不过它们在单步指令级而非源代码行级执行。
在机器指令级单步执行
要从一个机器指令单步执行到下一个机器指令,请使用 nexti 命令或 stepi 命令
nexti 命令和 stepi 命令与其对应的源代码级命令运行方式相同:nexti 命令步过 函数,stepi 命令步入由下一个指令调用的函数(停止于被调用函数中的第一个指令)。命令形式也相同。有关说明,请参见next 命令和step 命令。
nexti 命令和 stepi 命令的输出与其对应的源代码级命令的输出有两点不同:
输出中包含程序停止处的指令地址(而非源代码行号)。
缺省输出中包含反汇编指令,而非源代码行。
例如:
(dbx) func
hand::ungrasp
(dbx) nexti
ungrasp +0x18: call support
(dbx)
在机器指令级跟踪
机器级的跟踪技术与源代码级相同,只是要使用 tracei 命令。执行 tracei 命令时,dbx 只会在每次检查执行的地址或跟踪的变量值后,执行一个指令。tracei 命令会产生类似 stepi 的自动行为。程序一次前进一个指令来步入函数调用。
使用 tracei 命令时,它会使程序在执行每个指令后停止一会儿,这时,dbx 检查地址执行情况或跟踪的变量或表达式的值。使用 tracei 命令会显著降低执行速度。
有关跟踪及其事件规范和修饰符的更多信息,请参见跟踪执行和tracei 命令。
tracei 命令的常规语法如下:
tracei event-specification [modifier]
tracei 命令的常用形式为:
tracei step跟踪每一指令。
tracei next跟踪每一指令,但跳过调用。
tracei at address跟踪给定代码地址。
有关更多信息,请参见tracei 命令。
SPARC:
(dbx) tracei next -in main
(dbx) cont
0x00010814: main+0x0004: clr %l0
0x00010818: main+0x0008: st %l0, [%fp - 0x8]
0x0001081c: main+0x000c: call foo
0x00010820: main+0x0010: nop
0x00010824: main+0x0014: clr %l0
....
....
(dbx) (dbx) tracei step -in foo -if glob == 0
(dbx) cont
0x000107dc: foo+0x0004: mov 0x2, %l1
0x000107e0: foo+0x0008: sethi %hi(0x20800), %l0
0x000107e4: foo+0x000c: or %l0, 0x1f4, %l0 ! glob
0x000107e8: foo+0x0010: st %l1, [%l0]
0x000107ec: foo+0x0014: ba foo+0x1c
....
....
在机器指令级设置断点
要在机器指令级设置断点,应使用 stopi 命令。该命令接受任何 event specification,语法如下:
stopi event-specification [modifier]
stopi 命令的常用形式如下:
stopi [at address] [-if cond]
stopi in function [-if cond]
有关更多信息,请参见stopi 命令。
在地址处设置断点
要在特定地址设置断点,请键入:
(dbx) stopi at address
例如:
(dbx) nexti
stopped in hand::ungrasp at 0x12638
(dbx) stopi at &hand::ungrasp
(3) stopi at &hand::ungrasp
(dbx)
regs 命令用法
使用 regs 命令可以打印所有寄存器的值。
regs 命令的语法如下:
regs [-f][-F]
-f 表示包括浮点寄存器(单精度)。-F 表示包括浮点寄存器(双精度)。
有关更多信息,请参见regs 命令。
对于基于 SPARC 的系统:
dbx[13] regs -F
current thread: t@1
current frame: [1]
g0-g3 0x00000000 0x0011d000 0x00000000 0x00000000
g4-g7 0x00000000 0x00000000 0x00000000 0x00020c38
o0-o3 0x00000003 0x00000014 0xef7562b4 0xeffff420
o4-o7 0xef752f80 0x00000003 0xeffff3d8 0x000109b8
l0-l3 0x00000014 0x0000000a 0x0000000a 0x00010a88
l4-l7 0xeffff438 0x00000001 0x00000007 0xef74df54
i0-i3 0x00000001 0xeffff4a4 0xeffff4ac 0x00020c00
i4-i7 0x00000001 0x00000000 0xeffff440 0x000108c4
y 0x00000000
psr 0x40400086
pc 0x000109c0:main+0x4 mov 0x5, %l0
npc 0x000109c4:main+0x8 st %l0, [%fp - 0x8]
f0f1 +0.00000000000000e+00
f2f3 +0.00000000000000e+00
f4f5 +0.00000000000000e+00
f6f7 +0.00000000000000e+00
...
对于基于 x64 的系统:
(dbx) regs
current frame: [1]
r15 0x0000000000000000
r14 0x0000000000000000
r13 0x0000000000000000
r12 0x0000000000000000
r11 0x0000000000401b58
r10 0x0000000000000000
r9 0x0000000000401c30
r8 0x0000000000416cf0
rdi 0x0000000000416cf0
rsi 0x0000000000401c18
rbp 0xfffffd7fffdff820
rbx 0xfffffd7fff3fb190
rdx 0x0000000000401b50
rcx 0x0000000000401b54
rax 0x0000000000416cf0
trapno 0x0000000000000003
err 0x0000000000000000
rip 0x0000000000401709:main+0xf9 movl $0x0000000000000000,0xfffffffffffffffc(%rbp)
cs 0x000000000000004b
eflags 0x0000000000000206
rsp 0xfffffd7fffdff7b0
ss 0x0000000000000043
fs 0x00000000000001bb
gs 0x0000000000000000
es 0x0000000000000000
ds 0x0000000000000000
fsbase 0xfffffd7fff3a2000
gsbase 0xffffffff80000000
(dbx) regs -F
current frame: [1]
r15 0x0000000000000000
r14 0x0000000000000000
r13 0x0000000000000000
r12 0x0000000000000000
r11 0x0000000000401b58
r10 0x0000000000000000
r9 0x0000000000401c30
r8 0x0000000000416cf0
rdi 0x0000000000416cf0
rsi 0x0000000000401c18
rbp 0xfffffd7fffdff820
rbx 0xfffffd7fff3fb190
rdx 0x0000000000401b50
rcx 0x0000000000401b54
rax 0x0000000000416cf0
trapno 0x0000000000000003
err 0x0000000000000000
rip 0x0000000000401709:main+0xf9 movl $0x0000000000000000,0xfffffffffffffffc(%rbp)
cs 0x000000000000004b
eflags 0x0000000000000206
rsp 0xfffffd7fffdff7b0
ss 0x0000000000000043
fs 0x00000000000001bb
gs 0x0000000000000000
es 0x0000000000000000
ds 0x0000000000000000
fsbase 0xfffffd7fff3a2000
gsbase 0xffffffff80000000
st0 +0.00000000000000000000e+00
st1 +0.00000000000000000000e+00
st2 +0.00000000000000000000e+00
st3 +0.00000000000000000000e+00
st4 +0.00000000000000000000e+00
st5 +0.00000000000000000000e+00
st6 +0.00000000000000000000e+00
st7 +NaN
xmm0a-xmm0d 0x00000000 0xfff80000 0x00000000 0x00000000
xmm1a-xmm1d 0x00000000 0x00000000 0x00000000 0x00000000
xmm2a-xmm2d 0x00000000 0x00000000 0x00000000 0x00000000
xmm3a-xmm3d 0x00000000 0x00000000 0x00000000 0x00000000
xmm4a-xmm4d 0x00000000 0x00000000 0x00000000 0x00000000
xmm5a-xmm5d 0x00000000 0x00000000 0x00000000 0x00000000
xmm6a-xmm6d 0x00000000 0x00000000 0x00000000 0x00000000
xmm7a-xmm7d 0x00000000 0x00000000 0x00000000 0x00000000
xmm8a-xmm8d 0x00000000 0x00000000 0x00000000 0x00000000
xmm9a-xmm9d 0x00000000 0x00000000 0x00000000 0x00000000
xmm10a-xmm10d 0x00000000 0x00000000 0x00000000 0x00000000
xmm11a-xmm11d 0x00000000 0x00000000 0x00000000 0x00000000
xmm12a-xmm12d 0x00000000 0x00000000 0x00000000 0x00000000
xmm13a-xmm13d 0x00000000 0x00000000 0x00000000 0x00000000
xmm14a-xmm14d 0x00000000 0x00000000 0x00000000 0x00000000
xmm15a-xmm15d 0x00000000 0x00000000 0x00000000 0x00000000
fcw-fsw 0x137f 0x0000
fctw-fop 0x0000 0x0000
frip 0x0000000000000000
frdp 0x0000000000000000
mxcsr 0x00001f80
mxcr_mask 0x0000ffff
(dbx)
平台特定寄存器
以下各表列出了可在表达式中使用的 SPARC、x86 和 AMD64 体系结构的平台特定寄存器名称。
SPARC 寄存器信息
下面的寄存器信息适用于 [Please define the SPARCsans text entity] 体系结构。
寄存器说明
$g0 到 $g7全局寄存器
$o0 到 $o7“外部”寄存器
$l0 到 $l7“本地”寄存器
$i0 到 $i7“内部”寄存器
$fp帧指针,等同于寄存器 $i6
$sp栈指针,等同于寄存器 $o6
$yY 寄存器
$psr处理器状态寄存器
$wim窗口无效屏蔽寄存器
$tbr捕获基址寄存器
$pc程序计数器
$npc下一程序计数器
$f0 到 $f31FPU "f" 寄存器
$fsrFPU 状态寄存器
$fqFPU 队列
$f0f1 $f2f3 ... $f30f31 对浮点型寄存器视为 C 的“双精度”类型(通常 $fN 寄存器视为 C 的“浮点”类型)。这些对也可称为 $d0 ... $d30.
SPARC V9 和 V8+ 硬件上另外还提供了以下这些寄存器:
$g0g1 through $g6g7
$o0o1 through $o6o7
$xfsr $tstate $gsr
$f32f33 $f34f35 through $f62f63 ($d32 ... $$d62)
有关 SPARC 寄存器和寻址的更多信息,请参见《SPARC 体系结构参考手册》和《SPARC 汇编语言参考手册》。
x86 寄存器信息
下面的寄存器信息适用于 x86 体系结构。
寄存器说明
$gs交替数据段寄存器
$fs交替数据段寄存器
$es交替数据段寄存器
$ds数据段寄存器
$edi目标索引寄存器
$esi源索引寄存器
$ebp帧指针
$esp栈指针
$ebx通用寄存器
$edx通用寄存器
$ecx通用寄存器
$eax通用寄存器
$trapno异常向量数
$err异常错误代码
$eip指令指针
$cs代码段寄存器
$eflags标志
$uesp用户栈指针
$ss堆栈段寄存器
常用寄存器也使用其机器无关名称作为别名。
寄存器说明
$SP栈指针,等同于 $uesp
$pc程序计数器,等同于 $eip
$fp帧指针,等同于 $ebp
80386 下半部(16 位)寄存器为:
寄存器说明
$ax通用寄存器
$cx通用寄存器
$dx通用寄存器
$bx通用寄存器
$si源索引寄存器
$di目标索引寄存器
$ip指令指针,下 16 位
$flags标志,下 16 位
80386 的前四个 16 位寄存器可分为多个 8 位部分:
寄存器说明
$al寄存器 $ax 的(右)下半部分
$ah寄存器 $ax 的(左)上半部分
$cl寄存器 $cx 的(右)下半部分
$ch寄存器 $cx 的(左)上半部分
$dl寄存器 $dx 的(右)下半部分
$dh寄存器 $dx 的(左)上半部分
$bl寄存器 $bx 的(右)下半部分
$bh寄存器 $bx 的(左)上半部分
80387 的寄存器为:
寄存器说明
$fctrl控制寄存器
$fstat状态寄存器
$ftag标记寄存器
$fip指令指针偏移
$fcs代码段选择符
$fopoff操作数指针偏移
$fopsel操作数指针选择符
$st0 到 $st7数据寄存器
AMD64 寄存器信息
下面的寄存器信息适用于 AMD64 体系结构:
寄存器说明
rax通用寄存器-为函数调用传递参数
rbx通用寄存器-被调用方保存
rcx通用寄存器-为函数调用传递参数
rdx通用寄存器-为函数调用传递参数
rbp通用寄存器-栈管理/帧指针
rsi通用寄存器-为函数调用传递参数
rdi通用寄存器-为函数调用传递参数
rsp通用寄存器-栈管理/栈指针
r8通用寄存器-为函数调用传递参数
r9通用寄存器-为函数调用传递参数
r10通用寄存器-临时
r11通用寄存器-临时
r12通用寄存器-被调用方保存
r13通用寄存器-被调用方保存
r14通用寄存器-被调用方保存
r15通用寄存器-被调用方保存
rflags标志寄存器
rip指令指针
mmx0/st064 位媒体和浮点寄存器
mmx1/st164 位媒体和浮点寄存器
mmx2/st264 位媒体和浮点寄存器
mmx3/st364 位媒体和浮点寄存器
mmx4/st464 位媒体和浮点寄存器
mmx5/st564 位媒体和浮点寄存器
mmx6/st664 位媒体和浮点寄存器
mmx7/st764 位媒体和浮点寄存器
xmm0128 位媒体寄存器
xmm1128 位媒体寄存器
xmm2128 位媒体寄存器
xmm3128 位媒体寄存器
xmm4128 位媒体寄存器
xmm5128 位媒体寄存器
xmm6128 位媒体寄存器
xmm7128 位媒体寄存器
xmm8128 位媒体寄存器
xmm9128 位媒体寄存器
xmm10128 位媒体寄存器
xmm11128 位媒体寄存器
xmm12128 位媒体寄存器
xmm13128 位媒体寄存器
xmm14128 位媒体寄存器
xmm15128 位媒体寄存器
cs段寄存器
os段寄存器
es段寄存器
fs段寄存器
gs段寄存器
ss段寄存器
fcwfxsave 和 fxstor 内存映像控制字
fswfxsave 和 fxstor 内存映像状态字
ftwfxsave 和 fxstor 内存映像标记字
fopfxsave 和 fxstor 内存映像最后一个 x87 op 代码
fripfxsave 和 fxstor 内存映像 64 位代码段偏移
frdpfxsave 和 fxstor 内存映像 64 位日期段偏移
mxcsrfxsave 和 fxstor 内存映像 128 位媒体指令控制和状态寄存器
mxcsr_maskmxcsr_mask 中的设置位指示 mxcsr 中支持的特征位