The MASM Forum Archive 2004 to 2012

General Forums => The Workshop => Topic started by: Vortex on December 18, 2005, 04:15:53 PM

Title: A very simple GAS example
Post by: Vortex on December 18, 2005, 04:15:53 PM
For those who are interested, here is a simple GAS example.

.intel_syntax noprefix
.global _start

.data
.extern _MessageBoxA@16
.extern _ExitProcess@4

.set MessageBox,_MessageBoxA@16 /* .set = EQU */
.set ExitProcess,_ExitProcess@4

message:
.ascii "Hello world!"           /* define NULL terminated string */
.byte 0
caption:
.ascii "Message box"
.byte 0

.text
_start:
    pushd   0                   /* push DOUBLE word */
    push    OFFSET caption
    push    OFFSET message
    pushd   0
    call    MessageBox
    pushd   0
    call    ExitProcess


Building the project : ( I am using the GNU tools coming with FreeBASIC package )
C:\Freebasic\bin\win32\as -o %1.o %1.asm
C:\Freebasic\bin\win32\ld -e_start -subsystem windows -Lc:\masm32\lib -o %1.exe %1.o --library=user32  --library=kernel32 -s

[attachment deleted by admin]
Title: Re: A very simple GAS example
Post by: hutch-- on December 18, 2005, 08:24:46 PM
Vortex,

Thanks for the demo, I wonder if there is another way to get hold of "AS" ? I remember climbing through the GNU site and could not find it.
Title: Re: A very simple GAS example
Post by: Vortex on December 18, 2005, 08:36:36 PM
Hi Hutch,

All the information what I found to use the GNU assembler as.exe :

Manual for as.exe :

http://www.gnu.org/software/binutils/manual/gas-2.9.1/html_mono/as.html#SEC2

A nice CHM documentation :

ftp://ftp.berlios.de/pub/htmlhelp/as-2.15.chm
Title: Re: A very simple GAS example
Post by: GregL on December 18, 2005, 09:00:40 PM
Well, at least it supports Intel syntax now, it didn't used to. I couldn't stand looking at gas code before, this is better.
Title: Re: A very simple GAS example
Post by: Vortex on December 18, 2005, 09:26:30 PM
Hi Greg,

Yes, you are right. One feels really very uncomfortable with the  AT&T syntax.
Title: Re: A very simple GAS example
Post by: MusicalMike on December 18, 2005, 10:56:09 PM
The only reason to use gas is if you are on a linux machine, and FASM does the job better.
Title: Re: A very simple GAS example
Post by: hutch-- on December 19, 2005, 12:02:14 AM
Looks like a nice low level tool. With thanks to Vortex for the original example, here is a quick play with GAS using the Intel syntax.


/* -------------------------------------------------------------- */

    .intel_syntax noprefix
    .global _entry_point

    .macro msgbox arg1,arg2,arg3,arg4
      push \arg4                              /* push DOUBLE word */
      push OFFSET \arg3
      push OFFSET \arg2
      push \arg1
      call MessageBox
    .endm

    .macro quit
      push 0
      call ExitProcess
    .endm

  .data
    .extern _MessageBoxA@16
    .extern _ExitProcess@4

    .set MessageBox,_MessageBoxA@16
    .set ExitProcess,_ExitProcess@4

.text

    // ----------------------------------------
    // howdy, I am a leading C++ commented text
    // ----------------------------------------

    message: .ascii "Its a GAS\x00"     /* ZERO terminated string */
    caption: .ascii "Hey man !\x00"

/* -------------------------------------------------------------- */

_entry_point:

    .data
      buffer: .ascii "                    "
    .text

    push ebx

    mov ecx, OFFSET message
    mov edx, OFFSET buffer
    xor ebx, ebx
    call szcopy

    pop ebx

    msgbox 0,buffer,caption,0

    quit

/* -------------------------------------------------------------- */

  szcopy:
    mov al, BYTE PTR [ecx+ebx]
    mov BYTE PTR [edx+ebx], al
    add ebx, 1
    test al, al
    jnz szcopy

    ret

