Related Projects > NASMX

Win64 If invoke argument count is greater than four (RAX Glitch)

(1/2) > >>

encryptor256:
Hello!

Win64 If invoke argument count is greater than four (RAX Glitch)

I think, i, somewhere see/saw/seen, that somebody said/written,
that, it is hard to catch RAX usage within invoke macro arguments.
Yes, this results in error, in NASMX invoke macro, RIGHT NOW.

Source code:

--- Code: ---bits 64

%include "nasmx.inc"

IMPORT wsprintfA

NASMX_PRAGMA FASTCALL_STACK_PRELOAD, DISABLE

proc poo
locals none

invoke wsprintfA,0,0,0,0,[rax+0xAAAA],[rax+0xBBBB]

endproc

--- End code ---

Compiled with: "nasm.exe -f win64 -o test.obj test.asm".
NDisasm with: "ndisasm.exe -b 64 test.obj", results in:

--- Code: ---0000003C  55                push rbp
0000003D  4889E5            mov rbp,rsp
00000040  4883EC30          sub rsp,byte +0x30
00000044  4831C9            xor rcx,rcx
00000047  4831D2            xor rdx,rdx
0000004A  4D31C0            xor r8,r8
0000004D  4D31C9            xor r9,r9
00000050  488B80AAAA0000    mov rax,[rax+0xaaaa]
00000057  4889442420        mov [rsp+0x20],rax
0000005C  488B80BBBB0000    mov rax,[rax+0xbbbb]
00000063  4889442428        mov [rsp+0x28],rax
00000068  E800000000        call qword 0x6d
0000006D  4883C430          add rsp,byte +0x30
00000071  4889EC            mov rsp,rbp
00000074  5D                pop rbp
00000075  C3                ret

--- End code ---

These lines results in runtime error:

--- Code: ---...
00000050  488B80AAAA0000    mov rax,[rax+0xaaaa]
00000057  4889442420        mov [rsp+0x20],rax
0000005C  488B80BBBB0000    mov rax,[rax+0xbbbb]
00000063  4889442428        mov [rsp+0x28],rax
...

--- End code ---

So, no need to cry now  :'( , i have a solution.   8)

How to fix, just a skecth of invoke macro:

* Determine if one of the arguments use RAX
* If use RAX, save RAX into some stack place
* Now loop through each argument - prepare it for function call
* If current argument consists of RAX, then restore it from the place, where it was saved before
* And that's it, continue
How to determine if argument contains RAX usage:
So, here is a macro which determines, if an argument uses/contains RAX.


--- Code: ---%macro isRAXUsed0 1

%define ___isRAXUsed 0

%defstr txt %[%1]
%strlen txtlen txt
%if txtlen==3
%ifidni txt,"rax"
%define ___isRAXUsed 1
%endif
%elif txtlen>3
%substr strfirst txt 1,1
%ifidni strfirst,'['
%assign repcounter 2
%rep (txtlen-4)
%substr strP txt repcounter-1,1
%substr strRAX txt repcounter,3
%substr strN txt repcounter+3,1
%assign repcounter repcounter+1
;%warning strP strRAX strN
%ifidni strRAX,"rax"
%assign iP strP
%assign iN strN
%if iP<'0'||iP>'9'&&iP<'A'||iP>'Z'&&iP<'a'||iP>'z'
%if iN<'0'||iN>'9'&&iN<'A'||iN>'Z'&&iN<'a'||iN>'z'
%define ___isRAXUsed 1
%endif
%endif

%endif
%endrep
%endif
%endif
%endmacro

--- End code ---

If RAX is used, then "___isRAXUsed is 1", if not, then "___isRAXUsed is 0".
It works like this, "Determine if RAX is used and print message if used or not":

--- Code: --- isRAXUsed0 [rax+0xAAAA]
%if ___isRAXUsed==1
%warning 0. RAX is used
%else
%warning 0. RAX is not used
%endif

isRAXUsed0 [rbx+rax*8]
%if ___isRAXUsed==1
%warning 1. RAX is used
%else
%warning 1. RAX is not used
%endif

isRAXUsed0 [rax]
%if ___isRAXUsed==1
%warning 2. RAX is used
%else
%warning 2. RAX is not used
%endif

isRAXUsed0 rax
%if ___isRAXUsed==1
%warning 3. RAX is used
%else
%warning 3. RAX is not used
%endif

isRAXUsed0 [0xAAAA+rax*8]
%if ___isRAXUsed==1
%warning 4. RAX is used
%else
%warning 4. RAX is not used
%endif

extern ptrRAX

isRAXUsed0 [ptrRAX+rdx*8]
%if ___isRAXUsed==1
%warning 5. RAX is used
%else
%warning 5. RAX is not used
%endif

extern RAXzzz

isRAXUsed0 [RAXzzz+rdx*8]
%if ___isRAXUsed==1
%warning 6. RAX is used
%else
%warning 6. RAX is not used
%endif

--- End code ---

Output:


--- Code: ---test.asm:10: warning: 0. RAX is used
test.asm:17: warning: 1. RAX is used
test.asm:24: warning: 2. RAX is used
test.asm:31: warning: 3. RAX is used
test.asm:38: warning: 4. RAX is used
test.asm:49: warning: 5. RAX is not used
test.asm:58: warning: 6. RAX is not used

