Related Projects > NASMX
Win64 If invoke argument count is greater than four (RAX Glitch)
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