flat assembler
Message board for the users of flat assembler.

flat assembler > Macroinstructions > [fasmg] A simple macro for Win64 fastcall

Author
Thread Post new topic Reply to topic
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6876
Location: Kraków, Poland
I decided to give it a try and make a fasmg version of fasm's "fastcall" macro. The original one is quite convoluted, so instead of converting it I wrote a new one from scratch (but trying to preserve the behavior). This is what I got:
Code:
macro fastcall? proc*,args& local offset,framesize,type,value if framesize sub rsp,framesize end if offset = 0 match any, args iterate arg, args match =float? val, arg type = 'f' redefine value val SSE.parse_operand @src,val else match =addr? val, arg type = 'a' redefine value val x86.parse_operand @src,[val] else type = 0 redefine value arg SSE.parse_operand @src,arg end match if % < 5 if type = 'f' repeat 1, i:%-1 if @src.size = 8 | ~ @src.size | @src.type = 'mmreg' if @src.type = 'imm' mov rax,value movq xmm#i,rax else movq xmm#i,value end if else if @src.size = 4 if @src.type = 'imm' mov eax,value movd xmm#i,eax else movd xmm#i,value end if else err 'invalid argument ',`arg end if end repeat else iterate <rX,rXd,rXw,rXb>, rcx,ecx,cx,cl, rdx,edx,dx,dl, r8,r8d,r8w,r8b, r9,r9d,r9w,r9b indx 1+offset/8 if type = 'a' if @src.size = 8 | ~ @src.size lea rX,[value] else err 'invalid argument ',`arg end if else if @src.size = 8 | ~ @src.size mov rX,value else if @src.size = 4 mov rXd,value else if @src.size = 2 mov rXw,value else if @src.size = 1 mov rXb,value else err 'invalid argument ',`arg end if end if break end iterate end if else if @src.type = 'reg' mov [rsp+offset],value else if @src.type = 'mem' if type = 'a' if @src.size = 8 | ~ @src.size lea rax,[value] mov [rsp+offset],rax else err 'invalid argument ',`arg end if else if @src.size = 8 | ~ @src.size mov rax,value mov [rsp+offset],rax else if @src.size = 4 mov eax,value mov [rsp+offset],eax else if @src.size = 2 mov ax,value mov [rsp+offset],ax else if @src.size = 1 mov al,value mov [rsp+offset],al else err 'invalid argument ',`arg end if end if else if @src.type = 'imm' if @src.size = 8 | ~ @src.size mov qword [rsp+offset],value else if @src.size = 4 mov dword [rsp+offset],value else if @src.size = 2 mov word [rsp+offset],value else if @src.size = 1 mov byte [rsp+offset],value else err 'invalid argument ',`arg end if else if type = 'f' & @src.type = 'mmreg' & @src.size = 16 movq [rsp+offset],value else err 'invalid argument ',`arg end if end if offset = offset + 8 end iterate end match framesize = offset + offset and 8 call proc if framesize add rsp,framesize end if end macro macro invoke? proc*,args& fastcall [proc],args end macro macro cinvoke? proc*,args& fastcall [proc],args end macro
I have not tested it much, so it may still have some bugs. It does not implement the "frame"/"endf" the original had, but it should be an easy addition.

Note that macro uses the "parse_operand" from my x86 instruction set package to detect types of arguments, it therefore has to be used with these macro. If you have x64 instruction set implemented with some other macros, you may need to change this macro accordingly.

