对标号地址的另一种相对寻址方式

本文介绍了一种在汇编程序中实现数据访问的方法,利用call指令实现运行期标号地址的相对寻址,使汇编代码能适应不同的内存地址空间。
汇编程序中, 对数据访问时, 通常是这样的:

_asm{
...
DATA_LABLE:
    _emit 0x87
    _emit 0xa0
    _emit 0x49
    _emit 0x90
...

    mov ebx, dword ptr [DATA_LABLE]
...
}

其中, 当程序编译之后, mov指令中的DATA_LABLE标号地址会被转成一个绝对地址. 而有时绝对地址这一点可能会对这样一种需求带来障碍: 我们希望自己写的汇编代码不管被放在哪块地址空间中都是能正常运行的, 就象我们写高级语言中的那些函数一样, 函数位置可以被随意放置, 丝毫不会影响函数本身的功能使用. 当然, 不得不指出的是, 尽管要求的是同样功能, 但汇编与高级语言之间在这方面的实现却相去甚远. 高级语言的函数在最终被编译之后, 其函数地址也是固定的绝对地址, 而我们所想要用汇编实现的才是真正的"可被任意放置"的二进制执行块.

借用call指令, 可以实现运行期标号地址的相对寻址, 大致的思路如下:

_asm{
...
    call FUNC_START
FUNC_START:
    pop ebx
    sub ebx, offset FUNC_START
    mov [ebp-xx], ebx
...

DATA_LABLE:
    _emit 0x87
    _emit 0xa0
    _emit 0x49
    _emit 0x90

...
    mov eax, [ebp-xx]
    mov ebx, dword ptr [DATA_LABLE+eax]
...

}

步骤是这样的:

1.首先, 在汇编功能块或函数首部, 使用以下语句取得运行期地址与编译地址的修正差值.
    call FUNC_START
FUNC_START:
    pop ebx
    sub ebx, offset FUNC_START
    mov [ebp-xx], ebx

略作解释: call 函数会将eip寄存器压入堆栈, 之后用"pop ebp"是将eip值赋给ebp, 而eip表示的是"下一条语句的地址", 在这里, 当程序运行到"call FUNC_START"时, 它表示的是以标号"FUNC_START:"开始的"pop ebx"指令起始地址. 而另一方面, sub指令中的"offset FUNC_START", 在编译时, offset会被转成一个绝对地址. 这样,通过sub操作, 就获得了此段代码在编译期和运行期关于指令地址的修正值. 下面的这句: "mov [ebp-xx], ebx", 实际上只是锦上添花, 它把这个值保存在了某一个自定义的函数局部变量空间内, 以备后续语句方便引用.

2.相应的, 对标号数据的引用就变成这样的两句:
    mov eax, [ebp-xx]
    mov ebx, dword ptr [DATA_LABLE+eax]

对于汇编函数中的此类代码进行这样的处理后, 此段二进制执行块就可以被放置在任意地方而不致因为对DATA_LABLE数据地址的错误引用造成程序错误.


### 直接寻址 直接寻址是指在转移指令中直接给出转移的目标地址。在这种寻址方式下,指令中包含了程序要跳转的具体地址,CPU 会直接根据这个地址进行程序流程的转移。 #### 段内直接转移 段内直接转移是指转移发生在当前代码段内,只需要改变指令指针寄存器(IP)的值,代码段寄存器(CS)的值保持不变。在汇编语言中,通常使用 `JMP` 指令加上目标地址标号来实现段内直接转移。例如: ```asm JMP Label ; 跳转到标号 Label 处 ``` 这里的 `Label` 就是目标地址标号汇编器会将其转换为具体的偏移地址。当 CPU 执行到这条指令时,会将 IP 的值修改为 `Label` 对应的偏移地址,从而实现程序的跳转。 #### 段间直接转移 段间直接转移意味着程序要从当前代码段跳转到一个代码段,需要同时改变 CS 和 IP 的值。在汇编语言中,使用 `JMP FAR PTR` 指令加上目标地址标号来实现段间直接转移。例如: ```asm JMP FAR PTR Label ; 跳转到一个代码段的标号 Label 处 ``` 这里的 `Label` 代表一个代码段中的目标地址,CPU 会将 CS 的值修改为 `Label` 所在段的段地址,将 IP 的值修改为 `Label` 在该段内的偏移地址,以此实现跨段的程序跳转。 ### 相对寻址 相对寻址一种基于当前指令地址寻址方式,转移地址是通过当前指令地址加上一个偏移量来计算得到的。这个偏移量通常是一个有符号数,它可以是正数或负数,分别表示向前或向后跳转。 #### 段内相对转移 段内相对转移主要用于在当前代码段内进行跳转,通常使用相对短转移(SHORT)或相对近转移(NEAR)。相对短转移的转移范围为 -128 到 +127 字节,相对近转移的转移范围为 -32768 到 +32767 字节。在汇编语言中,使用 `JMP` 指令加上相对偏移量或标号来实现段内相对转移。例如: ```asm JMP SHORT Label ; 短转移到标号 Label 处 JMP NEAR PTR Label ; 近转移到标号 Label 处 ``` 当 CPU 执行到这些指令时,会根据当前指令的地址和偏移量计算出目标地址,然后将 IP 的值修改为目标地址,从而实现程序的跳转。 #### 相对寻址的优势 相对寻址的主要优势在于它的代码具有更好的可移植性和灵活性。由于转移地址是相对于当前指令地址计算得到的,因此无论程序被加载到内存的哪个位置,相对转移指令都能正确地跳转到目标地址。这使得程序在不同的内存环境中都能正常运行,而不需要进行额外的地址调整。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值