flat assembler
Message board for the users of flat assembler.

Index > Windows > Questions: parameter passing and return values

Author
Thread Post new topic Reply to topic
yq8



Joined: 08 May 2015
Posts: 15
yq8 27 May 2015, 18:54
Hey,

I just have a few general questions.

1. Question :

Assuming I have a function which takes 2 integer parameters and returns an integer.
I now want to properly grab the two parameters and store the values of them in some registers so I can work with them, what is the correct way to do that in x86 fasm and in x64 fasm.

Assuming soem basic function like this:

Code:
int PerformAddition(int a, intb)
{
   // Some Code...
}
    


How would I grab the first value 'a' and store it in eax and how would I grab the second value, 'b' and store it in ecx?

For x64 fasm, the same question, how would I do that, and storing 'a' in rax and 'b' in rcx?
I think something like this would be right (for x86):

Code:
push ebp                     
mov ebp, esp
mov eax, [ebp+0x0c]
mov ecx, [ebp+0x8]
    


Is this good/bad or wrong/right?
How would the construct for that look like in x64 since there are some differences in the offsets, they have another size if I remember correctly?


2. Question :

I am a bit confused with returning of values.
Assuming the addition example from before would I do sth like this:


Code:
pop ebp
ret 0x8
    


Why the 0x8, why not an empty operand or 0x4 or whatever.
And how is this for x64, I mean it has to something with the order of the parameters on the stack I think, but I am not entirely sure.
Also when do I have to pop a value and when I don't have to?
In this case I think it is to remove the address from the stack.
Would be cool if someone could bring a bit more light into this and clearify it a little so I can understand it better Very Happy
Post 27 May 2015, 18:54
View user's profile Send private message Reply with quote
cod3b453