Also the macro could become a bit faster if repeated parsing of the operands was avoided by calling the "x86.store_instruction" directly for at least some of the generated instructions. But this would make it a bit more complex and less clear.
Post 04 Apr 2017, 19:12
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6876
Location: Kraków, Poland
Another variant which is even cleaner thanks to the use of enumerated variables for fastcall register names:
Code:
define fastcall? fastcall.r1 equ rcx fastcall.rd1 equ ecx fastcall.rw1 equ cx fastcall.rb1 equ cl fastcall.rf1 equ xmm0 fastcall.r2 equ rdx fastcall.rd2 equ edx fastcall.rw2 equ dx fastcall.rb2 equ dl fastcall.rf2 equ xmm1 fastcall.r3 equ r8 fastcall.rd3 equ r8d fastcall.rw3 equ r8w fastcall.rb3 equ r8b fastcall.rf3 equ xmm2 fastcall.r4 equ r9 fastcall.rd4 equ r9d fastcall.rw4 equ r9w fastcall.rb4 equ r9b fastcall.rf4 equ xmm3 macro fastcall? proc*,args& local offset,framesize,type,value if framesize sub rsp,framesize end if offset = 0 match any, args iterate arg, args match =float? val, arg type = 'f' redefine value val SSE.parse_operand @src,val else match =addr? val, arg type = 'a' redefine value val x86.parse_operand @src,[val] else type = 0 redefine value arg SSE.parse_operand @src,arg end match if % < 5 if type = 'f' if @src.size = 8 | ~ @src.size | @src.type = 'mmreg' if @src.type = 'imm' mov rax,value movq fastcall.rf#%,rax else movq fastcall.rf#%,value end if else if @src.size = 4 if @src.type = 'imm' mov eax,value movd fastcall.rf#%,eax else movd fastcall.rf#%,value end if else err 'invalid argument ',`arg end if else if type = 'a' lea fastcall.r#%,[value] else if @src.size = 8 | ~ @src.size redefine target fastcall.r#% if @src.type <> 'reg' | ~ @src.imm eq fastcall.r#% mov fastcall.r#%,value end if else if @src.size = 4 redefine target fastcall.rd#% if @src.type <> 'reg' | ~ @src.imm eq fastcall.rd#% mov fastcall.rd#%,value end if else if @src.size = 2 redefine target fastcall.r#% if @src.type <> 'reg' | ~ @src.imm eq fastcall.rw#% mov fastcall.rw#%,value end if else if @src.size = 1 redefine target fastcall.rb#% if @src.type <> 'reg' | ~ @src.imm eq fastcall.rb#% end if else err 'invalid argument ',`arg end if end if end if else if @src.type = 'reg' mov [rsp+offset],value else if @src.type = 'mem' if type = 'a' lea rax,[value] mov [rsp+offset],rax else if @src.size = 8 | ~ @src.size mov rax,value mov [rsp+offset],rax else if @src.size = 4 mov eax,value mov [rsp+offset],eax else if @src.size = 2 mov ax,value mov [rsp+offset],ax else if @src.size = 1 mov al,value mov [rsp+offset],al else err 'invalid argument ',`arg end if end if else if @src.type = 'imm' if @src.size = 8 | ~ @src.size mov qword [rsp+offset],value else if @src.size = 4 mov dword [rsp+offset],value else if @src.size = 2 mov word [rsp+offset],value else if @src.size = 1 mov byte [rsp+offset],value else err 'invalid argument ',`arg end if else if type = 'f' & @src.type = 'mmreg' & @src.size = 16 movq [rsp+offset],value else err 'invalid argument ',`arg end if end if offset = offset + 8 end iterate end match framesize = offset + offset and 8 call proc if framesize add rsp,framesize end if end macro macro invoke? proc*,args& fastcall [proc],args end macro macro cinvoke? proc*,args& fastcall [proc],args end macro
Note that with the x86/x64 macros from fasmg package "=" would work just as well as "equ" for the creation of register aliases.
Post 06 Apr 2017, 14:04
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6876
Location: Kraków, Poland
The above ones still miss another feature of fasm's macro: the inline strings support. This can be added with a small insertion like this:
Code:
if @src.type = 'imm' & @src.size = 0 if value eqtype '' & ~ type = 'f' inline_string value type = 'a' end if end if
just before the "if % < 5" block. This requires an additional "inline_string" macro which may look like:
Code:
macro inline_string var local data,continue jmp continue data TCHAR var,0 redefine var data continue: end macro
The above variant inserts the string data in the middle of code just like fasm's original macro does, however having it as a separate macro allows to define alternatives, for example another implementation of "inline_string" could insert the string into data section, and perhaps even check for duplicates.
Post 06 Apr 2017, 14:13
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6876
Location: Kraków, Poland
This macro with added "frame"/"endf" feature is now part of the fasm-like Windows headers package for fasmg.
Post 12 Apr 2017, 18:38
View user's profile Send private message Visit poster's website Reply with quote
VEG



Joined: 06 Feb 2013
Posts: 77
Location: Minsk, Belarus
Tomasz Grysztar wrote:
for example another implementation of "inline_string" could insert the string into data section, and perhaps even check for duplicates.
Oh, nice idea! I think such technique will be very useful in different situations. Will be nice to have an example how to add some contents to a virtual container, and then insert all these contents in specific place of the binary. It could be useful not just for strings, but even for pieces of code, for example. Or you can add resources to the .rsrc section from different places (near the code which uses these resources) and in one special command all resources will be placed in the proper place.
Post 28 Apr 2017, 19:42
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6876
Location: Kraków, Poland
VEG wrote:
Oh, nice idea! I think such technique will be very useful in different situations. Will be nice to have an example how to add some contents to a virtual container, and then insert all these contents in specific place of the binary. It could be useful not just for strings, but even for pieces of code, for example. Or you can add resources to the .rsrc section from different places (near the code which uses these resources) and in one special command all resources will be placed in the proper place.
There are actually many ways to do such things and I do not know yet which one might be the most efficient. But I made something quickly for you as an example:
Code:
string.buffer = '' postpone string.data := string.buffer end postpone macro fastcall?.inline_string var local data virtual at fastcall?.strings db string.buffer data TCHAR var,0 load string.buffer:$-$$ from $$ end virtual redefine var data end macro ; Put this line in your data section: fastcall?.strings db string.data
(Note that I define "fastcall?.inline_string" name instead of "inline_string" because this is the naming convention that I used in the later version of the "fastcall" macro for the Windows header package for fasmg.)

This is a plain and simple variant, it does not try to reuse strings. It is, however, completely order-independent, you can put the data section first or last and it is going to work correctly in all cases.

As for the string reuse, there are also multiple variants that come to my mind. One could be to use Boyer-Moore-Hornspool search like in a similar set of macros I made for fasm 1. This would have an advantage that it would be able to find reusable substrings. The other option could be to use EVAL to define symbols with names derived from string (something like base64 encoding) in a special namespace and this way utilize fasmg's symbol lookup to quickly check if a given string has already been placed somewhere and at what address.
Post 28 Apr 2017, 20:18
View user's profile Send private message Visit poster's website Reply with quote
VEG



Joined: 06 Feb 2013
Posts: 77
Location: Minsk, Belarus
Tomasz Grysztar, oh, thank you for this example. I will definitely use it. It is a thing that I was almost dreaming about while using FASM1 Smile
Post 28 Apr 2017, 20:48
View user's profile Send private message Visit poster's website 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 © 2004-2018, Tomasz Grysztar.

Powered by rwasa.