Author Topic: Win64 Basic Hello World NASM MINGW64 GoLink (Example Code)  (Read 39866 times)

Offline encryptor256

  • Full Member
  • **
  • Posts: 250
  • Country: lv
  • Win64 .
    • On Youtube: encryptor256
Win64 Basic Hello World NASM MINGW64 GoLink (Example Code)
« on: October 22, 2013, 02:58:45 PM »
Hello!

If you just started with Win64,
then this example code might help you understand to more.
Main target, how to call function with more than four parameters, using macro or without.
Tested two linkers, Mingw64-GCC and GoLink, both worked fine.

.Formal_description:

; ---------------------------------------------------------------------------
; This is: Win64 Basic Hello World NASM MINGW64 GoLink
; Author: J.K. Encryptor256
; Date: October 22, 2013
; ---------------------------------------------------------------------------
; Using:
;
; 1. The Netwide Assembler, NASM (http://nasm.us/)
;        The Netwide Assembler, NASM: It is an 80x86 and x86-64 assembler
;        designed for portability and modularity.
;
; 2. Minimalist GNU for Windows, MinGW64 (http://mingw-w64.sourceforge.net/)
;        Mingw-w64: It delivers runtime, headers and libs for developing
;        both, 64 bit (x64) and  32 bit (x86) windows applications, using GCC and
;        other free software compilers.
;
;        GCC is a part of MINGW64 toolset.
;
; 3. GoLink, (http://www.godevtool.com/)
;
; ---------------------------------------------------------------------------
; Description:
;
; Once upon a time...
; I did not found and was struggling with - printf, how to pass more than
; four arguments, where to put them and manage them correctly.
; So, i collected info, learned from all the place and builded my own example.
;
; ---------------------------------------------------------------------------
; How to compile:
;
; Like this, in the way i did:
;
; 1. NASM: "nasm.exe helloworld.asm -f win64 -o helloworld.obj"
; Output obj size: 1016 bytes (1,016 bytes)
;
; Choose your linker:
;
; 2.1. GCC: "gcc.exe helloworld.obj -m64 -o helloworld.exe"
; Output exe size: 42.6 KB (43,697 bytes)
;
; 2.2. GoLink: "golink.exe /console /entry main helloworld.obj MSVCRT.dll"
; Output exe size: 2.00 KB (2,048 bytes)
;
; ---------------------------------------------------------------------------

.Example_code:

Code: [Select]
; ---------------------------------------------------------------------------
; Tell compiler to generate 64 bit code
; ---------------------------------------------------------------------------
bits 64

; ---------------------------------------------------------------------------
; Data segment:
; ---------------------------------------------------------------------------
section .data use64

        ; Multiline printf format
        txt_format: db 10,"==========================="
                        db 10,"Hello world:"
                        db 10,"==========================="
                        db 10,"param1: %u"
                        db 10,"param2: %I64u"
                        db 10,"param3: %f"
                        db 10,"param4: 0x%X"
                        db 10,"param5: %f"
                        db 10,"param6: 0x%I64X"
                        db 10,"===========================",10,0

        align 16 ; Align txt_format data to 16 byte boundary

        ; Parameters:
        param1: dq 0xFFFFFFFF
        param2: dq 0xFFFFFFFFFFFFFFFF
        param3: dq 29999.452
        param4: dq 0xDEADBEEF ; Some other value
        param5: dq 555595.5477
        param6: dq 0xABBABABAABBABABA

; ---------------------------------------------------------------------------
; Code segment:
; ---------------------------------------------------------------------------
section .text use64

        ; ---------------------------------------------------------------------------
        ; Define macro: Invoke
        ; ---------------------------------------------------------------------------
        %macro Invoke 1-*
                %if %0 > 1
                        %rotate 1
                        mov rcx,qword %1
                        %rotate 1
                        %if %0 > 2
                                mov rdx,qword %1
                                %rotate 1
                                %if  %0 > 3
                                        mov r8,qword %1
                                        %rotate 1
                                        %if  %0 > 4
                                                mov r9,qword %1
                                                %rotate 1
                                                %if  %0 > 5
                                                        %assign max %0-5
                                                        %assign i 32
                                                        %rep max
                                                                mov rax,qword %1
                                                                mov qword [rsp+i],rax
                                                                %assign i i+8
                                                                %rotate 1
                                                        %endrep
                                                %endif
                                        %endif
                                %endif
                        %endif
                %endif
                ; ------------------------
                ; call %1 ; would be the same as this:
                ; -----------------------------------------
                sub rsp,qword 8
                mov qword [rsp],%%returnAddress
                jmp %1
                %%returnAddress:
                ; -----------------------------------------
        %endmacro

        ; ---------------------------------------------------------------------------
        ; C management
        ; ---------------------------------------------------------------------------
        global main
        extern printf

main:
        ; -----------------------------------------------------------------------------
        ; Allocate stack memory
        ; -----------------------------------------------------------------------------
        sub rsp,8*7
        ; Allocated 8*7 bytes:
        ; 8*4 from them are defaut parameters for all functions.
        ; 8*3 from them are those extra three parameters on stack.
        ; Total allocated space for seven parameters.
        ; 4x Default parameters are passed via registers.
        ; Those 3x extra parameters are passed via stack.
        ; !!! Always allocate stack space => odd number * 8,
        ; like now is just like we need => 8*7, doing like this will
        ; balance stack and make it aligned on 16 byte boundary.
       
        ; -----------------------------------------------------------------------------
        ; Call printf with seven parameters
        ; 4x of them are assigned to registers.
        ; 3x of them are assigned to stack spaces.
        ; -----------------------------------------------------------------------------
        ; Call printf with seven parameters
        ; -----------------------------------------------------------------------------
        Invoke printf,txt_format,[param1],[param2],[param3],[param4],[param5],[param6]

        ; -----------------------------------------------------------------------------
        ; The same, BUT, without 'Invoke' macro would be like this:
        ; -----------------------------------------------------------------------------
        ; #3x# ; Stack parameters
        mov rax,qword [param6]
        mov qword [rsp+48],rax
        mov rax,qword [param5]
        mov qword [rsp+40],rax
        mov rax,qword [param4]
        mov qword [rsp+32],rax
        ; ---------------------------
        ; #4x# ; Register parameters
        mov r9,qword [param3]
        mov r8,qword [param2]
        mov rdx,qword [param1]
        mov rcx,qword txt_format
        ; ---------------------------
        call printf
        ; ---------------------------

        ; -----------------------------------------------------------------------------
        ; Release stack memory
        ; -----------------------------------------------------------------------------
        add rsp,8*7

        ; -----------------------------------------------------------------------------
        ; Quit
        ; -----------------------------------------------------------------------------
        mov rax,qword 0
        ret

; ----
; END ----
; ----

.Video:

You can watch source code and runtime demo at youtube: http://youtu.be/tWAYuOzlnvY

Video was recorded by "Win64 ScreenRecorder": http://forum.nasm.us/index.php?topic=1749.0

.Attachment:

Attachment named: "win64_basic_helloworld_printf_console_nasm_mingw64_golink.zip",
which includes source file and already compiled obj and exe file.


And that's it,
Encryptor256!
Encryptor256's Investigation \ Research Department.

Offline Bryant Keller

  • Forum Moderator
  • Full Member
  • *****
  • Posts: 360
  • Country: us
    • About Bryant Keller
Re: Win64 Basic Hello World NASM MINGW64 GoLink (Example Code)
« Reply #1 on: October 23, 2013, 02:09:13 AM »
I question the legitimacy of teaching newcomers to manually push the return address onto the stack then jump to an address rather than just using a call instruction. The call instruction is faster and it's smaller, there is no benefit to using the method done in the Invoke macro. If it was only for the readers edification, then it shouldn't have been encapsulated in a macro. There is a possibly a reader of this post might just copy the macro and throw it into an include for reuse (what macros are used for) which, in this case, would be a very bad idea.

About Bryant Keller
bkeller@about.me

Offline encryptor256

  • Full Member
  • **
  • Posts: 250
  • Country: lv
  • Win64 .
    • On Youtube: encryptor256
Re: Win64 Basic Hello World NASM MINGW64 GoLink (Example Code)
« Reply #2 on: October 23, 2013, 06:11:30 AM »
I question the legitimacy of teaching newcomers to manually push the return address onto the stack then jump to an address rather than just using a call instruction. The call instruction is faster and it's smaller, there is no benefit to using the method done in the Invoke macro. If it was only for the readers edification, then it shouldn't have been encapsulated in a macro. There is a possibly a reader of this post might just copy the macro and throw it into an include for reuse (what macros are used for) which, in this case, would be a very bad idea.

Quote
this example code might help you

Well Bryant, everything is possible.

The newcomer will not know, that call instruction is faster and smaller.

Newcomers have choice, to copy and reuse or to study the source code.

That's why i provided two examples there, one use macro and other not.

It's always about the difference, two examples, can see the difference!

Quote
legitimacy of teaching
, Yes, The Legitimacy of Teaching!

I made up a ryme about teaching now days:

Don't do this,
don't do that,
i don't like this,
i don't like that,
don't do that!

Never use goto statement in C or C++,
always do the things what other said to you,
like being honest and paying taxes.

What about, one, big, blue screen all over your desktop? :D

jmp $
Encryptor256's Investigation \ Research Department.

Offline Rob Neff

  • Forum Moderator
  • Full Member
  • *****
  • Posts: 429
  • Country: us
Re: Win64 Basic Hello World NASM MINGW64 GoLink (Example Code)
« Reply #3 on: October 23, 2013, 03:31:29 PM »
The invoke macro displayed here is a good attempt for someone just starting out with x64 programming and just wanting "something" to work.  Unfortunately, there are many additional considerations that must be factored in when attempting to create a truly general purpose macro.  Thus I can't recommend it for general use although we should all be thankful for encryptor256's various efforts to date with sharing of source code.

Let's look at one issue.  Stack alignment prior to a call is expected to be on a 16-byte boundary.  In encryptor256's current invoke macro there is no check for this.  A user may pass in any number of different parameters when calling various functions when attempting to use that macro and get very strange results or potential crashes.

Not related to the macro itself but definitely related to the stack:  The called function, unless it is a leaf function, likewise has restrictions on stack alignment.  Local variables used on the stack must be properly aligned.  Padding bytes might have to be used.  The expectation is that either RSP ( or RBP ) will be used as the frame pointer and that each local variable is at a properly aligned offset.  RSP will normally be used since the experienced developer can optimize his/her routine to ensure a properly aligned pointer which accounts for all parameters, locals vars, and return address.

However, by using the standard function prologue of:
Code: [Select]
push rbp           ; align our stack at entry
mov rbp, rsp      ; use RBP as frame reference
sub rsp, 64      ; our 16-byte aligned local storage area
We can accomplish both a fixed frame of reference AND a properly aligned stack after entry into our function since we know the stack was properly aligned prior to the call.

NASMX takes all these considerations into account ( along with the differences between operating systems and calling conventions ).  If anyone needs to deep-dive into learning various rules of x64 assembly calling convention requirements I would suggest looking at the macros PROC and INVOKE in the latest NASMX release.  The source should be friendly enough to read ( IMHO ).  Or one can always disassemble and examine various functions contained in object files generated by an x64 C compiler.

Offline Bryant Keller

  • Forum Moderator
  • Full Member
  • *****
  • Posts: 360
  • Country: us
    • About Bryant Keller
Re: Win64 Basic Hello World NASM MINGW64 GoLink (Example Code)
« Reply #4 on: October 24, 2013, 02:51:10 AM »
Well Bryant, everything is possible.

The newcomer will not know, that call instruction is faster and smaller.

Yes, this was actually my point.

Newcomers have choice, to copy and reuse or to study the source code.

That's why i provided two examples there, one use macro and other not.

It's always about the difference, two examples, can see the difference!

And that's great, but macros by design imply "reuse". My objection was the use of the push/jmp style invocation in the macro, not the source itself. You provided the proper solution in the volatile section of your code, rather than the reusable section.

I made up a ryme about teaching now days:

Don't do this,
don't do that,
i don't like this,
i don't like that,
don't do that!

Never use goto statement in C or C++,
always do the things what other said to you,
like being honest and paying taxes.

What about, one, big, blue screen all over your desktop? :D

jmp $

I'm sorry, I don't understand your rhyme as is.  ???
« Last Edit: October 24, 2013, 02:52:44 AM by Bryant Keller »

About Bryant Keller
bkeller@about.me