News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Improve _invoke

Started by ecube, April 16, 2007, 10:09:55 PM

Previous topic - Next topic

ecube

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

MichaelW

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

eschew obfuscation

ecube

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


MichaelW

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.
eschew obfuscation

ecube

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 :)

sinsi

But I can't see the difference between INVOKE and _invoke.
Light travels faster than sound, that's why some people seem bright until you hear them.

MichaelW

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.
eschew obfuscation

lingo

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

Vortex

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

hutch--

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.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

lingo

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

u

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]
Please use a smaller graphic in your signature.

hutch--

 :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 ...
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

lingo

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...




hutch--

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.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php