/* -------------------------------------------------------------- */
Title: Re: A very simple GAS example
Post by: GregL on December 19, 2005, 12:21:13 AM
I had a play with it too. It's not that bad. Once you get the hang of it, it works pretty well.


.intel_syntax noprefix
.arch pentium
.global _start

GetStdHandle = _GetStdHandle@4
lstrlen      = _lstrlenA@4
WriteConsole = _WriteConsoleA@20
ExitProcess  = _ExitProcess@4

NULL              =   0
STD_OUTPUT_HANDLE = -11

.data

    szTitle: .asciz "\nGNU Assembler Example\n\n"
    szMsg:   .asciz " Hello from GAS!\n\n"

.bss

    hStdOut:      .long 0
   
    dwNumWritten: .long 0

    dwNumToWrite: .long 0

    // szBuffer:  .space 255, 0

.text

_start:

    push    STD_OUTPUT_HANDLE
    call    GetStdHandle
    mov     hStdOut, eax
   
    push    OFFSET szTitle
    call    lstrlen
    mov     dwNumToWrite, eax

    push    NULL
    push    OFFSET dwNumWritten
    push    dwNumToWrite
    push    OFFSET szTitle
    push    hStdOut
    call    WriteConsole
   
    push    OFFSET szMsg
    call    lstrlen
    mov     dwNumToWrite, eax
   
    push    NULL
    push    OFFSET dwNumWritten
    push    dwNumToWrite
    push    OFFSET szMsg
    push    hStdOut
    call    WriteConsole

    push    0
    call    ExitProcess
   
.end

Title: Re: A very simple GAS example
Post by: hutch-- on December 19, 2005, 12:58:06 AM
Has some useful directives as well.


/* -------------------------------------------------------------- */

    .intel_syntax noprefix
    .global _entry_point

    .macro msgbox arg1,arg2,arg3,arg4
      push \arg4                              /* push DOUBLE word */
      push OFFSET \arg3
      push OFFSET \arg2
      push \arg1
      call MessageBox
    .endm

    .macro exit
      push 0
      call ExitProcess
    .endm

  .data
    .extern _MessageBoxA@16
    .extern _ExitProcess@4

    .set MessageBox,_MessageBoxA@16
    .set ExitProcess,_ExitProcess@4

    .set MB_OK,                 0x00
    .set MB_OKCANCEL,           0x01
    .set MB_ABORTRETRYIGNORE,   0x02
    .set MB_YESNOCANCEL,        0x03
    .set MB_YESNO,              0x04
    .set MB_RETRYCANCEL,        0x05
    .set MB_ICONHAND,           0x10
    .set MB_ICONQUESTION,       0x20
    .set MB_ICONEXCLAMATION,    0x30
    .set MB_ICONASTERISK,       0x40

.text

    // ----------------------------------------
    // howdy, I am a leading C++ commented text
    // ----------------------------------------

    message: .ascii "Its a GAS\nIts a GAS\x00"     /* ZERO terminated string */
    caption: .ascii "Hey man !\x00"

/* -------------------------------------------------------------- */

_entry_point:

    .data
      buffer: .ascii "                              "
    .text

    push OFFSET buffer
    push OFFSET message
    call szcopy

    msgbox 0,buffer,caption,MB_OK | MB_ICONASTERISK

    exit

/* -------------------------------------------------------------- */

  .align 8
  szcopy:

    mov ecx, [esp+4]
    mov edx, [esp+8]

    push ebx
    xor ebx, ebx

  cpy:
  .rept 3
    mov al, [ecx+ebx]
    mov [edx+ebx], al
    add ebx, 0x01
    test al, al
    jz cpout
  .endr

    mov al, [ecx+ebx]
    mov [edx+ebx], al
    add ebx, 0x01
    test al, al
    jnz cpy

  cpout:

    pop ebx

    ret 8