Joined: 25 Aug 2004
Posts: 618
cod3b453 27 May 2015, 20:24
Assuming the 32bit code is cdecl/stdcall with sizeof(void*)=sizeof(int)=4, the parameters will be on the stack at esp+4 and esp+8 (the return address is at esp) when entering the function. So with your push ebp these shift to esp+8/C like in your code. The return value, if a singular value, will be in eax, so:
Code:
PerformAddition:
push ebp ; Save ebp by pushing onto stack
mov ebp,esp
mov eax,dword [ebp+0x8] ; A is at ebp+8
add eax,dword [ebp+0xC] ; B is at ebp+C
pop ebp ; Restore ebp by popping off of stack
ret 8    
The pop ebp restores the original ebp value and balances the stack from the push ebp and ret 8 says increment esp by 8 to remove and balance the two parameters that were pushed the the calling code. [This tells us it's stdcall, since the function cleans up its own parameters rather than the code calling it; otherwise it would be just "ret").

It is possible to assemble using fastcall [or others] in both 32 and 64bit which use registers for the parameters but this usually depends on platform/compiler. The stack is still used to provide "shadow" space to save parameters the same as before but the function must save the register to this space by itself. In this case the stack is not needed at all but included example code you might see a debug build use:
Code:
PerformAddition:
; Equivalent code - note int is still 4 bytes but values occupy 8 bytes on the stack
; push rbp
; mov rbp,rsp
; mov dword [rbp+0x10],ecx
; mov dword [rbp+0x18],edx
; mov eax, dword [rbp+0x10]
; add eax,dword [rbp+0x10]
lea eax,[ecx+edx] ; Only really need this line. A is in rcx, B is in rdx
; pop rbp
ret ; Parameters on stack cleaned up by caller    
Hope that helps
Post 27 May 2015, 20:24
View user's profile Send private message Reply with quote
yq8



Joined: 08 May 2015
Posts: 15
yq8 28 May 2015, 00:12
So for x64 every offset is double the size of the x86 offset?
Post 28 May 2015, 00:12
View user's profile Send private message Reply with quote
El Tangas



Joined: 11 Oct 2003
Posts: 120
Location: Sunset Empire
El Tangas 28 May 2015, 10:04
yq8 wrote:
So for x64 every offset is double the size of the x86 offset?


Yes, 8 byte steps, and since there is always storage for at least 4 variables, the stack needs to be adjusted by (at least) 32 bytes on return. Problem is, I'm not sure who is supposed to do that, caller or callee.

Code:
;callee cleans up

...

ret 32

;caller does nothing
return_address:
    


Code:
;calle just returns

...

ret

;caller cleans up
return_address:
add     rsp,32
    


So you will have to try and trace with a debugger, see if the code crashes, etc. But check this link: https://msdn.microsoft.com/en-us/library/ms235286.aspx
Post 28 May 2015, 10:04
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20299
Location: In your JS exploiting you and your system
revolution 28 May 2015, 11:21
El Tangas wrote:
... and since there is always storage for at least 4 variables, the stack needs to be adjusted by (at least) 32 bytes on return. Problem is, I'm not sure who is supposed to do that, caller or callee.
For Windows 64bit fastcall it is well documented. Caller adjusts the stack. Callee just says "ret".
Post 28 May 2015, 11:21
View user's profile Send private message Visit poster's website Reply with quote
AsmGuru62



Joined: 28 Jan 2004
Posts: 1619
Location: Toronto, Canada
AsmGuru62 28 May 2015, 15:48
If few API calls must be made - code to reserve/adjust can be done just once:
Code:
sub rsp, 32
; perform parameters passing for an API call #1
; perform parameters passing for an API call #2
; perform parameters passing for an API call #3
add rsp, 32
    

Of course, provided there are no branches in the calls.
Post 28 May 2015, 15:48
View user's profile Send private message Send e-mail Reply with quote
cod3b453



Joined: 25 Aug 2004
Posts: 618
cod3b453 28 May 2015, 16:51
yq8 wrote:
So for x64 every offset is double the size of the x86 offset?
Normally, yes but there are exceptions:

- Passing parameters larger than 32bit on x86 would use more than 4 bytes e.g. f(int a, double b, int c) would use 4 bytes for a & c and 8 bytes for b
- You can pass structures by value rather than by reference which means a parameter can be any size [a similar mechanism is used when returning structures]
Post 28 May 2015, 16:51
View user's profile Send private message Reply with quote
eeikel



Joined: 17 Jun 2013
Posts: 7
Location: NL
eeikel 08 Apr 2016, 10:14
Assuming your using:
- eclipse cdt
- Fasm
- mingw with gcc on a windows machine
In Fasm create an object file:
Code:
format MS64 COFF 
public show
public PerformAddition

 hello db 'Hello World',0
           show:
                mov rax, hello;
                ret

 PerformAddition:
                push r10
                push r11
                mov r10,rcx
                mov r11,rdx
                mov rax,qword r10
                add rax,qword r11
                pop r10
                pop r11
                ret       
    

save as samplecode in fasm
open eclipse, create a new project TestASM
locate the samplecode.OBJ file and copy it to your workspace->TestASM folder

in eclipse: refresh project TestASM
open: Project-> c/c++ build -> Settings in Tools Settings Tab -> MinGW C Linker -> Miscellaneous add samplecode.OBJ you can use search workspace to lactae the file.

create an new c source file and name it: text.c
add following code:
Code:
#include "stdlib.h"
#include "stdio.h"
#include "string.h"

int Perform-addition(int a, int b); 
char * show();

int main(){
                 printf("-> %s \n",show());
                 
                 printf("============================ \n");
         
                 int number = PerformAddition(3, 9);
                 printf("3 + 9 = 12? %d",number);

return 0;
}
    


result:
Quote:

-> Hello World
============================
3 + 9 = 12? 12

https://en.wikipedia.org/wiki/X86_calling_conventions#x86-64_calling_conventions
The Microsoft x64 calling convention[11][12] is followed on Windows and pre-boot UEFI (for long mode on x86-64). It uses registers RCX, RDX, R8, R9 for the first four integer or pointer arguments (in that order), and XMM0, XMM1, XMM2, XMM3 are used for floating point arguments. Additional arguments are pushed onto the stack (right to left). Integer return values (similar to x86) are returned in RAX if 64 bits or less. Floating point return values are returned in XMM0. Parameters less than 64 bits long are not zero extended; the high bits are not zeroed.[/quote]
Post 08 Apr 2016, 10:14
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.