calling a asm function with a c function?

Programming, for all ages and all languages.
Post Reply
User avatar
Sam111
Member
Member
Posts: 385
Joined: Mon Nov 03, 2008 6:06 pm

calling a asm function with a c function?

Post by Sam111 »

Ok , I know how to call an asm function from a c function and visa-versa.

But I only know the above if their is no parameters/arguments being passed.

I have been reading the wiki
http://en.wikipedia.org/wiki/X86_calling_conventions

However I am still haveing trouble.

Code: Select all

#include <stdio.h>

int main()
{

int i = func1( 3 ) ;
printf( "Hi this is " +  i ) ;

return 0 ;

}

Code: Select all


global _func1
_func1:
pop ax
; don't know what to do here to return the value into int i in main
ret
basically I could declare int i to be a global/external variable and just put the value in that varible before returning but then I would have to always use i to assign the resulting value to another variable.

I am just wondering how they do it int i = func1(3) ;
Basically I want func1 just to take in one argument and return that argument just as a test it is working.

I know the x86 convention is to push the arguments on backwards then call the function.
So I am assuming in the function I would just pop the values
like this func( a , b , c ,d ) ;

func:
pop ax ; gets a
pop bx ; gets b
pop cx ; gets c
pop dx ; gets d
;does what it wants to with the varibles
ret ; returns but how do you return a value?

Unless you assign the value to a varible that is external and global their is know way I can figure it out?
Curious when you are in c and create a function how they break it down in asm to make a value return.
ret doesn't allow you to pass an argument to a varible ...etc

Thanks for any help
User avatar
Sam111
Member
Member
Posts: 385
Joined: Mon Nov 03, 2008 6:06 pm

Re: calling a asm function with a c function?

Post by Sam111 »

Maybe this something like this

Code: Select all

#include <stdio.h>

int j = 0 ;

int main()
{

func1( 3 ) ;
int i = j ;
printf( "Hi this is " +  i ) ;

}

Code: Select all

extern j

global _func1
_func1
pop eax
mov j , eax
ret

But my whole point would be getting rid of the fact you need to use an intermediate external variable j.
Gigasoft
Member
Member
Posts: 855
Joined: Sat Nov 21, 2009 5:11 pm

Re: calling a asm function with a c function?

Post by Gigasoft »

No, this is wrong. When you enter a function, the first item that will be popped is the return address, then the parameters follow. In most functions, it is customary to access the parameters by moving SP into BP and accessing them at an offset from BP. For example, like this:

Code: Select all

push bp ; Save BP
mov bp,sp
mov ax,[bp+4] ; gets parameter 1, skipping old BP and return address
mov bx,[bp+6] ; gets parameter 2
mov cx,[bp+8] ; gets parameter 3
mov dx,[bp+10] ; gets parameter 4
pop bp ; Restore BP
ret 8 ; return and remove 8 bytes of parameters, assuming the function uses the __stdcall convention
This approach is used in nearly every case, especially when there are many parameters or you need to access them randomly.

The return value is in AL if it's a byte, in AX if it's a word, and with 16 bit programs, the high half is in DX and the low half in AX if it's a dword, whereas with 32 bit programs, a dword is returned in EAX. Structures are returned in different ways depending on the compiler.

For example, if a 16-bit function takes one parameter which is a word, and returns the value of the parameter plus 100, it would look like one of these:

Code: Select all

Add100_Variant1:
push bp ; Save BP
mov bp,sp
mov ax,[bp+4] ; Get parameter
add ax,100
pop bp ; Restore BP
ret 2 ; Return and remove the 2 bytes making up the parameter

Add100_Variant2:
pop cx ; Remove return address and put it in CX
pop ax ; Get parameter and remove it
add ax,100
jmp cx ; Jump to return address
Here is the same example where the function operates on a dword and returns a dword:

Code: Select all

Add100_Variant1_L:
push bp
mov bp,sp
mov ax,[bp+4] ; Get low part of parameter
mov dx,[bp+6] ; Get high part of parameter
add ax,100 ; Add 100 to low part
adc dx,0 ; Add 0 to high part with carry
pop bp
ret 4 ; Return and remove the 4 bytes making up the parameter

Add100_Variant2_L:
pop cx ; Remove return address and put it in CX
pop ax ; Get low part of parameter and remove it
pop dx ; Get high part of parameter and remove it
add ax,100
adc dx,0
jmp cx ; Jump to return address
A 32 bit function doing the same to a dword could look like this:

