2.1 通用 CPU 寄存器
CPU 的寄存器能够对少量的数据进行快速的存取访问。在 x86 指令集里,一个 CPU 有 八个通用寄存器:EAX, EDX, ECX, ESI, EDI, EBP, ESP 和 EBX。还有很多别的寄存器,遇 到的时候具体讲解。这八个通用寄存器各有不同的用途,了解它们的作用对于我们设计调试 器是至关重要的。让我们先简略的看一看每个寄存器和功能。最后我们将通过一个简单的实 验来说明他它们的使用方法。
EAX 寄存器也叫做累加寄存器,除了用于存储函数的返回值外也用于执行计算的 操作。许多优化的 x86 指令集都专门设计了针对 EAX 寄存器的读写和计算指令。列如从最 基本的加减,比较到特殊的乘除操作都有专门的 EAX 优化指令。
前面我们说了,函数的返回值也是存储在 EAX 寄存器里。这一点很重要,因为通过返 回的 EAX 里的值我们可以判断函数是执行成功与否,或者得到确切返回值。
EDX 寄存器也叫做数据寄存器。这个寄存器从本质上来说是 EAX 寄存器的延伸, 它辅助 EAX 完成更多复杂的计算操作像乘法和除法。它虽然也能当作通用寄存器使用,不 过更多的是结合 EAX 寄存器进行计算操作。
ECX 寄存器,也叫做计数寄存器,用于循环操作,比如重复的字符存储操作,或 者数字统计。有一点很重要,ECX 寄存器的计算是向下而不是向上的(简单理解就是用于 循环操作时是由大减到小的)。
看一下下面的 Python 片段:
counter = 0
while counter < 10:
print "Loop number: %d" % counter
counter += 1
如果你把这代码转化成汇编代码,你会看到第一轮的时候 ECX 将等于 10,第二轮 的时候等于 9,如此反复知道 ECX 减少到 0。这很容易让人困惑,因为这和 Python 的循环 刚好代码相反,但是只要记得 ECX 是向下计算的就行了。
在 x86 汇编里,依靠 ESI 和 EDI 寄存器能对需要循环操作的数据进行高效的处理。 ESI 寄存器是源操作数指针,存储着输入的数据流的位置。EDI 寄存器是目的操作数指针, 存储了计算结果存储的位置。简而言之,ESI(source index)用于读,EDI(destination index) 用于写。用源操作数指针和目的操作数指针,极大的提高了程序处理数据的效率。
ESP 和 EBP 分别是栈指针和基指针。这两个寄存器共同负责函数的调用和栈的操 作。当一个函数被调用的时候,函数需要的参数被陆续压进栈内最后函数的返回地址也被压 进。ESP 指着栈顶,也就是返回地址。 EBP 则指着栈的底端。有时候,编译器能够做出优 化,释放 EBP,使其不再用于栈的操作,只作为普通的寄存器使用。
EBX 是唯一一个没有特殊用途的寄存器。它能够作为额外的数据储存器。 还有一个需要提及的寄存器就是 EIP。这个寄存器总是指向马上要执行的指令。当 CPU 执行一个程序的成千上万的代码的时候,EIP 会实时的指向当前 CPU 马上要执行到的位置。
一个调试器必须能够很方便的获取和修改这些寄存器的内容。每一个操作系统都提供 了一个接口让调试器和 CPU 交互,以便能够获取和修改这些值。我们将在后面的操作系统 章节详细的单独的讲解。