/* -------------------------------------------------------------- */
Title: Re: A very simple GAS example
Post by: hutch-- on December 19, 2005, 01:53:58 AM
It also does address substitution with .long data types.


  // -----------------------
  // zero terminated strings
  // -----------------------
    message: .ascii "Its a GAS\nIts a GAS\x00"     /* ZERO terminated string */
    caption: .ascii "Hey man !\x00"

  // -----------------------------------
  // pointers to zero terminated strings
  // -----------------------------------
    pmsg: .long message
    pttl: .long caption


On the fly .DATA seems to work fine as well.


    .data
      buffer: .fill 128         /* 128 byte buffer */
      pbuf: .long buffer        /* pointer to buffer */
    .text
Title: Re: A very simple GAS example
Post by: hutch-- on December 19, 2005, 03:38:57 AM
Here is a near complete set of include files for AS.

You need to use double backslashes in the paths as follows.

    .include "h:\\gas\\include\\user32.in"
    .include "h:\\gas\\include\\kernel32.in"


[attachment deleted by admin]
Title: Re: A very simple GAS example
Post by: drhowarddrfine on December 19, 2005, 03:40:17 AM
QuoteThe only reason to use gas is if you are on a linux machine, and FASM does the job better.
Well, it is the assembler for GCC so it's probably pretty good.  I started tinkering with it because I run FreeBSD but got sidetracked with some C stuff.  There is a whole tutorial on AS on the FreeBSD sites developers site and the Whizkid has one, too.  (They were both written by himi).  What little I did shows me that it is no more difficult using as/gas than switching between any two similar languages. 
Title: Re: A very simple GAS example
Post by: GregL on December 19, 2005, 06:11:08 AM
An example using msvcrt.dll.


.intel_syntax noprefix
.arch pentium
.global _start

GetStdHandle = _GetStdHandle@4
lstrlen      = _lstrlenA@4
WriteConsole = _WriteConsoleA@20
ExitProcess  = _ExitProcess@4
sprintf      = _sprintf
_getch       = __getch

NULL              =   0
STD_OUTPUT_HANDLE = -11

.macro exit rv
    push    \rv
    call    ExitProcess
.endm

.macro printsz szStr
    push    STD_OUTPUT_HANDLE
    call    GetStdHandle
    mov     hStdOut, eax
    push    OFFSET \szStr
    call    lstrlen
    mov     dwNumToWrite, eax
    push    NULL
    push    OFFSET dwNumWritten
    push    dwNumToWrite
    push    OFFSET \szStr
    push    hStdOut
    call    WriteConsole   
.endm

.macro getPi Pi
    finit
    fldpi
    fstp    QWORD PTR \Pi
.endm   

.data

    szTitle: .asciz "\nGNU Assembler Example\n\n"
    szMsg:   .asciz " It's a GAS!\n\n"
    szFmt:   .asciz " Pi = %.12lf\n\n"
    szPAK:   .asciz "Press any key to exit..."
    szLf:    .asciz "\n"

.bss

    hStdOut:      .long 0
   
    dwNumWritten: .long 0

    dwNumToWrite: .long 0
   
    dblPi:        .double 0.0

    szBuffer:     .space 32, 0

.text

_start:

    printsz szTitle
    printsz szMsg
   
    getPi   dblPi
   
    push    [dblPi+4]
    push    [dblPi+0]
    push    OFFSET szFmt
    push    OFFSET szBuffer
    call    sprintf
    add     esp, 4 * 4  /*_cdecl*/
   
    printsz szBuffer

    printsz szPAK
    call    _getch
    cmp     eax, 0
    je      again
    cmp     eax, 0xE0
    je      again
    jmp     done
  again:
    call    _getch
  done: 
    printsz szLf
   
    exit    0
   
.end
Title: Re: A very simple GAS example
Post by: Vortex on December 19, 2005, 06:30:18 AM
Hutch, Greg,

Thanks for the examples :U
Title: Re: A very simple GAS example
Post by: Vortex on December 19, 2005, 06:21:29 PM
Hello world example linked with PoLink to reduce the size of the final executable to 1536 bytes.

