The MASM Forum Archive 2004 to 2012

General Forums => The Workshop => Topic started by: ecube on April 16, 2007, 10:09:55 PM

Title: Improve _invoke
Post by: ecube on April 16, 2007, 10:09:55 PM
below is macro code I found on this forum awhile ago that allows you to call a function address indirectly. I forget who uploaded but thankyou for sharing whoever you are. Anyway this code works great for the most part but recently i've come across slight errors I was hoping someone here could help me fix
under the macro code is example code that causes errors.

_invoke MACRO funcname:REQ,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19,p20
local pos

    FOR arg,<p20,p19,p18,p17,p16,p15,p14,p13,p12,p11,p10,p9,p8,p7,p6,p5,p4,p3,p2,p1>

        IFNB <arg>
             pos=@InStr(1,arg,<ADDR>) OR @InStr(1,arg,<addr>) OR @InStr(1,arg,<Addr>)

             IF pos

                IF (OPATTR(@SubStr(arg,%pos+5))) EQ 98
                        lea eax,@SubStr(<arg>,%pos+5)
                        push eax
                ELSE
                        push OFFSET @SubStr(<arg>,%pos+5)
                ENDIF

             ELSE
                        push arg
             ENDIF
        ENDIF
    ENDM
call funcname
ENDM



;connectb points to connect's address returned from GetProcAddress

test proc dwSocket:DWORD
local SockAddrIn:sockaddr_in
_invoke connectb, dwSocket, addr SockAddrIn, sizeof SockAddrIn
ret
test endp


during compilation I get the error

Assembling: test.asm
test.asm(147) : error A2091: index value past end of string
@SubStr(1): Macro Called From
  MacroLoop(7): iteration 19: Macro Called From
   _invoke(21): Macro Called From
    test.asm(147): Include File
test.asm(147) : error A2091: index value past end of string

147 points to _invoke connectb line
Title: Re: Improve _invoke
Post by: MichaelW on April 17, 2007, 03:10:01 AM
The problem is the names with an embedded "Addr". This coding of the macro appears to eliminate the problem by rejecting any variation of  "addr" that is not followed by a space or tab, but I did not test it beyond ensuring that it would assemble OK.

_invoke MACRO funcname:REQ,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19,p20

    LOCAL pos

    iswhite = 0

    FOR arg,<p20,p19,p18,p17,p16,p15,p14,p13,p12,p11,p10,p9,p8,p7,p6,p5,p4,p3,p2,p1>

        IFNB <arg>

             pos=@InStr(1,arg,<ADDR>) OR @InStr(1,arg,<addr>) OR @InStr(1,arg,<Addr>)

             IF pos

                IFIDN @SubStr(arg,%pos+4,1), < >       ;; space
                  iswhite = 1
                ENDIF
                IFIDN @SubStr(arg,%pos+4,1), < >      ;; tab
                  iswhite = 1
                ENDIF

                IF iswhite

                    IF (OPATTR(@SubStr(arg,%pos+5))) EQ 98
                        lea eax,@SubStr(<arg>,%pos+5)
                        push eax
                    ELSE
                        push OFFSET @SubStr(<arg>,%pos+5)
                    ENDIF

                 ENDIF   

             ELSE
                   push arg
             ENDIF
        ENDIF
    ENDM
    call funcname
ENDM

Title: Re: Improve _invoke
Post by: ecube on April 17, 2007, 04:56:58 AM
Fantastic MichaelW!! You're definitely an all star  :U, that fixed the problem. I have been testing this macro ,which I assume you created?(is so thanks a lot it's extremely usefull) a great deal and it works well under all the tests I put it through except one last one, if you don't feel like writing anymore code that's fine, if you can however give me a hint in to whats wrong with it in a more clearer picture then what the compiler gave i'd appreciate it.



.386
.model flat, stdcall
option casemap:none

include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32
includelib \masm32\lib\user32


