进一步考虑,我认为这可以通过使用被调用者清理约定并手动管理我的所有返回地址来完成。它类似于 stdcall,但不完全相同,因为不能使用 call/ret(?)。
伪代码:
main:
; create stub, copy in 0x02 and &add, the resulting function pointer goes in add2
local add2 = _create_trampoline
; make the call
push [return address]
push 0x03 ;arg1
jmp add2
; the resulting stub, held on the heap somewhere
add2:
push 0x02 ;bound argument
jmp add
; var add(x,y)
add:
local x = pop
local y = pop
eax = x + y;
jmp pop
这样,add
知道堆栈的布局y x [ptr]
和执行正确返回。
失去 call/ret 似乎有点激烈,而且函数的堆栈框架add
非常脆弱,所以我将把这个问题至少再开放 24 小时,希望有更好的解决方案。
编辑:进一步考虑,您可以保留 cdecl、caller-cleanup、call/ret 和所有内容,只需在绑定的蹦床中携带返回地址(这只需要破坏一个寄存器,或将其移动到堆栈并返回)。
伪代码:
main:
; create stub, copy in 0x02 and &add, the resulting function pointer goes in add2
local add2 = _magic(0x02, &add);
; make the call
push 0x03;
call add2;
add2:
ebx = pop; ;the return address goes in a temporary
push 0x02;
push ebx;
jmp add
; var add(x,y)
add:
push ebp;
mov ebp, esp;
; local variables are [ebp+8] and [ebp+12]
perform calculation into eax
leave
ret
在那里,结果是一种非常简洁的技术,可以将绑定函数参数实现为堆上的可执行对象,同时保持 cdecl 调用约定。毫无疑问,这种方法在实施时会出现问题,但我希望它是可行的,而且效率不会太低。