[attachment deleted by admin]
Title: Re: A very simple GAS example
Post by: hutch-- on December 19, 2005, 10:45:37 PM
I have a working complete set of macros that make the API functions more or less high level function in the following form. I just did not know if anyone was interested in them. I have also ripped a set of equates from the masm32 windows.inc that seem to be working OK. All I am having trouble with at the moment is a convenient way to handle structures. If anyone is interested I will post a set somewhere so they are available.

.extern _ActivateKeyboardLayout@8
.macro ActivateKeyboardLayout arg1 arg2
push \arg2
push \arg1
call _ActivateKeyboardLayout@8
.endm

.extern _AdjustWindowRect@12
.macro AdjustWindowRect arg1 arg2 arg3
push \arg3
push \arg2
push \arg1
call _AdjustWindowRect@12
.endm

.extern _AdjustWindowRectEx@16
.macro AdjustWindowRectEx arg1 arg2 arg3 arg4
push \arg4
push \arg3
push \arg2
push \arg1
call _AdjustWindowRectEx@16
.endm
Title: Re: A very simple GAS example
Post by: GregL on December 19, 2005, 11:19:57 PM
Hutch,

I'm interested. GAS is a pretty darn good assembler. I had always avoided it before, mainly because of the AT&T syntax. Now that it supports Intel syntax, I like it. Another tool for the toolkit.


Title: Re: A very simple GAS example
Post by: GregL on December 20, 2005, 03:36:06 AM
Is there a way to handle varargs in the macros?

Title: Re: A very simple GAS example
Post by: MichaelW on December 20, 2005, 05:33:52 AM
FWIW I spent several hours today trying to create an invoke macro. I could find no good, clean method of using a variable number of arguments. Any macro call with more arguments than parameters would trigger "Error: too many positional arguments". When I switched to more parameters than arguments, I could find no direct method of determining which parameters were blank. What I am doing for now is passing the number of arguments and using that to determine which of the other arguments to process. Another problem was that an argument of the form "offset arg" would expand to two arguments. I was able to fix this by specifying the arguments as offset(arg).

    .macro invoke func,argcnt,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10
      .if \argcnt > 9
        push \a10
      .endif
      .if \argcnt > 8
        push \a9
      .endif 
      .if \argcnt > 7
        push \a8
      .endif
      .if \argcnt > 6
        push \a7
      .endif
      .if \argcnt > 5
        push \a6
      .endif
      .if \argcnt > 4
        push \a5
      .endif
      .if \argcnt > 3
        push \a4
      .endif
      .if \argcnt > 2
        push \a3
      .endif       
      .if \argcnt > 1
        push \a2
      .endif
      .if \argcnt > 0
        push \a1
      .endif
      call \func
    .endm

invoke MessageBox,4,0,offset(message),offset(buffer),MB_OK | MB_ICONASTERISK



I think a better method would be to append the number of arguments to the function name, perhaps just the way it is in the include files, for example _MessageBoxA@16, but I don't see where GAS provides any method of extracting the number from the name.


Title: Re: A very simple GAS example
Post by: hutch-- on December 20, 2005, 01:58:34 PM
Sorry to be a bit slow but I have been doing a lot of checking and experimenting with the include file layout. The currect batch are macros in the form suggested above that work as a form of type checking as you at least get some response if the argument count is incorrect. You can either use the macro which is just automated push/call or you can directly use CALL on the undecorated name.

You can do a passable emulation of a structure in the .data section with the following.


  .align 8
  // WNDCLASSEX structure
    wc_cbSize:          .long 48
    wc_style:           .long CS_BYTEALIGNWINDOW | CS_BYTEALIGNCLIENT
    wc_lpfnWndProc:     .long WndProc
    wc_cbClsExtra:      .long 0
    wc_cbWndExtra:      .long 0
    wc_hInstance:       .long 0
    wc_hIcon:           .long 0
    wc_hCursor:         .long 0
    wc_hbrBackground:   .long 0
    wc_lpszMenuName:    .long 0
    wc_lpszClassName:   .long szclassname
    wc_hIconSm:         .long 0


Michael solved the problem of using OFFSET with the macros with the brackets.