_invoke MACRO funcname:REQ,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19,p20

    LOCAL pos

    iswhite = 0

    FOR arg,<p20,p19,p18,p17,p16,p15,p14,p13,p12,p11,p10,p9,p8,p7,p6,p5,p4,p3,p2,p1>

        IFNB <arg>

             pos=@InStr(1,arg,<ADDR>) OR @InStr(1,arg,<addr>) OR @InStr(1,arg,<Addr>)

             IF pos

                IFIDN @SubStr(arg,%pos+4,1), < >       ;; space
                  iswhite = 1
                ENDIF
                IFIDN @SubStr(arg,%pos+4,1), < >      ;; tab
                  iswhite = 1
                ENDIF

                IF iswhite

                    IF (OPATTR(@SubStr(arg,%pos+5))) EQ 98
                        lea eax,@SubStr(<arg>,%pos+5)
                        push eax
                    ELSE
                        push OFFSET @SubStr(<arg>,%pos+5)
                    ENDIF

                 ENDIF   

             ELSE
                   push arg
             ENDIF
        ENDIF
    ENDM
    call funcname
ENDM


     CTEXT MACRO text:VARARG
            local TxtName
              .data
               TxtName BYTE text,0
              .code
            EXITM <ADDR TxtName>
     ENDM
     
testproc proto

.data?
MessageBoxb dd ?

TestStruct STRUCT
startbuf byte 512 dup(?)
hi       dd ?
bye      dd ?
TestStruct ENDS

ArryofStucts  TestStruct <>
                                     TestStruct <>
                                     TestStruct <>
.code
start:
invoke LoadLibrary,CTEXT("user32.dll")
mov ecx,eax
invoke GetProcAddress,ecx,CTEXT("MessageBoxA")
mov MessageBoxb,eax

invoke testproc
invoke ExitProcess,0

testproc proc
mov esi, offset ArryofStucts
assume esi:ptr TestStruct

invoke lstrcpy,addr [esi].startbuf,CTEXT("test string")
_invoke MessageBoxb,0,addr [esi].startbuf,CTEXT("title"),MB_OK ;if you change this to invoke MessageBox is works correctly otherwise gives compilation error below
ret
testproc endp

end start


C:\test.asm(90) : fatal error A1016: Internal Assembler Error
MacroLoop(21): iteration 19: Macro Called From
  _invoke(36): Macro Called From
   C:\test.asm(90): Main Line Code

Title: Re: Improve _invoke
Post by: MichaelW on April 17, 2007, 06:42:30 AM
Vortex was the original author:

http://www.masm32.com/board/index.php?topic=5981.0

The problem appears to be that the macro is not coded to properly handle the:

addr [esi].startbuf

For this operand OPATTR returns the value 34 instead of 98, so the macro was trying to do a:

push OFFSET [esi].startbuf

Instead of:

lea eax, [esi].startbuf
push eax

This version corrects the problem, but I'm not sure that the fix will not break something else.

_invoke MACRO funcname:REQ,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19,p20

    LOCAL pos
    iswhite = 0

    FOR arg,<p20,p19,p18,p17,p16,p15,p14,p13,p12,p11,p10,p9,p8,p7,p6,p5,p4,p3,p2,p1>
        IFNB <arg>
            pos=@InStr(1,arg,<ADDR>) OR @InStr(1,arg,<addr>) OR @InStr(1,arg,<Addr>)
            IF pos
                IFIDN @SubStr(arg,%pos+4,1), < >       ;; space
                    iswhite = 1
                ENDIF
                IFIDN @SubStr(arg,%pos+4,1), < >      ;; tab
                    iswhite = 1
                ENDIF
                IF iswhite
                    IF (OPATTR(@SubStr(arg,%pos+5))) EQ 98 OR (OPATTR(@SubStr(arg,%pos+5))) EQ 34
                        lea eax,@SubStr(<arg>,%pos+5)
                        push eax
                    ELSE
                        push OFFSET @SubStr(<arg>,%pos+5)
                    ENDIF
                ENDIF
             ELSE
                push arg
             ENDIF
        ENDIF
    ENDM
    call funcname
ENDM


I think for what you are doing the most reliable method, and the easiest to understand and debug, would be to forget about the macro and just code the necessary instructions directly.
Title: Re: Improve _invoke
Post by: ecube on April 17, 2007, 07:15:49 AM
Great! thanks again, the masm community is definitely lucky to have you apart of it :U. As far as your advice goes _invoke is just too neat not to use it :D in comparison to the tedious push push push push call debacle.  And while debugging the macro doesn't seem to add any overhead either, outputs the same as that just mentioned which is nice. I don't really understand macro coding in masm as of yet, minus at the basic level, but this whole thing definitely sparked my interest and I think it's time I start learning, to make life easier :)
Title: Re: Improve _invoke
Post by: sinsi on April 17, 2007, 07:27:27 AM
But I can't see the difference between INVOKE and _invoke.
Title: Re: Improve _invoke
Post by: MichaelW on April 17, 2007, 08:48:31 AM
INVOKE requires a prototype so it cannot be used simply and directly with a function pointer, although AFAIK there are three MASM32 macros that can create a suitable prototype, and the two more recent ones are very easy to use.
Title: Re: Improve _invoke
Post by: lingo on April 17, 2007, 01:38:52 PM
Quote"I think for what you are doing the most reliable method, and the easiest to understand and debug
would be to forget about the macro and just code the necessary instructions directly."

