我编写了一个挂钩库,用于检查PE可执行文件dll导入表,以创建一个可以更改参数和返回值的库。关于如何从函数传递返回值,我有几个问题。
我了解到函数的返回值保存在累加器寄存器中。总是这样吗?如果没有,编译器如何知道在哪里查找函数结果?
返回类型大小怎么样?一个整数很容易适合,但更大的结构怎么样?调用者是否保留堆栈空间,以便它调用的方法可以将结果写入堆栈?
答案 0 :(得分:6)
这一切都与调用约定有关。
对于大多数调用约定,浮点数在FPU-stack或XMM寄存器中返回。
调用返回结构的函数
some_struct foo(int arg1, int arg2);
some_struct s = foo(1, 2);
将被编译成一些等价的:
some_struct* foo(some_struct* ret_val, int arg1, int arg2);
some_struct s; // constructor isn't called
foo(&s, 1, 2); // constructor will be called in foo
修改 :(添加信息)
只是为了澄清:即使在sizeof(some_struct) <= 4
时,这适用于所有结构和类。因此,如果您将ip4_type
这样的小型有用类定义为唯一unsigned
字段,将某些构造函数/转换器定义为/ unsigned
,in_addr
,char*
它将缺少与使用原始unigned
值相比效率。
答案 1 :(得分:2)
如果函数内联,结果不会保存在eax中,如果结果通过引用/指针传递,则不会使用该寄存器。
看一下返回双精度函数会发生什么(在32位机器上)
double func(){
volatile double val=5.0;
return val;
}
int main(){
double val = func();
return 0;
}
双打不在eax中。
func():
pushq %rbp
movq %rsp, %rbp
movabsq $4617315517961601024, %rax
movq %rax, -8(%rbp)
movq -8(%rbp), %rax
movq %rax, -24(%rbp)
movsd -24(%rbp), %xmm0
popq %rbp
ret
main:
pushq %rbp
movq %rsp, %rbp
subq $24, %rsp
call func()
movsd %xmm0, -24(%rbp)
movq -24(%rbp), %rax
movq %rax, -8(%rbp)
movl $0, %eax
leave
ret
答案 2 :(得分:1)
您正在询问有关ABI(应用程序二进制接口)的问题。这取决于操作系统。你应该查一查。您可以在http://en.wikipedia.org/wiki/X86_calling_conventions
找到一些好的信息和指向其他文档的链接要回答你的问题,是的,据我所知,所有流行的操作系统都使用A寄存器来返回结果。
答案 3 :(得分:1)
它实际上取决于所使用的调用约定,但通常EAX
用于32位和更小的整数数据类型,浮点值倾向于使用FPU或MMX寄存器,而64位整数类型倾向于请改用EAX
和EDX
的组合。然后是复杂的类/结构类型的问题,在这种情况下,编译器可能决定优化掉返回值并在调用堆栈上使用额外的输出参数来通过引用将返回的对象传递给调用者。