Code: Select all

mov eax,[esp+4] ; Here, we can use ESP and don't need to put it in EBP. A 16-bit function could also use ESP provided that the upper half of ESP was zero, but it could not use SP.
add eax,100
ret 4
And a 32 bit function adding 5 dword parameters together would look like this:

Code: Select all

push ebp
mov ebp,esp
mov eax,[ebp+8] ; Parameters now start at 8, since the saved EBP takes 4 bytes and the return address takes 4 bytes.
add eax,[ebp+12]
add eax,[ebp+16]
add eax,[ebp+20]
add eax,[ebp+24]
pop ebp
ret 20
It should be noted that instructions that use addresses relative to ESP are one byte longer than instructions using addresses relative to EBP, and that's why the last example uses EBP.

Note that the C statement printf( "Hi this is " + i ) ; would add i to the address of the string "Hi this is " and pass the result to printf. So, for example, if i was 4, it would print "his is ".
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

Re: calling a asm function with a c function?

Post by Owen »

First thing you need to know:

Code: Select all

CALL x
Is, effectively,

Code: Select all

PUSH nextAddress
JMP x
nextAddress
This means that the top of the stack is actually the return address. ret is effectively "POP eip"

For arguments, they are pushed right to left. That is, the C code

Code: Select all

func(1, 2, 3, 4);
is translated to the following

Code: Select all

push 4
push 3
push 2
push 1
call func
add esp, 16 # We will come to this later
To access arguments:

Code: Select all

mov eax, [esp+4]
mov ebx, [esp+8]
And so on. Arguments less than 4 bytes are padded out to 4 bytes when pushed. Arguments bigger are padded out to a multiple of 4 bytes.

Values are returned by putting them in EDX:EAX. This means, for 32-bit (or smaller) values, they are placed in the appropriate part of EAX (EAX, AX, AL). For 64-bit values, they are split into two and the higher part is placed in EDX, lower part in EAX. For floating point values they are returned in st0. For structures bigger than 64-bits, a hidden first argument is passed to the function with the address in which to place the result.

Finally, we have the responsibility of removing the arguments from the stack, which depends upon the calling convention:
For the C standard calling convention, the caller is responsible for cleaning the stack. Thats what the "add esp, 16" was for in the earlier example.
For the "stdcall", "pascal" or "fortran" convention, the callee is responsible for cleaning the stack. In that case your method would return by "ret 16". This produces smaller code, but doesn't allow variable argument count functions.

For long mode, everything is completely different, and you will want the appropriate ABI documentation handy at all times.
User avatar
Sam111
Member
Member
Posts: 385
Joined: Mon Nov 03, 2008 6:06 pm

Re: calling a asm function with a c function?

Post by Sam111 »

Ok , I am sort of getting it all.

ss - points to the begin address of the stack

But I am haveing trouble determine what the difference in sp , esp is from the bp , ebp pointers when it comes to the stack?

Does sp , esp always point to the last element pushed on the stack? Or is this bp , ebp job.
Because if you know that the return address is going to take 2bytes in a 16bit program and 4bytes in a 32bit
program then all you would ever have to do is just skip over them like this [esp + 4] , or [sp + 2] to get to the variables.
Why even use bp , or ebp to begin with?

Also in your examples I don't see after you call the functions how it is returning the value into a variable.
Like int i = func1(3); Does c compilers automatically assume the return value is in eax , or eax : edx

Code: Select all

_func1:
push ebp
mov ebp,esp
mov ax,[ebp+4]
pop ebp
ret 2
or

Code: Select all

_func1:
mov ax,[esp + 4]
ret 2
so I call this function in my c program like this func1(3) and it should basically put the value in ax which is 3. Then clears the stack. But when I return how do I get the value 3 which is in ax into the varible int i?

As for the printf problem yes I saw that after I posting.... I fixed that forgot the wildcard character ,...stuff
And yes that totally makes since why it would print "this is" if i =3

Also
ret 8 ; return and remove 8 bytes of parameters, assuming the function uses the __stdcall convention
does ret alone with no number just pop the return address of the stack then jump to it.
Is this why we don't factor into the ret 8 the extra return address bytes and we just tell it the size of the parameters on the stack in bytes to clear.


Is my code now ok above
Gigasoft
Member
Member
Posts: 855
Joined: Sat Nov 21, 2009 5:11 pm

Re: calling a asm function with a c function?

