I have written a hooking library, that examines a PE executables dll import table, to create a library that enables changing of parameters and return values. I have a few questions on how the return value is passed from a function.
I have learned that the return value of a function is saved in the accumulator register. Is this always the case? If not, how does the compiler know where to look for the function result?
What about the return type size? An integer will easily fit, but what about a bigger structure? Does the caller reserve stack space so the method it calls could write the result onto stack?
eax (or rax) is the return value register. edi (or rdi) is the first function argument. esi (or rsi) is the second function argument.
Registers EAX, ECX, and EDX are designated for use within the function. Return values are stored in the EAX register. stdcall is the standard calling convention for the Microsoft Win32 API and for Open Watcom C++.
EAX register, a 32-bit processor register of x86 CPUs. Environmental Audio Extensions, a number of digital signal processing presets for audio, found in Sound Blaster sound cards.
I know when add integers in assembly, it stores result in the destination. and when we multiply it stores in eax. so if I call the function multiply( 3 , 8 ), the value of eax register after that line should be 120.
It's all specific to calling convention.
For most calling conventions floating point numbers are returned either on FPU-stack or in XMM registers.
Call to the function returning a structure
some_struct foo(int arg1, int arg2); some_struct s = foo(1, 2);
will be compiled into some equivalent of:
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
Edit: (add info)
just to clarify: this works for all structs and classes even when sizeof(some_struct) <= 4
. So if you define small useful class like ip4_type
with the only unsigned
field and some constructors/convertors to/trom unsigned
, in_addr
, char*
it will lack efficiency compared to use of raw unigned
value.
If the function get inlined, the result is not saved in eax, also if results are passed by reference/pointer, that register won't be used.
look at what happens to a function that return doubles (on a 32 bit machine)
double func(){ volatile double val=5.0; return val; } int main(){ double val = func(); return 0; }
doubles are not in 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With