
AI-摘要
PrideLzh GPT
AI初始化中...
介绍自己 🙈
生成本文简介 👋
推荐相关文章 📖
前往主页 🏠
前往爱发电购买
8-分支程序设计
pridelizihao一、分支程序设计
(1)分支程序设计示例
1. 两种分支结构
- if结构(图a);if-else结构(图b)
- 需要注意一下用汇编写if-else结构的时候,if分支结束后要用无条件转跳过else分支,后面详细说明
2. 简单分支示例
- 有简单分支程序
1 | //大写字母转小写 |
- 现在把它反汇编(关闭优化)
1 | //子过程cf315 |
- 现在再打开优化反汇编一次
1 | _asm |
- 观察到的优化手段:
- 巧妙地把两个分支减少到一个
- 充分利用寄存器,减少从内存取值
3. 双分支示例
- 有双分支程序
1 | //把十进制数m转十六进制字符的ASCII码 |
- 现在把它反汇编(关闭优化)
1 | _asm |
- 现在打开优化再反汇编一次
1 | _asm |
观察:
- 如果不开优化,参数或局部变量都存在堆栈,要修改必须要进行:取到Reg,修改,写回堆栈三步
- 汇编程序是自上往下顺序执行的,(不像C语言中if-else可以选择分支执行;while、for可以循环执行一段代码),它只能用jcc指令修改下一条程序代码的位置,就好像不能用if、else、while、for,只能用goto的C程序。因此像if-else结构,必须在if分支最后jmp跳过else,否则会顺序把else也执行一次。
- 优化手段:
- 用寄存器减少从内存取值
- 避免了jmp指令,两个分支不再合并而是各自返回。减少跳转次数
- 优化源程序
1 | int cf317(int m) |
- 小结:
- 靠编译器自动优化,再好也是依赖于源C程序的,要想真正提高程序效率,还得从源程序上改进
- 优化策略:
- 减少内存的存取数据,多用寄存器
- 减少跳转数量
- 避免时钟数多的指令(右移代替除法)
- 减少循环次数
- 用内联函数减少call和ret
(2)无条件和条件转移指令
1. 基本概念
段内转移(近转移)
:转移时只重置指令指针寄存器EIP
,不重置代码段寄存器CS
,段间转移(远转移)
:转移时重置指令指针寄存器EIP
和代码段寄存器CS
- 转移类型判断
转移 | 属于段内还是段间 |
---|---|
条件转移 | 段内 |
循环指令 | 段内 |
无条件转移 | 段内或段间 |
过程调用和返回 | 段内或段间 |
软中断指令 | 段间 |
中断返回指令 | 段间 |
直接转移
:转移指令中直接给出转移目的地址间接转移
:转移指令中给出包含转移目的地址的寄存器或存储单元
2. 无条件转移指令
- 无条件段内直接转移
- 无条件段内直接转移指令的机器码格式
- 操作码OP:转移指令的机器码
- 地址差rel:转移目标地址偏移(标号LABEL所指定指令的地址偏移)与紧随JMP指令的下一条指令的地址偏移之间的差值。
- rel会被汇编器自动计算,并自动选取为8/16/32位来表示。如果只用了8位,就称为短(short)转移。
- 如果程序不能自动计算地址偏差了多少,用32位来表示rel
- 如果编程时可以判断地址偏差不超过8位范围,可以用SHORT指令强制汇编器用8位表示rel
- 由于rel是有符号数,转移方向可以向前也可以向后
- 执行无条件段内转移指令时,把指令中的地址差rel加到指令指针寄存器EIP上,使EIP之内容为转移目标地址偏移,从而实现转移。
- 无条件段内直接转移指令的机器码格式
名称 | jmp(无条件段内直接转移指令) |
---|---|
格式 | jmp label |
动作 | 下一条指令转移到 label 处执行 |
- 无条件段内间接转移
名称 | jmp(无条件段内间接转移指令) |
---|---|
格式 | jmp OPDR |
动作 | 指令使控制无条件地转移到由操作数OPRD的内容给定的目标地址处 |
合法值 | OPDR:32位寄存器、32位存储单元 |
注意 | OPRD内容直接被装入指令指针寄存器EIP,从而实现转移 |
3. 条件转移指令
- 之前的文章已经写过,见
jcc
相关部分: - 条件转移指令通过判断状态标志确定转移是否发生,但是本身不影响标志状态
- 也是通过
label
标记确定转移位置,在机器码层面的实现和无条件段内直接转移一样
(3)多分支的实现
- 多分枝类似C中的
switch-case
- 源程序
1 | //示例函数cf319 |
- 进行反汇编
1 | //返汇编(速度最大化) |
分析
- 最后
LN12cf319
部分,开启了多向分支目标地址表,DD
代表地址表中每一项占4个字节,因此LN12cf319+4*0
就是LN6cf319
;LN12cf319+4*1
就是LN5cf319
,依次类推 jmp DWORD PTR LN12cf319[ eax*4 ]
这句,跳转到地址LN12cf319+eax*4
执行,这是实现switch-case的关键。注意eax要改到0起始- 空位置
default
必须留出,否则转换过来的时候会出错 - 可以看到地址表是按地址排列的,适用于多分枝且没有大空洞的情况。如果各个case间有大间距,会导致地址表中
default
项过多,最好预处理一下,或者改用if-else逻辑
评论
匿名评论隐私政策
TwikooValine
✅ 你无需删除空行,直接评论以获取最佳展示效果