I agree with Michael because if we learned and used the "pure" assembly language of
the debugger we can avoid implementation of the dangerous high level "easy code" macros written by lamers

May be is a time to forget about push, pop and inc 
and substitute them in our not virus code with:  :lol
sub esp, N*4
mov [esp+1*4], offset Mystring
mov dword ptr [esp+2*4], 17
....
mov [esp+N*4], offset MyTitle

and
       inc  ecx
with
      add  ecx, 1 

Regards,
Lingo
Title: Re: Improve _invoke
Post by: Vortex on April 17, 2007, 05:53:23 PM
Michael,

Thanks for the fixup, I never tested that condition with ASSUME.

lingo,

Once again, I am not suprised to see here your bla bla bla, we know all that this is your trademark. :bg
Title: Re: Improve _invoke
Post by: hutch-- on April 17, 2007, 10:40:23 PM
Depends on your sense of humour, the last time I tested direct writing procedure call arguments to the stack as against conventional PUSH / CALL syntax, the PUSH / CALL was faster.
Title: Re: Improve _invoke
Post by: lingo on April 17, 2007, 11:28:45 PM
Depends on your sense of humour it will be interesting for us to see
and re-test your testing bed..   :lol
Title: Re: Improve _invoke
Post by: u on April 18, 2007, 01:47:27 AM
The benchmark results show the "mov [esp+X*4],imm32/reg32" is faster than push:

IMM: 9 vs 11 (cycles, "mov" vs "push")
REG: 7 vs 11
VAR: 11
(on Sempron3000+)

... but the speed gap disappears when in the tested proc we start using the parameters, or we simply iterate less times. Both cases being dominant in real code.

[attachment deleted by admin]
Title: Re: Improve _invoke
Post by: hutch-- on April 18, 2007, 02:16:45 AM
 :bg

> Depends on your sense of humour it will be interesting for us to see and re-test your testing bed.

Your wish is my command.  :P

Immediate operand is faster with PUSH / CALL
Register operand is same speed with both techniques.

I did not bother to test a memory operand as it would require a register copy first where a PUSH does not so it would be slower again.


; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    include \masm32\include\masm32rt.inc
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

comment * -----------------------------------------------------
                        Build this  template with
                       "CONSOLE ASSEMBLE AND LINK"
        ----------------------------------------------------- *

    .code

start:
   
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

    call main
    inkey
    exit

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

main proc

    push esi
    push edi

    operand equ <0>         ; immediate operand

    print "======================",13,10
    print "Test immediate operand",13,10
    print "======================",13,10

  ; ==============================================

    REPEAT 8

    invoke GetTickCount
    push eax

    mov esi, 100000000
  @@:
    push operand
    push operand
    push operand
    push operand
    call testproc
    sub esi, 1
    jnz @B

    invoke GetTickCount
    pop ecx
    sub eax, ecx

    print str$(eax)," push call syntax",13,10

  ; ==============================================

    invoke GetTickCount
    push eax

    mov esi, 100000000
  @@:
    sub esp, 16
    mov [esp],    DWORD PTR operand
    mov [esp+4],  DWORD PTR operand
    mov [esp+8],  DWORD PTR operand
    mov [esp+12], DWORD PTR operand
    call testproc
    sub esi, 1
    jnz @B

    invoke GetTickCount
    pop ecx
    sub eax, ecx

    print str$(eax)," direct write syntax",13,10

    ENDM

  ; ==============================================

    print "=====================",13,10
    print "Test register operand",13,10
    print "=====================",13,10

    operand equ <eax>     ; register operand

  ; ==============================================

    REPEAT 8

    invoke GetTickCount
    push eax

    mov esi, 100000000
  @@:
    push operand
    push operand
    push operand
    push operand
    call testproc
    sub esi, 1
    jnz @B

    invoke GetTickCount
    pop ecx
    sub eax, ecx

    print str$(eax)," push call syntax",13,10

  ; ==============================================

    invoke GetTickCount
    push eax

    mov esi, 100000000
  @@:
    sub esp, 16
    mov [esp],    operand
    mov [esp+4],  operand
    mov [esp+8],  operand
    mov [esp+12], operand
    call testproc
    sub esi, 1
    jnz @B

    invoke GetTickCount
    pop ecx
    sub eax, ecx

    print str$(eax)," direct write syntax",13,10

    ENDM

  ; ==============================================

    pop edi
    pop esi

    ret

