微软编译器中寄存器的使用

来源:互联网 发布:地牢生成算法 编辑:程序博客网 时间:2024/05/16 08:24
 

微软编译器中寄存器的使用

原文地址:

http://www.skullsecurity.org/wiki/index.php/Registers

翻译:

本文是汇编语言指南的第一篇,若你准备阅读整个指南,你必将有所收获。

一个寄存器就像变量,只是这种变量的数量是固定的。寄存器是CPU中用来保存数据的地方。数学计算(加法、减法..)只能在寄存器中进行;寄存器常常保存着内存地址;寄存器与内存中数据的相互转移也是经常的事情。

Intel CPU有8个32位通用寄存器:eax, ebx, ecx, edx, esi, edi, ebp, esp.虽然数据可以再这些寄存器之间互相移动,但是编译器常常对于特定功能使用特定的寄存器,而且一些指令(比如乘法、除法)只能使用的CPU设计时指定的寄存器。

不同的编译器在如何使用不同寄存器时会有完全不同的习惯。本文的目的主要讨论微软的编译器如何使用这些寄存器的。

内容

1.易失性(Volatility)

2.通用寄存器

2.1 eax

2.2 ebx

2.3 ecx

2.4 edx

2.5 esi

2.6 edi

2.7 ebp

2.8 esp

3.16位和8位寄存器

4. 64位寄存器

 

 

1.易失性

一些寄存器在函数中常常是变化的,而另外一些却是不变的。这是编译器所决定的。因为寄存器是不会自动保存的(虽然有些汇编语言会自动保存,但是x86是不会的),所以编码时要自己保存。这句话的意思是:当一个函数被调用,是不保证在函数返回时,易失寄存器上的值不变的;但是函数必须负责保存非易失寄存器中的值。

微软编译器的寄存器使用习惯如下:

 

1)易失寄存器:ecx, edx

2)非易失寄存器: ebx, esi, edi, ebp

3)其他特殊寄存器: eax, esp (discussed later)

2. 通用寄存器

本节来了解一下x86架构下的8个通用寄存器。至于其他的特殊寄存器和浮点寄存器,可以参看wikipedia中的文章或其他网站。

2.1 eax

Eax是一个32为通用寄存器,一般的用途有两个:保存函数的返回值或者作为计算用的专用寄存器。在技术上来说,eax是一个易失寄存器,因为他的值是不能保存的,eax的值会在函数返回前设置为函数的返回值。除了ESP寄存器,EAX的这个功能(即保存返回值)可能是最重要的值得被记住的。eax还会在数据计算时要用,比如乘法和除法。这种用法我们会在指令使用一文讲解。

下面是C语言中函数返回的例子:

return 3;  // 返回3

对应的汇编语言:

mov eax, 3 ; 置EAX=3

ret        ; 返回

2.2 ebx

Ebx是一个非易失通用寄存器。它没有特定的用途,但是常被置为一个函数中常用的值(如0),以此来加快计算速度。

2.3 ecx

Ecx是一个易失通用寄存器。常被用作函数的参数或者是循环的计数器。

__fastcall的函数会将第一和第二个参数放置在ecx和edx寄存器中。另外,当调用一个类中的成员函数时,不管调用习惯是什么,指向类的指针常常是放在ecx中。

另外,ecx常被用作循环计算器。For循环一般(尽管不是总是)会将循环计数放在ecx中。Rep指令也会将ecx作为计数器,自动减少直到为0.这类功能将在后面讨论。

2.4 edx

Edx是一个易失通用寄存器,偶尔会被用作函数的参数。就像ecx,edx常用在__fastcall调用的函数中。

除了fastcall调用中存放参数,编译器通常会将局部(短期)变量储存在edx中。

2.5 esi

Esi是一个非易失通用寄存器,常被用作指针。特别的,在rep一类的指令中,esi通常指向“源”。因为esi中数据是不会改变的,所以esi通常会存储不会变的数据。

2.6 edi

Esi是一个非易失通用寄存器,常被用作指针。它和esi差不多,只不过一般是作为“目标”指针。

2.7 ebp

Ebp是一个非易失通用寄存器,根据编译器的设置,它有两个截然不同的用途:要么作为框架指针,要么作为一般寄存器。

若没有优化编译或者代码是手工写的,ebp会在函数开头就保存着堆栈的位置(关于堆栈下文会详细讨论)。因为堆栈在整个函数过程中是不停变换的,将ebp指向堆栈的原始位置可以使得方便使用存储在堆栈中的变量。这会在讨论堆栈是详谈。

如果编译被优化,当堆栈指针的计算通过指针的移动值计算时(这可能有点乱——IDA就是自动侦测并校准一个移动的堆栈指针),ebp就会作为一个保存任何数据的通用寄存器了。。

2.8 esp

最后说说esp,esp是一个保存着堆栈底端指针的特殊寄存器(堆栈是向低地址生长)。很少直接对ESP进行数学计算(加减),而且函数的开始和结束时的ESP值必须一致。Esp下文介绍。

3. 16位和8位寄存器

除了有8个有效的32位寄存器,CPU还有一些16位和8位寄存器。有点让人困惑的是,他们(16/8位寄存器)和32位寄存器使用相同的存储空间。换句话说,每个16位寄存器是32位寄存器的一半,所以改变16位寄存器同样会改变32位寄存器的值。同样的,8位寄存器是16位寄存器的一半。

比如,eax是32位寄存器。EAX的低一半是一个16寄存器AX,AX被分成两个8位寄存器:AH,AL.

1)8个32位寄存器:eax, ebx, ecx, edx, esi, edi, ebp, esp

2)8个16位寄存器:ax, bx, cx, dx, si, di, bp, sp.

3)8个8位寄存器:ah, al, bh, bl, ch, cl, dh, dl.

这些寄存器的关系如下:

32位

eax

 

ebx

 

ecx

 

edx

16位

 

ax

 

bx

 

cx

 

dx

8位

 

 

ah

al

 

 

bh

bl

 

 

ch

cl

 

 

dh

dl

 

32位

esi

 

edi

 

ebp

 

esp

16位

 

si

 

di

 

bp

 

sp

                    

举两个例子:

 

Eax

0x12345678

Ax

0x5678

Ah

0x56

Al

0x78

 

Ebx

0x00000025

Bx

0x0025

Bh

0x00

Bl

0x25

4. 64位寄存器

一个64位寄存器由2个32个寄存器组成,在两个寄存器中用“:”隔开。

最普通的64位寄存器(在乘除法中用到)是edx:eax.这表示32位寄存器EDX放在32位寄存器EAX前面,用来建立8字节寄存器。

举例如下:

Edx

0x11223344

Eax

0xaabbccdd

Edx:eax

0x11223344aabbccdd