assembly - 变量参数的 stdcall (callee-pops) 中的堆栈清理
问题描述
stdcall
我正在学习一些组装的乐趣(目前在 Windows 上使用 NASM),并且我对调用约定和具有可变数量参数的函数有疑问。例如,一个sum
函数接受 X 个整数并将它们加在一起。
由于被调用者在使用时需要清理/重置堆栈,但stdcall
您只能使用常量值ret
与. 我认为这会更慢,因为它需要更多指令,但可以接受吗?pop
esp
ret
; int sum(count, ...)
sum:
mov ecx, [esp+4] ; count
; calc args size
mov eax, ecx ; vars count
inc eax ; + count
mov edx, 4 ; * 4 byte per var
mul edx
mov edx, eax
xor eax, eax ; result
cmp ecx, 0 ; if count == 0
je .done
inc ecx ; count++, to start with last arg
.add:
add eax, [esp+4*ecx]
dec ecx ; if --ecx != 1, 0 = return, 1 = count
cmp ecx, 1
jnz .add
.done:
pop ebx
add esp,edx
jmp ebx
我不明白为什么这不行,而且它似乎可以工作,但是我读过一些文章,这些文章谈到了如何stdcall
不能处理变量参数,因为函数不知道要传递什么值ret
。我错过了什么吗?
解决方案
ret imm
如果参数的大小是常数,当然可以。如果函数能够在运行时确定其参数的大小,您的想法将起作用,在这种情况下,它可以从count
参数中确定,尽管正如ecm 指出的那样,它可能效率低下,因为间接分支预测器不是为此类恶作剧而设计的.
但在某些情况下,被调用函数可能根本不知道参数的大小,甚至在运行时也不知道。考虑printf
。您可能会说它可以从格式字符串中推断出其参数的大小;例如,如果格式字符串是,"%d"
那么它应该知道int
传递了一个,因此从堆栈中清除额外的 4 个字节。但是在 C 标准下调用是完全合法的
printf("%d", 123, 456, 789, 2222);
需要忽略多余的参数。但是根据你的调用约定,printf
它会认为它只需要从堆栈中清理 4 个字节(加上它的非可变格式字符串参数),而它的调用者会期望它清理 16 个字节,程序就会崩溃。
因此,除非您的调用约定将包含一个“隐藏”参数,该参数告诉被调用函数要清理多少字节的参数,否则它将无法工作。传递这样一个额外的参数将需要更多的指令,而不是让调用者自己进行堆栈清理。
推荐阅读
- css - 绝对定位的图像无法在手机/iPhone 上正确显示
- database - 如何在 sqflite 的数据库中创建多个表?
- mysql - 如果上一个查询成功则运行查询
- java - 用于检测 JDatePicker 文本字段中的移动/单击的合适侦听器是什么?
- python - Python 创建街道地址表
- javascript - 我的 javascript 不能在登录页面上运行,只能刷新?
- floating-point - IEEE754 FP32/64 标准错误
- java - Spring Boot Rest API - Querydsl,谓词不能为空
- java - 我的代码通过了所有给定的 CodingBat 测试,但没有通过“其他测试”。我不确定我的问题是什么
- google-cloud-platform - 无需要求即可连接到 Cloud Dataflow 中的 mysql 源