CPU对软件调试的支持(二)

从38 6 开始 , IA 一3 2 处理器 内部都含有 8 个 3 2 位的调试 寄存器 DR0一 DR7 (如 图 1 所 示 )。 除了 DR4 和 DR5 保留 外 , 其它 6 个寄存器 分别是 :

  • 四 个 3 2 位的调试地址 寄存器 (DR0~DR3)
  • 一个 3 2 位的调试控制寄存器 (DR7) 
  • 一个 3 2 位的调试状态寄存器 (DR6) 

通过 以上寄存器可 以最多设置 4 个断点, 其基本分工是DR0一DR3 用来指定断点的内存 (线性地址) 或l/ O 地址 。 DR7 用 来进一步定义断点的中断条件。 DR6的作用是当调试事件发生向调试器 《d e b g u g , ) 报告事件的类别和属性 . 以供调试器判断发生 的是何种事 件 (哪个断点 、 单步跟踪 、 断点属性等等 )。 下面分 别详细介绍 D R 7 和DR6 的用法 。

3 2 位的DR7寄存器 中 , 有2 4 位是被划分成 4 组分 别与 4 个调试地址寄存器相对应的.比如 L0 . G0 . R/ W0 和 LEN0这 六位都是 与DR0相对应的 .L1 . G1. R/ W1 和 LEN1 这六位都是与DR1相对应的 依此类推。表 1 列出了DR7 中各个位的具体含义。

调试状态寄存器 DR6 是 当 CPU 检测到匹配 断点 条件的断点时 . 用来向调试器的断点异常处理程序传递该断点异常的具体情况 . 以使调试器可 以很容易 的识别 出发 生的是什么断点 。 比如如果 BO被设置为1, 那么就说 明满足 DR0、 L E N 0、和 R/ W0 所定义条件 的断点发 生了 。 下面的表分 别列出了 DR6 中各个位的具体含义。

通过表 1 的定义可 以看出 . 调试控制寄存器的各个位域提 供了很灵活 的方式 .允许我们通过不同的组合定义出各种复杂的断点条件。 下面我们先进一步介绍一下读写域R/ Wn , 通过 对它的设里 , 我们可 以指定要定义的断点的访问类型 (数据 、代码还是 l/ 0 ) . 即断点的访问条件 :
1、读写内存中的数据时中断 , 这种断点又被称为数据访 问断点 (data breakpointer )。 利用数据访问 断点 可 以监 控对全局变量 , 或局部变量的读写操作。 例如 . 在进行某些复杂的系统级调试 . 或者调 试多线程程序时 , 我们不知道哪个线程在何时修改了某一变量,这时我们就可 以设置一个数据访问断 点。 现代调试器大多还 都支持复杂的条件断点, 比如 当某个变 量等于某个确定的值时中断 . 这其实也是利用数据访问断点实 现的 . 其基本思路是设置 一个数据访问断点来监视这个变量 , 每次这个变量改变时 CPU 都会调用调试器 的中断处理程序 ,调试器检查这个变量的值 . 如 果不 满足规定的条件就立刻返回让 CPU 继续执行 。 如果满足 , 就中断到调 试环境。
2、执行 内存中的代 码时 中断. 这 种断点 又被称为代码访问断点 (Code Br e a k Po in t 》或指令断点 (i n s tr u e t io n b r e a k po in t ) 。 代码访问断点从实现 的功 能上 看与软件断点类似 , 都是 当 C PU 执行到某个地址 的指令时 中断。但是通 过寄存器 实现的代码访问断 点具有一个软件断点无法 实现 的优点 , 就是不 需要 软 件断点那样向 目标代码处插入指令 。 这个优点在某些 情况 下非 常重要 。 例如 . 当我们调 试位于 RO M (只读存储器 ) 上的 代码 《比 如 BIO S 中的 PO S T 程序 ) 时 根本没 有办法向那里 插入软件断点 ( lNT 3 ) 指令 . 因为 目标内存是只 读的 。 另外软件断点的另 一个局限是 只有 当 目标代码被加载进内存后才可 以 向该区域设置软件断点。而调 试寄存器 断点没有这 些限 制. 因为只 要把需 要 中断的内存地址 放入调试地址 寄存器 (DR0一DR3 ) . 并设置好调试控制寄存器 ( DR7 ) 的相应位就可 以 了。

