本章介绍 MDB 语言语法、运算符以及命令和符号名称解析的规则。
调试器处理来自标准输入的命令。如果标准输入是终端,则 MDB 提供终端编辑功能。MDB 还可以处理来自宏文件和 dcmd 管道的命令,如下所述。语言语法是围绕计算表达式的值(通常为目标中的内存地址)和向该地址应用 dcmd 的概念设计的。当前的地址位置称为 dot,"." 用于引用其值。
metacharacter 是以下字符之一:
[ ] | ! / \ ? = > $ : ; NEWLINE SPACE TAB
blank 是 TAB 或 SPACE。word 是由一个或多个不带引号的元字符分隔的字符序列。一些元字符仅用作某些上下文中的分隔符,如下所述。identifier 是字母、数字、下划线、句点或反引号的序列,以字母、下划线或句点开头。标识符用作符号、变量、dcmd 和 walker 的名称。命令由 NEWLINE 或分号 (;) 分隔。
dcmd 由以下字或元字符之一表示:
/ \ ? = > $character :character ::identifier
由元字符命名或者前缀为单个 $ 或 : 的 dcmd 是作为内置运算符提供的,实现与传统 adb(1) 实用程序命令集的完全兼容。解析 dcmd 后,不再将 /、\、?、=、>、$ 和 : 字符识别为元字符,直到参数列表结束。
simple-command 是一个后跟零个或多个空格分隔字序列的 dcmd。将字作为被调用 dcmd 的参数进行传递,算术展开和加引号中指定的除外。每个 dcmd 都返回一种退出状态,指示它成功、失败或是通过无效参数调用的。
pipeline 是由 | 分隔的一个或多个简单命令的序列。与 shell 不同,MDB 管道中的 dcmd 不作为单独的进程执行。解析管道后,按从左到右的顺序调用每个 dcmd。按dcmd 管道中所述处理和存储每个 dcmd 的输出。左侧 dcmd 完成后,它的输出经处理后用作管道中下一个 dcmd 的输入。如果任何 dcmd 未返回成功退出状态,则异常中止管道。
expression 是对其求值以计算 64 位无符号整数值的字的序列。对这些字求值使用的是算术展开中所述的规则。
command 是以下形式之一:
简单命令或管道可以选择使用 ! 字符作为后缀,指示调试器应该打开 pipe(2),并将 MDB 管道中最后一个 dcmd 的标准输出发送到通过执行 $SHELL -c(后跟由 ! 字符之后的字串联而成的字符串)而创建的外部进程。有关更多详细信息,请参阅Shell 转义。
简单命令或管道可以使用表达式作为前缀。执行管道之前,将点(由 “.” 表示的变量)的值设置为表达式的值。
简单命令或管道可以使用两个表达式作为前缀。计算第一个表达式的值可确定点的新值,计算第二个表达式的值可确定管道中第一个 dcmd 的重复计数。在执行管道中的下一个 dcmd 之前,将对此 dcmd 执行 count 次。重复计数仅适用于管道中的第一个 dcmd。
如果省略初始表达式,则不修改点;但是,将根据表达式的值重复管道中的第一个 dcmd。
命令只能包含算术表达式。计算表达式的值,将点变量设置为该表达式的值,然后使用点的新值执行前面的 dcmd 和参数。
命令只能包含点表达式和重复计数表达式。将点设置为第一个表达式的值后,按第二个表达式的值所指定的次数重复执行前面的 dcmd 和参数。
如果省略初始表达式,则不修改点,但是按计数表达式的值所指定的次数重复执行前面的 dcmd 和参数。
如果命令以 ! 字符开头,则不执行任何 dcmd,而且调试器执行后跟字符串的 $SHELL -c,该字符串通过串联 ! 字符后的字而构成的。
字以 // 开头时,会忽略该字之后 NEWLINE 之前的所有字符。
MDB 命令前面是表示起始地址的可选表达式或起始地址和重复计数时,将执行算术展开。也可以执行算术展开以计算 dcmd 的数值参数。算术表达式可以出现在用方括号括起来且前面是美元符号 ($[ expression ]) 的参数列表中,并将被替换为表达式的值。
表达式可以包含以下任一特殊字:
指定的整数值。整数值可以使用 0i 或 0I 作为前缀以指示二进制值,使用 0o 或 0O 作为前缀以指示八进制值,使用 0t 或 0T 作为前缀以指示十进制值,以及使用 0x 或 0X 作为前缀以指示十六进制值(缺省值)。
指定的十进制浮点值,已转换为其 IEEE 双精度浮点表示形式
通过将每个字符转换为等于其 ASCII 值的字节而计算的整数值。在字符常量中,最多可以指定八个字符。字符按相反顺序(从右到左)填入整数,从最低有效字节开始。
由 identifier 指定的变量值
由 identifier 指定的符号值
expression 的值
点值
用于执行 dcmd 的最新点值
按当前增量递增的点值
按当前增量递减的点值
增量是一个全局变量,它存储上一个格式设置 dcmd 读取的总字节。有关增量的更多信息,请参阅格式设置 dcmd的讨论。
一元运算符是右关联的,其优先级高于二元运算符。一元运算符如下:
逻辑否定
按位补码
整型否定
与目标虚拟地址空间中虚拟地址 expression 相对应的目标文件位置处指针大小量的值
与目标虚拟地址空间中虚拟地址 expression 相对应的目标文件位置处 char、short、int 或 long 大小量的值
与目标虚拟地址空间中虚拟地址 expression 相对应的目标文件位置处单字节、双字节、四字节或八字节量的值
目标虚拟地址空间中虚拟地址 expression 处指针大小量的值
目标虚拟地址空间中虚拟地址 expression 处 char、short、int 或 long 大小量的值
目标虚拟地址空间中虚拟地址 expression 处单字节、双字节、四字节或八字节量的值
二元运算符是左关联的,其优先级低于一元运算符。按最高到最低的优先顺序排列,二元运算符如下:
整数相乘
整数相除
将左侧值向上舍入为最接近的右侧值倍数
整数相加
整数相减
按位向左移位
按位向右移位
逻辑相等
逻辑不等
按位和
按位异或
按位或
前面介绍的每个元字符(请参见语法)都会终止字,除非用引号引起来。通过将字符放在一对单引号 (') 或双引号 (") 之中,可以将其引起来,以强制 MDB 将每个字符解释为其本身,而没有任何特殊意义。单引号不能出现在单引号内。在双引号内,MDB 可识别 C 编程语言字符转义序列。
可以使用 ! 字符创建 MDB 命令和用户的 shell 之间的管道。Shell 转义仅在使用 mdb(而不是 kmdb)时可用。如果设置了 $SHELL 环境变量,则 MDB 将为 shell 转义对此程序执行 fork 和 exec;否则使用 /bin/sh。shell 是使用后跟字符串的 -c 选项调用的,该字符串通过串联 ! 字符后的字而构成。
! 字符优先于所有其他元字符,但分号 (;) 和 NEWLINE 除外。检测到 shell 转义后,下一个分号或 NEWLINE 之前的其余字符将“按原样”传递到 shell。shell 命令的输出不能传输到 MDB dcmd。shell 转义执行的命令将其输出直接发送到终端而不是 MDB。
variable 是变量名称、对应的整数值和一组属性。变量名称是字母、数字、下划线或句点的序列。可以使用 > dcmd 或 ::typeset dcmd 为变量赋值,而且可以使用 ::typeset dcmd 处理其属性。每个变量的值都表示为 64 位无符号整数。变量可以具有一个或多个以下属性:只读(用户不能进行修改)、持久性(用户不能取消设置)和带有标记(用户定义的指示符)。
使用 /、\、? 或 = dcmd 列显的最新值
用于 $< dcmd 的最新计数
数据节基数的虚拟地址
与当前执行 kmdb 的 CPU 相对应的 CPU 标识符。
数据节的大小(以字节为单位)
入口点的虚拟地址
与软件事件说明符匹配的次数。请参见事件回调。
目标的主目标文件的初始字节(魔数);如果尚未读取目标文件,则为零
文本节的大小(以字节为单位)
当前代表线程的线程标识符。标识符的值取决于当前目标所用的线程模型。请参见线程支持。
此外,MDB 内核和进程目标将代表线程的寄存器集的当前值导出为命名变量。这些变量的名称取决于目标的平台和指令集体系结构。
如语法中所述,表达式上下文中存在的符号标识符的计算结果是此符号的值。该值通常表示与目标虚拟地址空间中符号关联的存储区的虚拟地址。目标可以支持多个符号表,包括但不限于:
主可执行文件符号表
主动态符号表
运行时链接编辑器符号表
许多装入对象(如用户进程中的共享库或 Solaris 内核中的内核模块)中每个装入对象的标准符号表和动态符号表
目标通常先搜索主可执行文件的符号表,然后搜索一个或多个其他符号表。请注意,ELF 符号表仅包含外部、全局和静态符号的项;自动符号不出现在 MDB 处理的符号表中。
此外,MDB 提供了专用的用户定义符号表,在搜索任何目标符号表之前都会先搜索该表。专用符号表最初为空,可以使用 ::nmadd 和 ::nmdel dcmd 处理它。
可以使用 ::nm -P 选项显示专用符号表的内容。通过专用符号表,用户可以为程序函数或者原始程序中缺少的或已删除的数据创建符号定义。然后,每当 MDB 将符号名称转换为地址,或将地址转换为最接近的符号时,都可以使用这些定义。
由于目标包含多个符号表,而且每个符号表可以包括多个目标文件中的符号,因此可能存在同名的不同符号。MDB 将反引号 “`“ 字符用作符号名称作用域运算符,以允许程序员在这种情况下获取所需符号的值。
可以指定用于将符号名称解析为以下任一内容的作用域:object`name、file`name 或 object`file`name。对象标识符引用装入对象的名称。文件标识符引用源文件的基本名称,该文件在指定对象的符号表中具有 STT_FILE 类型的符号。如何解释对象标识符取决于目标类型。
MDB 内核目标要求 object 指定已装入内核模块的基本名称。例如,符号名称:
specfs`_init
的计算结果是 specfs 内核模块中 _init 符号的值。
mdb 进程目标要求 object 指定可执行文件或已装入共享库的名称。它可以采用以下任一形式:
完全匹配(即,完整路径名): /usr/lib/libc.so.1
基本名称完全匹配: libc.so.1
与基本名称开头匹配(即取 ''.'' 后缀之前的部分):libc.so 或 libc
作为可执行文件的别名接受的文字字符串 a.out
进程目标还可接受上述四种形式前面加上可选的链接映射 id (lmid)。 lmid 前缀由初始 LM 后跟十六进制链接映射 id 以及附加反引号指定。 例如,符号名称:
LM0`libc.so.1`_init
的计算结果将是在链接映射 0 (LM_ID_BASE) 上装入的 libc.so.1 库中 _init 符号的值。 如果在多个链接映射上装入同一库,则解决符号命名冲突可能需要链接映射说明符。 有关链接映射的更多信息,请参阅《链接程序和库指南》和 dlopen(3C) 手册页。根据 showlmid 选项的设置列显符号时将显示链接映射标识符,如命令行选项摘要中所述。
如果符号和十六进制整数值之间存在命名冲突,则 MDB 尝试首先将不明确的标记计算为符号,然后将它计算为整数值。例如,标记 f 可以指以十六进制(缺省基数)指定的十进制整数值 15,也可以指目标符号表中名为 f 的全局变量。如果存在名称不明确的符号,则可以使用显式 0x 或 0X 前缀指定整数值。
如上所述,每个 MDB dmod 都提供了一组 dcmd 和 walker。dcmd 和 walker 是在两个不同的全局名称空间中跟踪的。MDB 还跟踪与每个 dmod 关联的 dcmd 和 walker 名称空间。在给定 dmod 内不允许存在名称完全相同的 dcmd 或 walker:具有此类型命名冲突的 dmod 将无法装入。
在全局名称空间中,允许在不同 dmod 的 dcmd 或 walker 之间存在名称冲突。 如果存在冲突,则优先装入全局名称空间中具有该特定名称的第一个 dcmd 或 walker。替换定义按装入顺序保存在列表中。
可以将反引号字符 “`“ 用作 dcmd 或 walker 名称中的作用域运算符以选择替换定义。例如,如果 dmod m1 和 m2 都提供了 dcmd d,而且 m1 是在 m2 之前装入的,则:
执行 m1 的 d 定义
执行 m1 的 d 定义
执行 m2 的 d 定义
如果当前卸载了模块 m1,则全局定义列表上的下一个 dcmd (m2`d) 将被提升为全局可见。可以使用 ::which dcmd 确定 dcmd 或 walker 的当前定义,如下所述。可以使用 ::which -v 选项显示全局定义列表。
使用 | 运算符可以将 dcmd 编入管道中。管道的用途是将值列表(通常为虚拟地址)从一个 dcmd 或 walker 传递到另一个 dcmd 或 walker。可以使用管道阶段将一种数据结构类型的指针映射到指向对应数据结构的指针,对地址列表排序,或者选择具有某些属性的结构的地址。
MDB 按从左向右的顺序执行管道中的每个 dcmd。最左侧的 dcmd 是使用点的当前值执行的,或者是使用命令开头的显式表达式指定的值执行的。遇到 | 运算符时,MDB 将在其左侧的 dcmd 输出和 MDB 解析器之间创建一个管道(共享缓冲区)以及一个空白值列表。
在 dcmd 执行时,其标准输出放置在管道中,然后由解析器使用和计算,好像 MDB 从标准输入读取此数据。每行都必须包含以 NEWLINE 或分号 (;) 结尾的算术表达式。将表达式的值附加到与管道关联的值列表。如果检测到语法错误,则中止管道。
在 | 运算符左侧的 dcmd 完成后,使用与管道关联的值列表调用 | 运算符右侧的 dcmd。对于列表中的每个值,将点设置为此值并执行右侧的 dcmd。只有管道中最右侧的 dcmd 才将其输出列显到标准输出。如果管道中的任何 dcmd 产生标准错误输出,则这些消息将直接列显为标准错误,而不作为管道的一部分进行处理。
/、\、? 和 = 元字符用于表示特殊的输出格式设置 dcmd。其中的每个 dcmd 都接受由一个或多个格式字符、重复计数或带引号字符串组成的参数列表。格式字符是下表所示的 ASCII 字符之一。
格式字符用于从目标读取数据和设置数据的格式。重复计数是格式字符前面的正整数,始终用基数 10(十进制)解释它。也可以将重复计数指定为括在方括号中、前面是美元符号的表达式 ($[ ])。必须用双引号 (" ") 将字符串参数引起来。格式参数之间不需要留空格。
格式设置 dcmd 包括:
显示目标的虚拟地址空间(从点指定的虚拟地址开始)中的数据。
显示目标的物理地址空间(从点指定的物理地址开始)中的数据。
显示目标的主目标文件中从与点指定的虚拟地址相对应的目标文件位置开始的数据。
按每种指定的数据格式显示点本身的值。因此 = dcmd 对于在基数之间转换和执行运算是很有用的。
除了点外,MDB 还跟踪名为 increment 的另一全局值。增量表示点和上一格式设置 dcmd 读取的所有数据后面的地址之间的距离。
例如,如果格式设置 dcmd 是在点等于地址 A 的情况下执行的,且显示一个 4 字节整数,则在此 dcmd 完成后,点仍为 A,但是增量设置为 4。算术展开中所述的 + 字符的计算结果现在是值 A + 4,并可以用于将点重置为后续 dcmd 的下一个数据对象的地址。
大多数的格式字符按与表中所示的数据格式大小相对应的字节数增加增量的值。使用 ::formats dcmd 可以从 MDB 内显示格式字符表。
格式字符包括:
按计数递增点(可变大小)
按计数递减点(可变大小)
十六进制 int(1 字节)
使用 C 字符表示法的字符(1 字节)
十进制带符号 int(4 字节)
十进制无符号 long long(8 字节)
双精度(8 字节)
八进制无符号 long long(8 字节)
交换字节和 short(4 字节)
地址和反汇编的指令(可变大小)
十六进制 long long(8 字节)
十六进制 uintptr_t(4 或 8 字节)
新行
八进制无符号 int(4 字节)
符号(4 或 8 字节)
八进制带符号 int(4 字节)
二进制 int(8 字节)
使用 C 字符串表示法的字符串(可变大小)
水平制表符
十进制无符号 int(4 字节)
十进制无符号 int(1 字节)
缺省基数无符号 int(4 字节)
十六进制 int(4 字节)
已解码的 time32_t(4 字节)
十六进制 long long(8 字节)
按增量 * 计数递减点(可变大小)
点作为符号+偏移
八进制无符号 int(1 字节)
字符(1 字节)
十进制带符号 short(2 字节)
十进制带符号 long long(8 字节)
浮点(4 字节)
八进制带符号 long long(8 字节)
交换字节(2 字节)
反汇编的指令(可变大小)
新行
八进制无符号 short(2 字节)
符号(4 或 8 字节)
八进制带符号 short(2 字节)
空格
原始字符串(可变长度)
水平制表符
十进制无符号 short(2 字节)
十进制带符号 int(1 字节)
缺省基数无符号 short(2 字节)
十六进制 short(2 字节)
已解码的 time64_t(8 字节)
/、\ 和 ? 格式设置 dcmd 还可以用于写入目标的虚拟地址空间、物理地址空间或目标文件,方法是将以下修饰符之一指定为第一个格式字符,然后指定作为即时值或括在方括号中且前面是美元符号的表达式 ($[ ]) 的字的列表。
写入修饰符包括:
将每个表达式值的最低字节写入从点指定的位置开始的目标
将每个表达式值的最低 2 字节写入从点指定的位置开始的目标
将每个表达式值的最低 4 字节写入从点指定的位置开始的目标
将每个表达式值的完整 8 字节写入从点指定的位置开始的目标
/、\ 和 ? 格式设置 dcmd 还可以分别用来在搜索目标的虚拟地址空间、物理地址空间和目标文件中搜索特定的整数值,方法是将以下修饰符之一指定为第一个格式字符,然后指定值和可选掩码。值和掩码均指定为即时值或括在方括号中且前面是美元符号的表达式。
如果仅指定了值,则 MDB 读取相应大小的整数,并停止在包含匹配值的位置。如果指定了值 V 和掩码 M,则 MDB 读取相应大小的整数,并停止在包含值 X(其中 (X & M) == V)的位置。dcmd 完成时,点将更新为包含匹配项的地址。如果找不到匹配项,则点停留在读取的最后一个地址处。
搜索修饰符包括:
搜索指定的 2 字节的值
搜索指定的 4 字节的值
搜索指定的 8 字节的值
对于用户目标和内核目标,地址空间通常都由一组不连续的段组成。从没有对应段的地址中读取是非法的。如果搜索到达了段边界但未找到匹配项,则在超过段边界结尾的读取失败时它将异常中止。