main endp

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

align 16

testproc:

    nop
    nop

    retn 16

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

end start


Result on my old Northwood dev box.


======================
Test immediate operand
======================
375 push call syntax
453 direct write syntax
375 push call syntax
469 direct write syntax
390 push call syntax
454 direct write syntax
375 push call syntax
468 direct write syntax
391 push call syntax
453 direct write syntax
375 push call syntax
453 direct write syntax
375 push call syntax
453 direct write syntax
391 push call syntax
453 direct write syntax
=====================
Test register operand
=====================
375 push call syntax
391 direct write syntax
375 push call syntax
375 direct write syntax
390 push call syntax
375 direct write syntax
375 push call syntax
391 direct write syntax
375 push call syntax
375 direct write syntax
391 push call syntax
375 direct write syntax
375 push call syntax
375 direct write syntax
375 push call syntax
406 direct write syntax
Press any key to continue ...
Title: Re: Improve _invoke
Post by: lingo on April 19, 2007, 01:16:21 PM
I appreciate your sense of humour  :lol
but your test results are not real because:

The time of the "mov [esp],    DWORD PTR operand"  *4
  is very, very, very, very, very .... little part of the sum of :

   - delay of the loop; instruction fetching-> due to smaller size push
     is "automatically" aligned ;  for mov we need to do that manually...
   - GetTickCount API ( why not RDTSC as Ultrano)
   - call testproc with ret  ( we don't need it-> may be add esp, 4*4 is better)

Unfortunately  I can't offer better test bed right now...



Title: Re: Improve _invoke
Post by: hutch-- on April 19, 2007, 10:26:48 PM
The virtue of identical measuring techniques working in real time is the capacity to see the difference measured in real time.

Quote
The time of the "mov [esp],    DWORD PTR operand"  *4
  is very, very, very, very, very .... little part of the sum of :

The virtue of objective testing iis that "push operand" takes an even smaller part of the sum.

RDTSC
In ring3 it has no precision gain.
Title: Re: Improve _invoke
Post by: lingo on April 19, 2007, 10:57:17 PM
Quote"push operand" takes an even smaller part of the sum.

Disagree because:
- Modern CPU can manage 8 or 16 operations in
  two clocks with a throughput of one instruction per clock and really
  speed up code that lends itself well to parallel operations.
  It is true for mov but not for push because :
  2 and more push can`t paralleled (second push can`t execute before increment ESP)

- we can use MMX mov too..

- push reg -> 2  uops
  mov  mem, r -> 1 uops  (see A.Fog)

Title: Re: Improve _invoke
Post by: hutch-- on April 21, 2007, 11:08:17 PM
I have split this topic because it shifted in its content. The offshoot is in the Colloseum.
Title: Re: Improve _invoke
Post by: ecube on December 28, 2007, 10:04:02 AM
are there any noticeable limitations with the latest verison of _invoke? can I for example use

     CTEXT MACRO text:VARARG
            local TxtName
              .data
               TxtName BYTE text,0
              .code
            EXITM <ADDR TxtName>
     ENDM

_invoke apifunc,CTEXT("hi")

safely and things of that nature? I want to use _invoke in a indepth project but would like to have a "headsups" on issues to save time on possibly debugging or a broken application.
Title: Re: Improve _invoke
Post by: Vortex on December 28, 2007, 09:20:10 PM
It works :


include \masm32\include\masm32rt.inc
include invoke.inc

CTEXT MACRO text:VARARG
    LOCAL TxtName
    .data
    TxtName BYTE text,0
    .code
    EXITM <ADDR TxtName>
ENDM

.code

start:

    _invoke MessageBox,0,CTEXT("_invoke demo"),CTEXT("Hello"),MB_OK
    _invoke ExitProcess,0

END start

[attachment deleted by admin]