flat assembler
Message board for the users of flat assembler.

Index > Macroinstructions > The 64bit version of invoke (fastcall) makes extra code

Author
Thread Post new topic Reply to topic
madmatt



Joined: 07 Oct 2003
Posts: 1045
Location: Michigan, USA
madmatt
The 64bit version of invoke (fastcall) makes extra code that shouldn't be there
an example:

Code:
;[fasm code]

     invoke  GetSystemMetrics, SM_CXMAXIMIZED
     shr     rax, 1
     sub     rax, WINDOWWIDTH shr 1
     mov     [startx], rax

     invoke  GetSystemMetrics, SM_CYMAXIMIZED
     shr     rax, 1
     sub     rax, WINDOWHEIGHT shr 1
     mov     [starty], rax

     invoke  SetRect, addr clientrectangle, 0, 0, WINDOWWIDTH, WINDOWHEIGHT
     invoke  AdjustWindowRect, addr clientrectangle, WS_OVERLAPPEDWINDOW + WS_VISIBLE, FALSE

;[diassembly using x64 dumpbin]
  
  ;invoke  GetSystemMetrics, SM_CXMAXIMIZED
  0000000000402176: 48 C7 C1 3D 00 00  mov         rcx,3Dh
                    00
  000000000040217D: FF 15 B5 10 00 00  call        qword ptr [00403238h]
  
  ;shr     rax, 1
  ;sub     rax, WINDOWHEIGHT shr 1
  ;mov     [starty], rax
  0000000000402183: 48 83 C4 20        add         rsp,20h          ;<-- HERE
  0000000000402187: 48 D1 E8           shr         rax,1
  000000000040218A: 48 2D 90 01 00 00  sub         rax,190h
  0000000000402190: 48 89 45 F0        mov         qword ptr [rbp-10h],rax
  0000000000402194: 48 83 EC 20        sub         rsp,20h          ;<-- HERE
 
  ;invoke  GetSystemMetrics, SM_CYMAXIMIZED
  0000000000402198: 48 C7 C1 3E 00 00  mov         rcx,3Eh
                    00
  000000000040219F: FF 15 93 10 00 00  call        qword ptr [00403238h]

  ;shr     rax, 1
  ;sub     rax, WINDOWHEIGHT shr 1
  ;mov     [starty], rax
  00000000004021A5: 48 83 C4 20        add         rsp,20h          ;<-- HERE
  00000000004021A9: 48 D1 E8           shr         rax,1
  00000000004021AC: 48 2D 2C 01 00 00  sub         rax,12Ch
  00000000004021B2: 48 89 45 F8        mov         qword ptr [rbp-8],rax
  
  ;invoke  SetRect, addr clientrectangle, 0, 0, WINDOWWIDTH, WINDOWHEIGHT
  00000000004021B6: 48 83 EC 30        sub         rsp,30h          ;<-- HERE
  00000000004021BA: 48 8D 4D E0        lea         rcx,[rbp-20h]
  00000000004021BE: 48 C7 C2 00 00 00  mov         rdx,0
                    00
  00000000004021C5: 49 C7 C0 00 00 00  mov         r8,0
                    00
  00000000004021CC: 49 C7 C1 20 03 00  mov         r9,320h
                    00
  00000000004021D3: 48 C7 44 24 20 58  mov         qword ptr [rsp+20h],258h
                    02 00 00
  00000000004021DC: FF 15 7E 10 00 00  call        qword ptr [00403260h]
  
  invoke  AdjustWindowRect, addr clientrectangle, WS_OVERLAPPEDWINDOW + WS_VISIBLE, FALSE
  00000000004021E2: 48 83 C4 30        add         rsp,30h          ;<-- HERE
  00000000004021E6: 48 83 EC 20        sub         rsp,20h          ;<-- HERE
  00000000004021EA: 48 8D 4D E0        lea         rcx,[rbp-20h]
  00000000004021EE: 48 C7 C2 00 00 CF  mov         rdx,10CF0000h
                    10
  00000000004021F5: 49 C7 C0 00 00 00  mov         r8,0
                    00
  00000000004021FC: FF 15 0E 10 00 00  call        qword ptr [00403210h]    

_________________
Gimme a sledge hammer! I'LL FIX IT!
Post 17 Jun 2014, 17:13
View user's profile Send private message Reply with quote
tthsqe



Joined: 20 May 2009
Posts: 730
tthsqe
As you sure? the standard is that there is stack support for the arguments that are passed in registers...
Post 17 Jun 2014, 17:38
View user's profile Send private message Reply with quote
AsmGuru62



Joined: 28 Jan 2004
Posts: 1413
Location: Toronto, Canada
AsmGuru62
Maybe reserve these 20h bytes once in a procedure instead of doing it on each call.
Post 17 Jun 2014, 19:58
View user's profile Send private message Send e-mail Reply with quote
l_inc