There is a big file winequ.gi (gas include) that has a lot of equates in it but the file is rough in that it does not properly handle joined styles. I fixed the WS_OVERLAPPEDWINDOW one for one of the examples but the file needs to be done a lot better than it currently is.

One example is a resource less window that seems to work OK. I have included the two batch files I have been using to build examples and I have so far continued to use the masm32 API import libraries. The MASM32 library is also used.

http://www.masmforum.com/private/gas.zip
Title: Re: A very simple GAS example
Post by: MichaelW on December 20, 2005, 03:24:12 PM
Thanks Hutch :U

A bit slow? You either have very sore fingers now, or you somehow automated the creation of all this.

Title: Re: A very simple GAS example
Post by: Bieb on December 20, 2005, 06:40:17 PM
Say, would anyone happen to know how to write a simple command line program under Linux using GAS?
Title: Re: A very simple GAS example
Post by: Vortex on December 20, 2005, 06:44:52 PM
Hi Hutch,

Very nice work :U
I guess you have a special tool to convert the master windows.inc file to GAS syntax.
Title: Re: A very simple GAS example
Post by: GregL on December 20, 2005, 07:06:26 PM
MichaelW,

I came up with something almost identical to what you did for variable arguments. I was hoping there was a better way.

offset(arg)  :U


Hutch,

Thanks.



Title: Re: A very simple GAS example
Post by: Vortex on December 20, 2005, 08:38:30 PM
MichaelW, thanks for your invoke macro  :U

Based on the macro, I added a feature to support GAS to my tool external function scanner :
// Original example by Hutch

    .intel_syntax noprefix
    .global _start

    .macro szByteCopy arg1,arg2
      push \arg2
      push \arg1
      call szcopy
    .endm

    .macro m2m arg1,arg2
      push \arg2
      pop  \arg1
    .endm

    .include "invoke.gi"
    .include "msgbox.imp"

    .set MB_ICONASTERISK, 0x40
    .set MB_OK, 0x0

  // -----------------------
  // zero terminated strings
  // -----------------------

  .data
    message: .ascii "Its a GAS Its a GAS Its a GAS Its a GAS\n"
             .ascii "Its a GAS Its a GAS Its a GAS Its a GAS\n"
             .ascii "Its a GAS Its a GAS Its a GAS Its a GAS\x00"

    caption: .ascii "Hey man !\x00"

// -----------------------------------
// pointers to zero terminated strings
// -----------------------------------
    pmsg: .long message
    pttl: .long caption

.text

/* ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««« */

_start:

    .bss
      buffer: .fill 128         /*  128 byte buffer  */
    .data
      pbuf:   .long buffer      /* pointer to buffer */
    .text

    invoke GetCL,2,1,pbuf
    cmp eax, 1
    jne nocl

    invoke MessageBox,4,0,pbuf,pttl,MB_OK | MB_ICONASTERISK

    jmp quit

  nocl:
    .data
      nomsg: .ascii "No command line argument was entered\x00"
      nottl: .ascii "Sorry ....\x00"
      pnm: .long nomsg
      pnt: .long nottl
    .text
    invoke MessageBox,4,0,pnm,pnt,MB_OK | MB_ICONASTERISK

  quit:
    invoke ExitProcess,1,0

/* ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««« */

  .align 8
  szcopy:

    mov ecx, [esp+4]
    mov edx, [esp+8]

    push ebx
    xor ebx, ebx

  cpy:
  .rept 3
    mov al, [ecx+ebx]
    mov [edx+ebx], al
    add ebx, 0x01
    test al, al
    jz cpout
  .endr

    mov al, [ecx+ebx]
    mov [edx+ebx], al
    add ebx, 0x01
    test al, al
    jnz cpy

  cpout:

    pop ebx

    ret 8

/* ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««« */


Building the demo :

scan Msgbox.as -ga
as -o Msgbox.obj Msgbox.as
ld -e_start -subsystem windows -L\masm32\lib -o Msgbox.exe Msgbox.obj --library=masm32 --library=user32 --library=kernel32 -s


The output of scan for external functions :

.extern _ExitProcess@4
.set ExitProcess,_ExitProcess@4