--- End code ---


Im using my own set of macros and
macro "isRAXUsed0" came from my arsenal,
so, you, can have it,
it is tested and so far, no glitches for me.

For example, in my invoke macro, if RAX was used, i saved it into unallocated stack space [rsp-8].

Bye,
Encryptor256.

Rob Neff:
Very good find, encryptor256!  Yes, we already do check for RAX usage as an argument because we have to use some register to move the argument contents to.  We've had some internal discussions regarding this behavior and RAX was chosen by default to handle this chore.  However, the check currently does not look for RAX if we encounter a memory reference using RAX as the base register thus leading to the bug you found.

For example, if your code looked like this instead:

--- Code: ---bits 64

%include "nasmx.inc"

IMPORT wsprintfA

NASMX_PRAGMA FASTCALL_STACK_PRELOAD, DISABLE

proc poo
locals none

invoke wsprintfA,0,0,0,0,[rax+0xAAAA],rax   ; <-- note last rax argument

endproc

--- End code ---

You would get a warning message regarding possible incorrect rax usage.  I'll modify the invoke macro and add this additional check and the save/restore of RAX as well.  I'm wondering if we still want to print out a warning that this leads to unoptimized code since we now have to save/restore rax due to multiple rax arguments.  Probably not necessary if we document the behavior.  Your thoughts?
Thanks, again!

encryptor256:

--- Quote from: Rob Neff on March 01, 2014, 05:17:47 PM ---You would get a warning message regarding possible incorrect rax usage.

--- End quote ---

Confirm, there was a warning message:

--- Code: ---test.asm:12: warning: (INVOKE:409) use of _AX as arg resulted in inconsistent logic state

--- End code ---


--- Quote from: Rob Neff on March 01, 2014, 05:17:47 PM --- I'm wondering if we still want to print out a warning that this leads to unoptimized code since we now have to save/restore rax due to multiple rax arguments.  Probably not necessary if we document the behavior.

--- End quote ---

I agree, "Probably not necessary if we document the behavior".

Bye,
Encryptor256.

encryptor256:
Well, i think, it is not right to call it "unoptimized code".

MINGW64-GCC Tell's the same story: it uses rax register to store arguments.

So, it is not unompimized, it is just the way it is. :)

In directory: "C:\Program Files\mingw-builds\x64-4.8.1-posix-seh-rev5\mingw64\bin"
I compile this C file with: "gcc -Wall -c main.c"

--- Code: ---#include <stdio.h>

#define type long long

void helloworld(type a,type b,type c,type d,type e,type f,type g,type h,type i,type j)
{
printf("\r\nHello world: %I64d, %I64d, %I64d, %I64d, %I64d, %I64d, %I64d, %I64d, %I64d, %I64d",
a,b,c,d,e,f,g,h,i,j);
};

int main()
{
helloworld(1,2,3,4,5,6,7,8,9,10);
return 0;
};

--- End code ---

Then i debug with: "objdump -D -m i386:x86-64 main.obj > out.txt"
Output assembly syntax is a bit alienish, but can be understood well.
Content's of main.c - main procedure:

--- Code: ---0000000000000080 <main>:
  80: 48 83 ec 58          sub    $0x58,%rsp
  84: b8 0a 00 00 00        mov    $0xa,%eax
  89: 48 89 44 24 48        mov    %rax,0x48(%rsp)
  8e: b8 09 00 00 00        mov    $0x9,%eax
  93: 48 89 44 24 40        mov    %rax,0x40(%rsp)
  98: b8 08 00 00 00        mov    $0x8,%eax
  9d: 48 89 44 24 38        mov    %rax,0x38(%rsp)
  a2: b8 07 00 00 00        mov    $0x7,%eax
  a7: 48 89 44 24 30        mov    %rax,0x30(%rsp)
  ac: b8 06 00 00 00        mov    $0x6,%eax
  b1: 48 89 44 24 28        mov    %rax,0x28(%rsp)
  b6: b8 05 00 00 00        mov    $0x5,%eax
  bb: 48 89 44 24 20        mov    %rax,0x20(%rsp)
  c0: 41 b9 04 00 00 00    mov    $0x4,%r9d
  c6: 41 b8 03 00 00 00    mov    $0x3,%r8d
  cc: ba 02 00 00 00        mov    $0x2,%edx
  d1: b9 01 00 00 00        mov    $0x1,%ecx
  d6: e8 25 ff ff ff        callq  0 <helloworld>
  db: 31 c0                xor    %eax,%eax
  dd: 48 83 c4 58          add    $0x58,%rsp
  e1: c3                    retq   

--- End code ---

There we can see, that it uses rax to store arguments.

Bye.

Rob Neff:

--- Quote from: encryptor256 on March 01, 2014, 06:16:05 PM ---There we can see, that it uses rax to store arguments.

--- End quote ---

Yes, but in that C code example you're not passing RAX, which already contains a pointer or some value, as an argument.  Add some assembler code in the mix and watch what happens.  :P

Navigation

[0] Message Index

[#] Next page

Go to full version