使用 adb 程序来显示和处理源文件

此部分描述了如何使用 adb 程序显示和处理源文件。

显示指令和数据

adb 程序提供了一些子命令以显示给定程序的指令和数据以及给定数据文件的数据。 子命令及其格式为:

显示地址
地址 [, Count ] = 格式
显示指令
地址 [, Count ] ? 格式
显示变量值
地址 [, Count ] / 格式

在此格式中,符号和变量的意义如下:

地址
给出指令或数据项的位置。
计数
给出要显示的项的数目。
格式
定义如何显示项。
=
显示项的地址。
?
显示文本段中的指令。
/
显示变量值。

形成地址

adb 程序中,地址为指示特定内存地址的 32 位值。 然而,它们可以用以下形式表示:

绝对地址
此 32 位值由一个 8 位十六进制数字或其他某一种进制的系统中与其等价的数字表示。
符号名称
程序中定义的符号的位置可由程序中该符号的名称表示。
入口点
例程入口点由前面带有一个 .(句点)的例程的名称表示。 :NONE. 例如,要引用开始的地址main例程,请使用以下表示法:
.main
位移
可通过从程序中入口点使用位移引用程序中的其他点。 例如,以下表示法引用在符号入口点之后 4 字节的指令main:
.main+4

显示地址

使用 =(等号)子命令以给定格式显示地址。 此命令显示以更简单的格式显示指令和数据地址,并且可以显示算术表达式的结果。 例如,输入:

main=an

显示符号的地址main:

10000370:

以下示例显示了用于显示(以十进制方式)内部变量 b 和十六进制值 0x2000 的总和及其输出的命令:

<b+0x2000=D
    268443648

如果给出计数,那么对相同值重复的次数与该给定计数相同。 以下示例显示了显示以下值的命令:main两次及其生成的输出:

main,2=x
    370 370

如果未给定地址,那么使用当前地址。 运行以上命令一次后 (将当前地址设置为main) ,以下命令重复该功能:

,2=x
    370 370

如果未指定格式,那么 adb 调试程序使用上次对此命令使用的格式。 例如,在以下命令序列中,mainone以十六进制显示:

main=x
  370
one=
  33c

显示 C 堆栈反向跟踪

要跟踪所有活动函数的路径,请使用 $c 子命令。 此子命令列示所有已被调用且还未返回控制权的函数的名称。 它还列示从其调用每个函数且将参数传递给每个函数的地址。 例如,以下命令序列在函数地址处设置断点.f+2adbsamp2 程序中。 该断点调用 $c 子命令。 程序启动,运行至该断点,然后显示调用的 C 语言函数的反向跟踪:

.f+2:b$c
:r
adbsamp2:running
.f(0,0) .main+26
.main(0,0,0) start+fa
breakpoint                f+2:           tgte     r2,r2

在缺省情况下,$c 子命令显示所有调用。 要减少显示的调用,请提供要显示的调用的计数。 例如,以下命令仅显示以上断点处的某一个活动函数:

,1$c
.f(0,0) .main+26

选择数据格式

格式即定义如何显示数据的字母或字符。 以下是最为常用的格式:

a
当前符号地址
b
八进制字节(显示与指令相关的数据或寄存器的高低字节)
c
字符字节(字符型变量)
d
十进制半字(短整型变量)
D
十进制全字(长整型变量)
i
助记符格式的机器指令
n
换行
o
八进制半字(短整型变量)
O
八进制全字(长整型变量)
r
空格
s
以 null 结束的字符串(以 null 结束的字符型变量数组)
t
水平制表符
u
无符号整数半字(短整型变量)
x
十六进制半字(短整型变量)
X
十六进制全字(长整型变量)

例如,在使用 adbsamp 示例程序时,以下命令产生指示输出:

main=o
1560
main=O
4000001560
main=d
880
main=D
536871792
main=x
370
main=X
20000370
main=u
880

格式可自我使用或与其他格式结合以表示不同格式数据的组合。 可将 anrt 格式与其他格式结合使显示更加易于阅读。

更改内存映射

可使用 ?m/m 子命令更改内存映射的值。 这些命令将指定值赋给对应映射条目。 命令格式为:

[,count] ?m b1 e1 f1
[,count] /m b1 e1 f2

以下示例显示先前示例中使用 $m 子命令显示的内存映射上这些命令的结果:

,0?m    10000100         10000470       0
/m      100      100     100
$m
  [0] :   ?map :   'adbsamp3'
 b1 = 0x10000100,  e1 = 10000470,  f1 = 0
 b2 = 0x20000600,  e2 = 0x2002c8a4, f2 = 0x600

 [1] :  ?map :   'shr.o' in library '/usr/ccs/lib/libc.a'
 b1 = 0xd00d6200,  e1 = 0xd01397bf,  f1 = 0xd00defbc
 b2 = 0x20000600,  e2 = 0x2002beb8, f2 = 0x4a36c

  [-] : /map :  '-'
 b1 = 100,    e1 = 100,   f1 = 100
 b2 = 0,    e2 = 0,     f2 = 0

要更改数据段的值,请将 *(星号)添加到 /? 之后。

,0?*m   20000270         20000374       270
/*m     200      200     200
$m
  [0] : ?map :   'adbsamp3'
 b1 = 0x10000100,  e1 = 10000470,  f1 = 0
 b2 = 0x20000270,  e2 = 0x20000374, f2 = 0x270

 [1] :  ?map :   'shr.o' in library '/usr/ccs/lib/libc.a'
 b1 = 0xd00d6200,  e1 = 0xd01397bf,  f1 = 0xd00defbc
 b2 = 0x20000600,  e2 = 0x2002beb8, f2 = 0x4a36c

  [-] :  /map :  '-'
 b1 = 100,    e1 = 100,   f1 = 100
 b2 = 0,    e2 = 0,     f2 = 0

修补二进制文件

您可以对任何文件 (包括可执行二进制文件) 进行更正或更改, 通过使用 -w 标志启动 adb 程序并使用 wW () 子命令。

在文件中查找值

使用 lL 子命令在文件中查找特定值。 子命令格式为:

? l

/l

搜索开始自当前地址,并查找由 Value 指示的表达式。 l 子命令搜索 2 字节值。 L 子命令 搜索 4 字节值。

?l 子命令从当前地址处开始搜索并继续查找,直至找到第一个匹配项或到达文件结尾。 如果找到该值,那么将当前地址设为该值的地址。 例如,以下命令在 adbsamp2 文件中搜索 f 符号第一次出现的地方。

?l .f.
write+a2

.write+a2 处找到该值,并将当前地址设为该地址。

写入文件

使用 wW 子命令写到文件。 子命令格式为:

[ Address ] ? w

在此格式中,Address 参数为想要更改的值的地址,而 Value 参数为新值。 w 子命令写入 2 字节值。 W 子命令写入 4 字节值。 例如,以下命令将单词“This”更改 为“The”:

?l .Th.
?W .The.

W 子命令更改所有的 4 个字符。

对内存进行更改

在程序运行的任何时间更改内存。 如果已经使用带有断点的 :r 子命令启动了程序操作,那么随后的 w 子命令将导致 adb 程序写到内存中的程序而不是写到文件。 此命令用于在程序运行时更改它的数据,比如临时更改程序标记或变量的值。

使用 adb 变量

adb 调试程序在启动时自动创建其自身变量的集合。 按下表中定义的那样,将这些变量设置给程序文件不同部分中的地址和大小:

0
上一次显示的值
1
指令源的上一个位移部分
2
1 变量的先前值
9
对最后一个 $<$<< 命令进行计数
b
数据段的基地址
d
数据段的大小
e
程序的入口地址
m
魔数
s
堆栈段的大小
t
文本段的大小

adb 调试程序读取程序文件以查找这些变量的值。 如果文件看起来不是程序文件,那么 adb 程序不定义这些值。

要显示 adb 调试程序赋给这些变量的值,请使用 $v 子命令。 此子命令以当前格式列示了后跟其值的变量名。 子命令显示值不为 0(零)的任何变量。 如果变量也具有非零段值,那么变量值将显示为地址。 否则,它将显示为数字。 以下示例显示了使用此命令显示样本程序 adbsamp 的变量值:

$v
Variables
0 = undefined
1 = undefined
2 = undefined
9 = undefined
b = 10000000
d = 130
e = 10000038
m = 108
t = 298

Specify the current value of an 阿德布 variable in an expression by preceding the variable name with < (less than sign). 以下示例显示 b 基数变量的当前值:

<b=X
10000000

创建您自己的变量或通过将值指定给具有> (大于号) 的变量名称来更改现有变量的值。 赋值格式为:

表达式 > VariableName

其中,Expression 参数为将要赋给变量的值,VariableName 参数为将要接收该值的变量。 VariableName 参数必须为单个字母。 例如,赋值:

0x2000>b

将十六进制值 0x2000 赋给 b 变量。 再次显示 b 的内容以显示发生赋值:

<b=X
 2000

查找当前地址

adb 程序具有两个特殊变量,它们能跟踪命令中上一次使用的地址以及上一次随命令输入的地址。 (句点) 变量 (也称为当前地址) 包含命令中使用的最后一个地址。 "(双引号)变量包含上一次随命令输入的地址。 " 变量通常包含相同的地址,但使用隐式命令 (例如换行符和 ^ (插入标记) 字符) 时除外。 这些字符会自动增大和减小 变量但保留) (右括号) 变量不变。

这两个 并且可以在任何表达式中使用 " 变量。 <(小于号)不是必需的。 例如,在使用 adbsamp 程序开始调试时,以下命令会显示这些变量:

.=
    0.
=
    0

显示外部变量

使用 $e 子命令来显示 adb 程序中所有外部变量的值。 外部变量是程序中的变量,它们可在全局范围内起作用或者已定义在任何函数外,并且包含在程序使用的库例程中定义的变量以及共享库的所有外部变量。

在获取所有可用变量名称的列表或它们值的摘要时,$e 子命令非常有用。 命令在每一行上显示一个名称,而变量值(如果有)也将显示在同一行上。 如果指定了 Count 参数,那么仅显示与该文件相关的外部变量。

以下示例演示了在调用 $e 子命令的 adbsamp2 样本程序中设置断点的过程,以及程序运行时产生的输出(请确保删除您先前可能设置的任何断点):

.f+2:b,0$e
:r
adbsamp2: running
_errno: 0
_environ:  3fffe6bc
__NLinit:  10000238
_main: 100001ea
_exit: 1000028c
_fcnt: 0
_loop_count:  1
_f:   100001b4
_NLgetfile: 10000280
_write: 100002e0
__NLinit__X:  10000238
_NLgetfile__X:   10000280
__cleanup: 100002bc
__exit: 100002c8
_exit__X:  1000028c
__cleanup__X:  100002bc
breakpoint .f+2:  st r2,1c(r1)

显示地址映射

adb 程序为程序中的文本段和数据段准备了一组映射,并使用这些映射对请求显示的项进行访问。 使用 $m 子命令显示地址映射的内容。 子命令显示程序中所有段的映射,并使用从程序和核心文件或直接从内存获取的信息。

$m 子命令显示的信息与以下内容类似:

$m
  [0] : ?map :   'adbsamp3'
  b1 = 0x10000200,  e1 = 0x10001839,  f1 = 0x10000200
  b2 = 0x2002c604,  e2 = 0x2002c8a4,  f2 = 0x600

  [1] : ?map :   'shr.o' in library 'lib/libc.a'
  b1 = 0xd00d6200,  e1 = 0xd013976f,  f1 = 0xd00defbc
  b2 = 0x20000600,  e2 = 0x2002bcb8,  f2 = 0x4a36c

  [-] :  /map :          '-'
  b1 = 0x0000000,  e1 = 0x00000000,  f1 = 0x00000000
  b2 = 0x0000000,  e2 = 0x00000000,  f2 = 0x00000000

屏幕定义文本的地址映射参数 (b1,e1f1) 和数据 (b2e2f2) adb 调试程序正在使用的两个文件的段。 此示例仅显示 adbsamp3 样本程序的值。 映射值的第二个集合针对正在使用的核心文件。 既然所有的均未使用,示例将该文件名显示为 -(短划线)。

方括号内显示的值可以用作 ? e? m 子命令中的 Count 参数。