.extern _MessageBoxA@16
.set MessageBox,_MessageBoxA@16

.extern _GetCL@8
.set GetCL,_GetCL@8

[attachment deleted by admin]
Title: Re: A very simple GAS example
Post by: hutch-- on December 21, 2005, 02:46:53 PM
I have just finished a much better equate file for AS. This one properly handles the joined ORRED styles.

So far I have not found a way to pass quoted strings to a macro, anyone know if it can be done ?

[attachment deleted by admin]
Title: Re: A very simple GAS example
Post by: NPNW on December 22, 2005, 04:30:16 AM
Hutch,

So far I have not found a way to pass quoted strings to a macro, anyone know if it can be done ?

.ascii "Its a GAS Its a GAS Its a GAS Its a GAS\x00"

You mean something like this?

.ascii ""its a GAS" "Its a GAS" "Its a GAS "Its a GAS"\x00"

Wouldn't this be a problem with the .ascii directive in the GAS compiler?
You would have to make your own routine to keep the " " symbols.

Your table parser rountine useing ascii characters would probably be able to handle this type of conversion as a template. Then you could simplify with a small set symbols or single symbol if you just wanted it to handle the "". Otherwise, I don't know of routines although my knowledge is limited.
Hope this helps give ideas.






Title: Re: A very simple GAS example
Post by: hutch-- on December 22, 2005, 06:48:12 AM
I found ".altmacro" buried in the documentation,


    .altmacro
    .macro sztext uname,quoted_text
      .data
        uname&: .string quoted_text
      .text
    .endm
Title: Re: A very simple GAS example
Post by: James Ladd on May 26, 2006, 05:11:59 AM
Ok, here is my super simple GAS example as it doesnt have any code really, just calls exit.
There is a make to simplify assembling and linking.
I'll extend the example to include a DLL and call a function in it.

Does anyone know where I can find the 'invoke' macro used in the other examples
below that works with GAS?

Note: Im using cygwin under WinXP to build my examples.

Rgs, James.

simple.s

.intel_syntax noprefix

.arch pentium

.global _start

.data
    .extern _ExitProcess@4

.text

_start:

    xor eax, eax
    pushd 0
    call _ExitProcess@4


Makefile  (note that there are tabs in the Makefile)


SRC = simple.s

OBJ = simple.o

BIN = simple

LIBS = kernel32


all: $(OBJ) $(BIN)

clean:
rm -f $(OBJ)
rm -f $(BIN)

.s.o:
as -g -o $@ $<

simple: simple.o
ld -e_start -o simple simple.o -l$(LIBS)

Title: Re: A very simple GAS example
Post by: Vortex on October 14, 2006, 09:47:55 AM
Here is a dialog box example with GAS :

.intel_syntax noprefix
.global _start

.include "invoke.gi"
.include "Dlgbox.gi"

.data
DlgName:
    .asciz "MyDialog"

.bss
hInstance:
    .long 0
hCursor:
    .long 0

.text

_start:

invoke     GetModuleHandle,1,0
mov        hInstance,eax
invoke     DialogBoxParam,5,hInstance,OFFSET(DlgName),0,OFFSET(DlgProc),0
invoke     ExitProcess,1,eax


// DlgProc PROC hWnd:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD

DlgProc:

    push    ebp
    mov     ebp,esp

    cmp     dword ptr [ebp+uMsg], WM_CLOSE
            jne     initdlg
            invoke  EndDialog,2,[ebp+hWnd],0
            jmp     true

initdlg:

    cmp     dword ptr [ebp+uMsg], WM_INITDIALOG
            jne     setcursor
            invoke  LoadCursor,2,0,IDC_CROSS
            mov     hCursor,eax
            jmp     true

setcursor:

    cmp     dword ptr [ebp+uMsg], WM_SETCURSOR

jne       false
invoke    SetCursor,1,hCursor
jmp       true

false:

    xor     eax,eax
    pop     ebp
    ret     16

true:

    mov     eax,1
    pop     ebp
    ret     16

[attachment deleted by admin]