Post by Gigasoft »

SP or ESP points to the last item pushed. With 16 bit addressing, BP is used because there is no SP-relative addressing mode. One could use BX, SI or DI instead if that's more convenient. With 32 bit addressing, EBP is used because ESP-relative addressing modes are 1 byte longer. Any register can be used with 32 bit addressing. Using EAX, ECX or EDX could be shorter since these do not need to be saved.

Yes, C compilers always get the return value from AL, AX, DX:AX, EAX, or EDX:EAX.

Ret pops the return address that SP or ESP points to into IP or EIP, and then it adds the specified number of bytes to SP or ESP, so the size of the return address is not included here.

In a 32 bit program, each parameter always occupies a multiple of 4 bytes, so you should use ret 4 rather than ret 2 to remove a word parameter. Similarly, in a 16 bit program, each parameter is always a multiple of 2 bytes long, since stack operations can't be byte sized. In the example using EBP, you should use [ebp+8] to get at the first parameter, since [ebp] is the saved value of the old EBP and [ebp+4] is the return address.

When BP, EBP or ESP is used as the base register for an address, the address is relative to the SS segment by default. In all other cases, the default segment is DS. It is recommended to have SS and DS pointing to the same segment, otherwise you can't use stack addresses and other addresses interchangeably without factoring in the difference between the starts of the segments.
User avatar
Sam111
Member
Member
Posts: 385
Joined: Mon Nov 03, 2008 6:06 pm

Re: calling a asm function with a c function?

Post by Sam111 »

Ok , I think I got you

so it should be like this for 32 bit programs

Code: Select all

_func1:
push ebp ; pushes 4 bytes onto the stack
mov ebp,esp ; moves the 4 byte stack pointer into ebp
xor eax , eax ; zero out the register so the top half is zero if we are dealing with an integer (word 2bytes)
mov eax,[ebp+8] ; skip over ebp 4bytes on the stack plus skip over 4byte return address
pop ebp ; take 4bytes off the stack (i.e take ebp off the stack now all that is left is the return address and variable)
ret 4 ; return 4 pops the return address off the stack into eip and clears 4bytes off the stack so the varable is now off the stack and we are back to are function.

Code: Select all

_func1:
xor eax , eax ; zero out eax registar
mov eax,[esp + 4] ; skip over the return address and move the varible into eax
ret 4 ; return and clear off the variable from the stack. 

Let me know if I am missing anything or not interpreting you correctly.

also ret n
for example ret 4 , ret 2 ,...etc

could I just not use return and just use
pop eip
add esp , 4
or add esp , 2

Thanks
Gigasoft
Member
Member
Posts: 855
Joined: Sat Nov 21, 2009 5:11 pm

Re: calling a asm function with a c function?

Post by Gigasoft »

EIP is not a general register and can not be used as an operand in an instruction. And, when jumping to a different address, the instructions following the jump will obviously not be executed. The correct equivalent (except in that it overwrites a register) of, for example, ret 20 would be:

Code: Select all

pop ecx
add esp,20
jmp ecx
This is of course much longer, so just stick to the ret instruction.

mov eax, [esp+4] loads the entire dword into eax, so that's no good. If you only want to load a word, use mov ax, [esp+4]. However, there is a separate instruction that simultaneously loads a byte or word and clears the rest of the register, which is called movzx. It would therefore be:

Code: Select all

movzx eax,word ptr [esp+4]
ret 4
There is also another instruction, movsx, which does almost the same as movzx but instead copies the highest bit of the loaded value into the rest of the register. It is therefore useful for loading signed values.
User avatar
Sam111
Member
Member
Posts: 385
Joined: Mon Nov 03, 2008 6:06 pm

Re: calling a asm function with a c function?

Post by Sam111 »

mov eax, [esp+4] loads the entire dword into eax, so that's no good. If you only want to load a word, use mov ax, [esp+4]. However, there is a separate instruction that simultaneously loads a byte or word and clears the rest of the register, which is called movzx. It would therefore be:
I don't get you on why it would be no good to do mov eax , [esp+4]
even if the top half of the eax registar is garbage if you are only returning a word won't the return value in eax only use ax in the c code?

I get you on this I could use mov ax , [esp+4] but their is really no difference since the varibles have to be 4bytes pad anyway.

Also if their was junk in the top of eax then you could just zero it out by xor if that really bothered you .

Maybe I am not seeing your point

As well why do we need these new instructions are they any faster then just using the regular xor , mov ,...etc commands