Joined: 23 Oct 2009
Posts: 881
l_inc
madmatt
Have a look at the documentation about how to avoid stack allocation on each API call.

_________________
Faith is a superposition of knowledge and fallacy
Post 17 Jun 2014, 21:23
View user's profile Send private message Reply with quote
madmatt



Joined: 07 Oct 2003
Posts: 1045
Location: Michigan, USA
madmatt
l_inc wrote:
madmatt
Have a look at the documentation about how to avoid stack allocation on each API call.

Thanks! that seems to have solved the problem. why would stack space be allocated for each invoke call in the first place, if there is no need for it?

_________________
Gimme a sledge hammer! I'LL FIX IT!
Post 17 Jun 2014, 23:27
View user's profile Send private message Reply with quote
l_inc



Joined: 23 Oct 2009
Posts: 881
l_inc
madmatt
I think, it's obvious from the description of the frame macro:
Quote:
They should be used to enclose a block, inside which the RSP register is not altered between the procedure calls

Just imagine what happens, if you try to save some register value between API calls onto the stack.

P.S. I assume here, that you have the basic background knowledge regarding the "shadow space".

_________________
Faith is a superposition of knowledge and fallacy
Post 18 Jun 2014, 00:35
View user's profile Send private message Reply with quote
madmatt



Joined: 07 Oct 2003
Posts: 1045
Location: Michigan, USA
madmatt
l_inc wrote:
madmatt
I think, it's obvious from the description of the frame macro:
Quote:
They should be used to enclose a block, inside which the RSP register is not altered between the procedure calls

Just imagine what happens, if you try to save some register value between API calls onto the stack.

P.S. I assume here, that you have the basic background knowledge regarding the "shadow space".


the macro shouldn't reserve stack space for every invoke call, just every proc / endp. The programmer should decide whether extra stack space is needed per invoke call. I shouldn't have to use the frame/endf in every proc/endp function to stop this.

_________________
Gimme a sledge hammer! I'LL FIX IT!
Post 18 Jun 2014, 01:21
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 17476
Location: In your JS exploiting you and your system
revolution
madmatt wrote:
the macro shouldn't reserve stack space for every invoke call, just every proc / endp. The programmer should decide whether extra stack space is needed per invoke call. I shouldn't have to use the frame/endf in every proc/endp function to stop this.
That is a dangerous way to make the macros. Because:
Code:
invoke function,...
push rax rcx
invoke function,...
pop rcx rax ;<--- what values are retrieved here if that stack allocation is static?    
You have to know the restrictions of what you are doing.
Post 18 Jun 2014, 01:29
View user's profile Send private message Visit poster's website Reply with quote
madmatt



Joined: 07 Oct 2003
Posts: 1045
Location: Michigan, USA
madmatt
revolution wrote:
madmatt wrote:
the macro shouldn't reserve stack space for every invoke call, just every proc / endp. The programmer should decide whether extra stack space is needed per invoke call. I shouldn't have to use the frame/endf in every proc/endp function to stop this.
That is a dangerous way to make the macros. Because:
Code:
invoke function,...
push rax rcx
invoke function,...
pop rcx rax ;<--- what values are retrieved here if that stack allocation is static?    
You have to know the restrictions of what you are doing.


The 64bit disassembly of my windows c code shows none of this kind of stack manipulation. 32bit cinvoke call required this, but not 64bit invoke / cinvoke calls.

_________________
Gimme a sledge hammer! I'LL FIX IT!
Post 18 Jun 2014, 13:06
View user's profile Send private message Reply with quote
l_inc



Joined: 23 Oct 2009
Posts: 881
l_inc
madmatt
Quote:
The 64bit disassembly of my windows c code shows none of this kind of stack manipulation

That's because a C compiler tracks stack content. This is possible because the instructions are generated solely by the compiler and the compiler can decide on it's own, if it puts something onto the stack in-between.

fasm does not track, what you put onto the stack, and therefore cannot decide if "shadow space" is required. By using the frame macro you promise fasm to not do anything with the stack, and this way no stack allocation per call is required for code correctness.

Quote:
32bit cinvoke call required this

In fact, stack adjustment is not really necessary for the 32 bit cinvoke as well. In particular when the classical stack frame is in use:
Code:
push ebp
mov ebp,esp
... ;unbalanced stack is not a problem here
leave
ret    

the unbalanced stack is automatically fixed during procedure return, and this way you could leave it as is by omitting add esp,X after each ccall-function invocation.

But again register value saving within such a frame would break code correctness. Ensuring it requires fasm to adjust the stack frame each time.

_________________
Faith is a superposition of knowledge and fallacy
Post 18 Jun 2014, 13:31
View user's profile Send private message Reply with quote
madmatt



