
AI-摘要
PrideLzh GPT
AI初始化中...
介绍自己 🙈
生成本文简介 👋
推荐相关文章 📖
前往主页 🏠
前往爱发电购买
10-子程序设计
pridelizihao一、子程序设计要点
两种传参方法
- 寄存器
- 堆栈
调用约定
决定了到底怎么传参,在C语言写函数定义时,写以下关键词来显示指定调用约定,
如void _fastcall cf330(unsigned m, char *buffer)
指定了约定方式为_fastcall安排局部变量
- 子程序需要一些局部变量,限于子程序部分
- 寄存器可以作为局部变量提高效率,但是寄存器的数量过少,一般不把局部变量安排在寄存器中
- 使用堆栈来安排局部变量,较为复杂,但是可以安排足够多的局部变量
- 用堆栈要控制esp指针位置
- 如果局部变量数量少,可以push一个寄存器进去,如果数量多,可以直接修改esp的值,然后用堆栈操作赋值
保护寄存器的约定
- 子程序可能会破坏某些寄存器内容。为此必须对有关寄存器的内容进行保护与恢复。
- 事前压入堆栈,事后从堆栈弹出。在利用堆栈进行寄存器的保护和恢复时,一定要注意堆栈的先进后出特性,一定要注意堆栈平衡
- 可能会降低效率。
- 需要主程序和子程序之间的“默契”和“约定”。子程序只保护主程序关心的那些寄存器,通常保护ebx、esi、edi和ebp。
描述子程序的说明
- 在给出子程序代码时,应该给出子程序的说明信息。
- 子程序说明信息一般包括:
- 子程序名(或者入口标号);
- 子程序功能描述;
- 子程序的入口参数和出口参数;
- 所影响的寄存器等情况;
- 使用的算法和重要的性能指标;
- 其他调用注意事项和说明信息;
- 调用实例。
二、子程序举例说明
1 | //子程序名(入口标号):BTOHS |
三、子程序调用方法
(1)调用指令
分类
段内直接调用
段内间接调用
段间直接调用
段间间接调用段内直接
名称 | call(段内直接调用指令) |
---|---|
格式 | CALL LABEL |
动作 | 把调用指令下一行指令地址压栈,然后转到LABEL处执行 |
注意 | 除了保存返回地址,其他同无条件转JMP |
- 段内间接
名称 | call(段内间接调用指令) |
---|---|
格式 | CALL OPDR |
动作 | 把调用指令下一行指令地址压栈,然后OPDR内容送到EIP,转到OPDR给出偏移地址处执行 |
合法值 | OPDR:保护方式下,32位通用寄存器、双字存储单元 |
注意 | 除了保存返回地址,其他同无条件转JMP |
1 |
|
- 函数指针
1 | //源C程序 |
可以看到
- 指针的本质就是地址
- 这里把函数入口和函数参数都放在堆栈,用堆栈传参。
- 注意传参时从ESP开始向高地址找参数,push参数的时候按从右到左的顺序
- 采用的是段内间接调用的方法
(2)返回指令
分类
- 按段内段间分
- 段内返回指令(对应段内调用)
- 段间返回指令(对应段间调用)(不介绍)
- 按返回时是否平衡堆栈
- 不带立即数的返回指令
- 带立即数的返回指令
- 按段内段间分
段内返回不带立即数
名称 | RET(段内返回不带立即数指令) |
---|---|
格式 | RET |
动作 | 指令从堆栈弹出地址偏移,送到指令指针寄存器EIP,返回到call时压栈的返回地址处执行 |
- 段内返回带立即数
名称 | RET(段内返回带立即数指令) |
---|---|
格式 | RET count |
动作 | 指令从堆栈弹出地址偏移(当然这也会影响esp),送到指令指针寄存器EIP,还额外把count 加到ESP |
注意 | 用于平衡堆栈 |
四、示例
- 以下是一个全汇编程序示例,它将十六进制数字符串转为数值(二进制),再转十进制输出查看,可以看一下函数调用的各种方法。
- 此程序是按8086机资源写的,在64位机器上运行此程序,需要:
- 保存以下代码为
.asm
文件 - 用nasm编译成
.com
文件 - 用DOSbox模拟8086环境运行
- 保存以下代码为
1 | ;说明:将十六进制数字符串转为数值(二进制),再转十进制输出查看 |
评论
匿名评论隐私政策
TwikooValine
✅ 你无需删除空行,直接评论以获取最佳展示效果