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
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
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
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.
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 :)
But I can't see the difference between INVOKE and _invoke.
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.
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
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
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.
Depends on your sense of humour it will be interesting for us to see
and re-test your testing bed.. :lol
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]
: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 ...
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...
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.
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)
I have split this topic because it shifted in its content. The offshoot is in the Colloseum.
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.
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]