i.e would this be more efficient or faster

Code: Select all

movzx eax,word ptr [esp+4]
then

Code: Select all

 mov ax ,[esp+4] 
Also their is one more thing that is bothering me
push ebx
or
push [ebx]

I know the [] means the actual value in the registar but what is push ebx doing?
Is it just pushing the address of where the ebx is pointing at. If so I would think you always want to push ebx not the value it contains at that address.... so when you are getting the varibles from the stack you must push ebx on then use ebx+8 ,ebx + 12 ,...etc to get your variables.

If you where to push [ebx] on the stack then this would put the base pointer at some random value when you pop it probably causing a stack over flow or something. Correct me if I am wrong.
Gigasoft
Member
Member
Posts: 855
Joined: Sat Nov 21, 2009 5:11 pm

Re: calling a asm function with a c function?

Post by Gigasoft »

If you don't care about the upper part of EAX, it's okay to just use mov eax, [esp+4]. I was just assuming that the xor eax, eax was meant to clear the upper part because of the comment you had written. Either way, the xor eax, eax has no effect since the entire EAX is overwritten by mov eax, [esp+4]. Therefore, if you want the upper part to remain the same as before, you must use mov ax, [esp+4]. The advantage of using movzx eax, word ptr [esp+4] rather than xor eax,eax followed by mov ax, [esp+4] is that it is shorter. I don't know if it is faster or not, but it might be (and it may vary between processor models).

EBX means the value in the register EBX, so push ebx pushes the value of the register EBX on the stack. [EBX] is the data item stored at the address specified in EBX, so push [ebx] would save the dword at the address contained in EBX, which is not what you want if your purpose is to save EBX. The only register changed by a push instruction is ESP, and it always decreases by 4 for a dword sized push, so the stack would be okay no matter what value is pushed. Let's say you want to use EBX, instead of EBP, to access your variables and parameters. Then, you'd save the old value of EBX with push ebx, and then you copy the current value of ESP into EBX with mov ebx, esp. EBX+8 would then be the address of the first variable, and the variable would be accessed with [ebx+8]. However, if you want to use ECX instead, you would not need to save and restore ECX, since EAX, ECX and EDX do not need to be preserved in a function call. So, adding 5 dwords together using ECX as the frame pointer, would be done like this:

Code: Select all

mov ecx,esp
mov eax,[ecx+4]
add eax,[ecx+8]
add eax,[ecx+12]
add eax,[ecx+16]
add eax,[ecx+20]
ret 20
User avatar
Sam111
Member
Member
Posts: 385
Joined: Mon Nov 03, 2008 6:06 pm

Re: calling a asm function with a c function?

Post by Sam111 »

Thanks I got you.

The only other question is what does gcc/ld default to when compiling/linking ..
Does it use stdcall or cdel ?
Because I think we are talking about how we would code it for a stdcall function.
Is their any benfit over the other faster ...else code/space ,...etc ?

And will their be away to switch gcc to compile in either way you want?

Their is also fastcall I noticed if this is even useful I think it is just using more registars to speed things up but other then this I don't think their is much benfit to using it.

Also printf( ... ) variable arguments how is that done in asm.
Like how can you pass variable amount of arguments?
Gigasoft
Member
Member
Posts: 855
Joined: Sat Nov 21, 2009 5:11 pm

Re: calling a asm function with a c function?

Post by Gigasoft »

GCC uses cdecl by default. I don't think you can change it, so you have to explicitly declare each function as stdcall. Stdcall is the best overall choice, since each function only requires 2 extra bytes per return instruction instead of having an add esp instruction at each site where it is called.

With functions that take a variable number of arguments, the function ends with a normal ret, and the caller removes the arguments after the call, just like in cdecl.
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

Re: calling a asm function with a c function?

Post by Owen »

Erm... Theres no such thing as a varargs stdcall function. The compiler should generate an error if you try to create one.

To define a function as stdcall, use __attribute__((stdcall)). Or, just use the C calling convention. The only difference (Unless you're calling functions) is that cdecl doesn't require a number after the ret ;-)
User avatar
Sam111
Member
Member
Posts: 385
Joined: Mon Nov 03, 2008 6:06 pm

Re: calling a asm function with a c function?

Post by Sam111 »

how about default calling conventions for cl , link if I am using Visual Studio's
Is it all cdecl by default on everything normally?

Like gcc is.

Thanks
Post Reply