flat assembler
Message board for the users of flat assembler.
Index
> Main > varargs with stdcall Goto page 1, 2 Next |
Author |
|
LocoDelAssembly 17 Jan 2007, 15:45
Why not just
Code: printf: ; Usual prolog ; Code ; Partial epiloge (just non-volatile registers restoring) mov ecx, [ebp+4] ; Return address mov edx, [ebp+varArgsSize] ; I think that a stdcall version of print should specify the size instead of guessing it with fmt ; Another possible code could be ;if defined varArgsSize ; mov edx, [ebp+varArgsSize] ;else ; ; We trust that the programmer has set EDX before entering in the epilog ;end if leave lea esp, [esp+edx+..fixedArgsNumber*4] mov [esp], ecx ret ; I hope it will be predicted correctly even with all the mess above But note that I think it is not a very good idea since it can be done by using regular stdcall and passing a pointer to the varArgsArray and both of our solutions are suboptimal than simply using "add esp, argsPassed*4" on return. So a better addition to fasm could be able to pass arrays in the same way you can pass strings (stdcall something, "Hello world"), but an improvement is required to allow defining all this constants on a global memory space instead of the current implementation which defines the data on code and it is skipped and pushed to stack by a CALL. [edit] Fixed problem of modification of EFLAGS and reduced the amount of instructions at the same time (so it's better now ) [/edit] [edit2] Even better Code: printf: ; Usual prologue ; Code ; ECX is set with varArgsSize ; Partial epilogue (just non-volatile registers restoring) mov esp, ebp mov ebp, [ebp+4] ; Return Address mov [esp+ecx+4+..fixedArgsNumber*4], ebp pop ebp lea esp, [esp+ecx+..fixedArgsNumber*4] retn since it doesn't affect EDX which sometimes is used to return 64-bit values. ECX is OK because it is pure volatile since it is never used as return value (I mean according to standards)[/edit2] Last edited by LocoDelAssembly on 18 Jan 2007, 03:12; edited 2 times in total |
|||
17 Jan 2007, 15:45 |
|
vid 17 Jan 2007, 16:22
because ALL registers have to be preserved, including ECX and EDX.
I am planning to implement formatted output (printf-like) to FASMLIB, and i would like to keep current calling convention. Few extra cycles per such call is not a problem, having to count arguments every time you call it, and writing "lea esp,[esp+XXX]" is bigger problem. Also many people would probably use "add esp," and destroy CF holding error status |
|||
17 Jan 2007, 16:22 |
|
MazeGen 17 Jan 2007, 16:48
First, can you use unallocated stack entries in the fasmlib? I mean something like
Code: push 1 add esp, 4 mov eax, [esp-4] ; eax always contains the value of 1 |
|||
17 Jan 2007, 16:48 |
|
vid 17 Jan 2007, 16:53
as long as "unallocated stack entries" doesn't get overwritten by interrupt, yes.
EDIT: and unless MS says somewhere we can do this on win32, and linus says we can do this on linux, and all future supported-OS authors say i can do it on their OSes, i won't do it so no go.... |
|||
17 Jan 2007, 16:53 |
|
LocoDelAssembly 17 Jan 2007, 18:22
Quote:
Then you are not using stdcall. If you want to keep stdcall again I think it's better to pass a pointer to array instead. remember this things Code: fmt db "%d %d", 0 ccall printf fmt, 3, 4, 6, 8, 9 ; No problem since ccall will add to ESP the correct value (which can be done with LEA if EFLAGS needs to be preserved) vidcall printf fmt, 3, 4, 6, 8, 9 ; FATAL, since printf will wrongly think that there is just 2 varArgs only. Note that the last fictitious call nor the ccall are incorrect since I can later change fmt to show those args but now are invisible due to "user settings". shoudn't FASMLIB procedures be called throught libcall? Then better modify the macros for procedure declaration a bit to allow varArgs and then libcall will do a "lea esp, [esp+varArgsSize]" when it detects you are calling a procedure with varArgs (or just modify libcall and when you pass more args than the amount of formal parameters then on return it will do a "lea esp, [esp+extraArguments*4]"). Not all this is more easy to implement but it is safer also. If you want to continue in your way anyway then sorry, I have no ideas of how to do it better |
|||
17 Jan 2007, 18:22 |
|
vid 17 Jan 2007, 18:34
Quote: Then you are not using stdcall. allright. varargs isn't stdcall too. i mean, arguments pushed right to left, procedure cleans arguments from stack. [qutoe]If you want to keep stdcall again I think it's better to pass a pointer to array instead. [/quote]Is it better to do from pure assembly? It's this code: Code: push 2 push 1 push _format call printf Code: push 2 push 1 mov eax, esp push eax push _format call printf lea esp, [esp+8] ;remove structure from stack without destroying CF Second is much uglier, harder to comprehend for newbies, harder to use, easier to make mistake (manual removing of structure from stack), etc. etc. Quote: shoudn't FASMLIB procedures be called throught libcall Quote: vidcall printf fmt, 3, 4, 6, 8, 9 Quote: If you want to continue in your way anyway then sorry, I have no ideas of how to do it better |
|||
17 Jan 2007, 18:34 |
|
LocoDelAssembly 17 Jan 2007, 19:38
Quote:
In fact it is againts: Code: call endOfArray dd 1 dd 2 endOfArray: push _format call printf I mean using an array placed in compile-time or run-rime allocated memory. But the code above uses the same trick that Tomasz does to push strings but for me the better way is the same that Fresh does/did which consists in defining all constant data in one place. [edit] Though, my code does not accept arguments like "[ebx+something]" nor "addr ebx+something"... [/edit] |
|||
17 Jan 2007, 19:38 |
|
vid 17 Jan 2007, 19:49
consider usage in pure assembly. in FASMLIB, it would be in simplest case:
Code: idata{ array: dd 1 dd 2 } push array push _format call print but this is pretty inefficient... that data should be declared on stack, not statically. Still, in pure assembly usage (the one which matters in assembly library) is ugly and my "vararg stdcall" is worth of it. Purpose of library is to make things easier for caller, and my idea is imho most easy usage without any major drawbacks. It's just unstandard, but that isn't problem for me, as long as it's effective |
|||
17 Jan 2007, 19:49 |
|
LocoDelAssembly 17 Jan 2007, 21:06
Code: printf: ; Usual prologue * ; Code ; EBP is set with varArgsSize ; Partial epilogue (just non-volatile registers restoring) push dword [esp+..localVarsSize] ; oldEBP pop dword [esp+ebp+..localVarsSize+..fixedArgsSize+4] push dword [esp+..localVarsSize+4] ; returnAddress pop dword [esp+ebp+..localVarsSize+4+..fixedArgsSize+4] lea esp, [esp+ebp+..localVarsSize+..fixedArgsSize] pop ebp ret ; I hope it will be predicted correctly even with all the mess above ; * push ebp ; * mov ebp, esp ; * add esp, ..localVarsSize ; * push non-volatile registers What about implementing like above? If I remember right PUSH/POP pairs are optimized to behalf like "mov [mem], [mem]" instead of two reads and two writes to memory, but if not well, some testings will be needed to check if using MOV EAX, [...] (after preservation of EAX) is faster. I think that the way above is easily macrosable. I'm not apporting to much to your first code, but this one is more compatible with the prolog generated by PROC32.INC If for some reason EBP must hold the number of varArgs instead of the size then replace all the occurrencies of "+ebp+" with "+ebp*4+". Decide this carefully, it seems to be more confortable for the programmer to set EBP to the number of args but since inside the code you probably did "mov eax, [ebp+lastFormalArg+edi]"/"add edi, 4" (fragment of code that iterates throught the varArgs), then before the epilog a simple "mov ebp, edi" will be enough instead of "shr edi, 2"/"mov ebp, edi". Most of the time the used pointers will help a lot to calculate the size but if EBP must be set with the number of args then you will need to store a counter somewhere or do a little more calculations. Cheers Last edited by LocoDelAssembly on 18 Jan 2007, 00:48; edited 1 time in total |
|||
17 Jan 2007, 21:06 |
|
vid 17 Jan 2007, 23:05
looks like nice idea. From my point it doesn't matter if size is in EBX or EBP, and it suits better to classical epiloque. nice idea.
|
|||
17 Jan 2007, 23:05 |
|
LocoDelAssembly 18 Jan 2007, 00:46
I see a stupid coding, LEAVE is incorrect and must be changed to a simple POP EBP. I'll edit it now.
|
|||
18 Jan 2007, 00:46 |
|
vid 18 Jan 2007, 01:07
no idea, i never ever used "leave"
|
|||
18 Jan 2007, 01:07 |
|
LocoDelAssembly 18 Jan 2007, 02:39
Never???? Well ret of PROC32.inc use it all the time and it is the functional equivalent of mov esp, ebp/pop ebp but in a single CPU instruction.
The problem of course is that in this case ESP already has the appropiate value to do POP EBP but thanks to LEAVE I'm destroying the good work done by LEA and setting ESP with the argsSize which is obviously fatal. BTW, my epilogue introduces a new requirement not found in current PROC32.INC, the need to have ESP at the end of local variables, if the user by some reson decided to save registers in localVariables then he can set ESP as much bytes as he wants away of the localVariables since LEAVE supports it perfectly but not the new epilogue. (Yes, if the programmer has used "uses ebx esi edi" then ESP can't be with arbitrary values but since he could not use "uses" but instead save the registers by himself then on a stdcall with varArgs he must have special care - though, this situation is so, so, so improbable...-) |
|||
18 Jan 2007, 02:39 |
|
f0dder 18 Jan 2007, 03:40
bad bad fucking bad idea - SMC is bad for performance, and what happens if a wrong format string is used? *boom*.
|
|||
18 Jan 2007, 03:40 |
|
vid 18 Jan 2007, 08:32
f0dder: no SMC anywhere. It was one of my conditions in first post - no SMC. I can't afford it in portable code.
|
|||
18 Jan 2007, 08:32 |
|
Octavio 18 Jan 2007, 15:25
Is there some reason for not using the C stdcall here?
if parameters are incorrect how will the called procedure return? |
|||
18 Jan 2007, 15:25 |
|
LocoDelAssembly 18 Jan 2007, 15:36
Returning is always possible, the problem is that the stack will be unbalanced, and if stack is unbalanced then the caller will not be able to return nor restore the non-volatile registers (unless he used local variables to save them), but the callee can mess the stack and still be able to return.
|
|||
18 Jan 2007, 15:36 |
|
vid 18 Jan 2007, 16:25
hehe, everyone is taking the wrong number of arguments into account just because ccall allowed it. It's same as when you call classical stdcall proc with bad number of arguments - you just fucked it up, it's BUG!
Quote: Is there some reason for not using the C stdcall here? |
|||
18 Jan 2007, 16:25 |
|
Octavio 18 Jan 2007, 23:55
>don't need to count arguments sized and write "lea esp, [esp+XYZ]" every
have you seen the ccall macro in fasm sources? or perhaps is to reduce code size? >time yourself. And I CAN do it, so question is: Is there some reason NOT to >do it? of course no, that's assembly what about this: Code: mov ebp,esp push params call f1 ......... f1: pusha ........ popa pop [ebp-4] lea esp,[ebp-4] ret |
|||
18 Jan 2007, 23:55 |
|
Goto page 1, 2 Next < Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.