Joined: 07 Oct 2003
Posts: 1045
Location: Michigan, USA
madmatt
l_inc wrote:
madmatt
Quote:
The 64bit disassembly of my windows c code shows none of this kind of stack manipulation

That's because a C compiler tracks stack content. This is possible because the instructions are generated solely by the compiler and the compiler can decide on it's own, if it puts something onto the stack in-between.

fasm does not track, what you put onto the stack, and therefore cannot decide if "shadow space" is required. By using the frame macro you promise fasm to not do anything with the stack, and this way no stack allocation per call is required for code correctness.

Quote:
32bit cinvoke call required this

In fact, stack adjustment is not really necessary for the 32 bit cinvoke as well. In particular when the classical stack frame is in use:
Code:
push ebp
mov ebp,esp
... ;unbalanced stack is not a problem here
leave
ret    

the unbalanced stack is automatically fixed during procedure return, and this way you could leave it as is by omitting add esp,X after each ccall-function invocation.

But again register value saving within such a frame would break code correctness. Ensuring it requires fasm to adjust the stack frame each time.


But if code works fine without this code, why add the code in the first place? Why should I have to use frame/endf every proc/endp just to turn this 'feature' off? All I'm asking for is a simple fix that will remove this code without the use of frame/endf. So Tomasz, is there a simple fix for this, without the use of frame/endf?

_________________
Gimme a sledge hammer! I'LL FIX IT!
Post 19 Jun 2014, 11:05
View user's profile Send private message Reply with quote
l_inc



Joined: 23 Oct 2009
Posts: 881
l_inc
madmatt
Oh, come on. The code does not always work fine. I gave an example before. revolution even provided code for that example.

The question is just about what guarantees you'd prefer to have: guarantees of small code or guarantees of correct code. The latter choice is nearly always the defaults. That is why you need additional coding overhead for having smaller code.

_________________
Faith is a superposition of knowledge and fallacy
Post 19 Jun 2014, 11:32
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 17476
Location: In your JS exploiting you and your system
revolution
madmatt wrote:
All I'm asking for is a simple fix that will remove this code without the use of frame/endf.
You can override the proc and endp macros like this:
Code:
include 'win64ax.inc'

macro proc [args] {
        common
        proc args
        frame
}

macro endp {
        endf
        endp
}

section '.text' code readable executable
entry start
entry equ nul

proc start
        fastcall        function,0,1,2,3,4,5
        fastcall        function,0,1,2,3,4,5
        fastcall        function,0,1,2,3,4,5
        invoke  MessageBox,0,'hi','ho',0
        invoke  ExitProcess,0
endp

proc function a1,a2,a3,a4,a5,a6
        ret
endp

.end 0    
Post 19 Jun 2014, 12:49
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 7756
Location: Kraków, Poland
Tomasz Grysztar
madmatt wrote:
But if code works fine without this code, why add the code in the first place? Why should I have to use frame/endf every proc/endp just to turn this 'feature' off? All I'm asking for is a simple fix that will remove this code without the use of frame/endf. So Tomasz, is there a simple fix for this, without the use of frame/endf?
I created the static_rsp_prologue/static_rsp_epilogue/static_rsp_close variant of "proc" as an example of how to deal with this kind of problem. But keep in mind that it is a trade-off, as the encodings of some instructions will get larger. If what you are looking for is still something different, you can make your own set of prologue/epilogue/close macros, just like revolution pointed out.

See also the original thread when I introduced the "static RSP" macros, it has the example disassembly.
Post 19 Jun 2014, 15:45
View user's profile Send private message Visit poster's website Reply with quote
madmatt



Joined: 07 Oct 2003
Posts: 1045
Location: Michigan, USA
madmatt
Tomasz Grysztar wrote:
madmatt wrote:
But if code works fine without this code, why add the code in the first place? Why should I have to use frame/endf every proc/endp just to turn this 'feature' off? All I'm asking for is a simple fix that will remove this code without the use of frame/endf. So Tomasz, is there a simple fix for this, without the use of frame/endf?
I created the static_rsp_prologue/static_rsp_epilogue/static_rsp_close variant of "proc" as an example of how to deal with this kind of problem. But keep in mind that it is a trade-off, as the encodings of some instructions will get larger. If what you are looking for is still something different, you can make your own set of prologue/epilogue/close macros, just like revolution pointed out.

See also the original thread when I introduced the "static RSP" macros, it has the example disassembly.


Thank you, works how I want it to now. By the way, you're right, some instructions are larger, but some instructions are smaller too.

_________________
Gimme a sledge hammer! I'LL FIX IT!
Post 21 Jun 2014, 00:46
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-2020, Tomasz Grysztar. Also on YouTube, Twitter.

Website powered by rwasa.