3、读写 I/O(输入输出 ) 端口时中断 .这种断点又被称为I/O访问断点 ( I  /O   b r e a kp oi n t ) 。I/ O 访问断点对于调试设计使用输入输出端口的设备驱动程序非常有用。 也可以利用 I/O 访问断点来监视对I/O空间的非法读写操作 . 提 高系统的安全性 。 因为某些恶意程序在实现破坏动作时 . 需要对某些 I/O 端 口进行读写操作 。

读写域定义 了要监视的访问类型 . 地址 寄存器(DR0一DR3 ) 定义 了要监视的起始地址 。 那么要监视的区域长度呢 , 这便是长度域 L E N n (n= 0, 1, 2,3 . 位于 D R 7 中 ) 的任务。L EN n 位段可 以指定 1,2,4 或 8 字节长的范围。 需要说明的是 :
1、对于代码访 问断点 . 长度域应该为00,代表一字节长 度 。 另外地址寄存器应该指向指令的起始字节 。 也就是 CPU 只会用指令的起始字节来检查代码 断点 匹配 。
2、对于数据和 I/O访问断点 . 有两点需要注意 :
第一 只 要 断点区域中的任一字节在被访问的范围 内 . 都会触发该断点。
第二 , 边界对齐要求 . 两字节区域必 须按字 (w o r d ) 边界对齐 : 四字节区域必须按双 宇 (d o u b l e w o r d ) 边界对齐 : 八字 节区城必 须按四字 (qu a d w o r d ) 边界对齐。 也就是说 . CP U 在 检查断点匹配时会 自动去除相应数量的低位 。 因此如果地址没 有按耍求对齐可能无法实现预期的结果。 例如 假设希望通过将 DR0 设为0 x A 003.L E N 0 设为 1 1 (代表 4 字节长)实现任何对0 x A00 3 ~0 x A c 0 6 内存区的写操作都会触发断点: 那么只有当0 x A 00 3 被访问时会触发断点 . 对0x A 00 4、0x A 00 s 和0x A006 处的内存访问都不会触发断点 。 因为长度域指定的是4 字节 所以CPU 在检查地址匹配时 . 会自动将起始地址0x A003 的低4 位屏蔽掉 , 也就是会被看作是0x A00 )。 表3 给出了更多的例子 用来说明断点的触发条件 。

因为以上介绍的断点不需要像软件断点那样向代码中插入 软件指令 , 依靠处理器本身的功能便可 以实现 . 所以人们习惯 上把这些使用调试寄存器 (DR0~DR7 ) 设置的断点叫硬件断点 ,以与软件断点区别开来 。
l A 一 32 处理器专门分配了两个中断向量来处理调试异常 向量1 和向量3。 向量3 用来处理 INT 3 指令产生的断点异常。 向量1 用来处理调试异常(de b u g e x c e pt io n . 简称 # DB ) 。 硬件断点产生的是调试异常 .所以硬件断点发生时C U P 会执行 1 号向量对应的处理例程 。

硬件断点其 有很多优点 , 但是也有不足 , 最明显的就是数量限制因为只用 4 个调试地址寄存器 . 所以 lA 一3 2 Cpu 允许 最多设置 4 个硬件断点。 这基本可以满足大多数情况下的调试 需要 。

另一点祷要说明的是 . 只有在实模式或保护模式的内核优先级( ring0 ) 下 才能访问调试寄存器 . 否则便会导致保护性 异常。 这是 出于安全性的考虑. 那么像 v i s u a . s t u d i o 这样的用 户态调试器是如何设一硬件断点 (VC 6 支持数据访问断点。 没 必 要也不支持 l/ O 访问断点 , 因为从 Win d o w s 9 8 开始用户态 下不允许进行直接 l/ O 读写 ) 的呢 ? 答案是通过访问线程 的C ON E T X T 数据 (每个线程被挂起时 . 寄存器等上下文信息都被 保存起来 . 当该线程恢复执行时 . 寄存器会被恢复回来) 来间 接访问调试寄存器 。

下面给出一个C + + 例子演示一下如何手工设置 数据访问断点.

 

猜你喜欢

转载自www.cnblogs.com/yilang/p/12132785.html