Been practicing MASM lately. Not sure how this would work on other PCs but here is a code sample I been working on the past days. Just a simple routine to display the stack. But my urgency is about turning this kind of code into 1) an accessible .obj and 2) DLL, the native way if possible - that works in almost all situations and using any linker possible. I still don't quite get the full picture of various linking techniques in 64-bit environment.
Just need a simple outline/layout for such conversions and other related things like the requirement for underscores, decorated names etc.
Thanks in advance.
Hi coder,
Here is a similar example :
EXTERNDEF printf:PROC
EXTERNDEF ExitProcess:PROC
option casemap:none
.data
format db '%.16X %.16X',13,10,0
.code
mainCRTStartup PROC
mov rdx,rsp
sub rsp,4*8+8
mov rcx,10
call DisplayStack
xor rcx,rcx
call ExitProcess
mainCRTStartup ENDP
DisplayStack PROC
LOCAL dummy:QWORD ; copy rcx and rdx
LOCAL _rcx:QWORD ; to aligned memory locations
LOCAL dummy2:QWORD
LOCAL _rdx:QWORD
sub rsp,4*8
shl rcx,3
add rdx,rcx
mov _rcx,rcx
mov _rdx,rdx
@@:
mov rcx,OFFSET format
mov r8,_rdx
mov rdx,QWORD PTR [r8]
call printf
sub _rdx,8
sub _rcx,8
jnz @b
ret
DisplayStack ENDP
END
The object module has no any decorated symbols :
\PellesC\bin\podump.exe /SYMBOLS DisplayStack64.obj
Dump of DisplayStack64.obj
File type: OBJ
SYMBOL TABLE
0000 009E9D1B ABS notype static | @comp.id
0001 00000000 SECT1 notype static | .text
length of section 5A, #relocations 3, #linenumbers 0
0003 00000000 SECT2 notype static | .data
length of section E, #relocations 0, #linenumbers 0
0005 00000000 SECT3 notype static | .debug$S
length of section 70, #relocations 0, #linenumbers 0
0007 00000000 UNDEF notype external | printf
0008 00000000 UNDEF notype external | ExitProcess
0009 00000000 SECT2 notype static | format
000A 00000000 SECT1 notype () external | mainCRTStartup
000B 0000001B SECT1 notype () external | DisplayStack
Thanks for the alternative code Vortex. Gives me new idea on printf formatting.
On to the code, if you could make a slight adjustment to your code, you should be able to see the actual / current content of the stack instead.
push rcx
push rdx
push rbx
mov rax,10
call DisplayStack64 ;Display the 3 objects recently pushed above
The adjustment will serve the purpose of the module even more accurately.
Btw, I managed to make an accessible object (.obj) by using the "public" keyword and it is now linkable from both LINK.exe and GCC. I tried linking with GOLINK but it hangs on me. Not sure why but I'll try again if I have time.
Pushing three registers on the stack may have bad consequences in 64-bit land :eusa_naughty:
include \Masm32\MasmBasic\Res\JBasic.inc ; ## console demo, builds with ML, AsmC, JWasm, HJWasm ##
Init ; OPT_64 1 ; requires MasmBasic (and nothing else) (http://masm32.com/board/index.php?topic=94.0)
PrintLine Chr$("This code was assembled with ", @AsmUsed$(1), " in ", jbit$, "-bit format")
.data?
buffer dq 8 dup(?)
.code
push 88888888
push 77777777
push 66666666
push 55555555
push 44444444
push 33333333
push 22222222
push 11111111
mov rsi, rsp
lea rdi, buffer
mov ecx, 8
rep movsq
lea rsi, buffer
.Repeat
lodsq
Print Str$("%i\n", rax)
.Until rsi>=rdi
Inkey "ok?"
EndOfCode
Output:
This code was assembled with HJWasm32 in 64-bit format
11111111
22222222
33333333
44444444
55555555
66666666
77777777
88888888
ok?
Hi jj2007. Thanks for the reminder and nice to know you.
I think even or odd pushes will not affect the stack because I am designing it to work around TOS or specifically from the caller's TOS. As long as the user uses the stack correctly, this module should give persistent and accurate feedback. But I haven't tested it thoroughly though.
Hi coder,
Same code linked with GoLink :
\masm32\bin64\ml64.exe /c DisplayStack64.asm
\goasm\golink /console /entry mainCRTStartup DisplayStack64.obj kernel32.dll msvcrt.dll
Thanks a lot vortex. Exactly what I need.
Any idea why it fails on linking to kernel32.lib and msvcrt.lib using the same command?
Hi coder,
GoLink is designed to import functions from DLLs not from import libraries :
QuoteInput files - dll/ocx/exe/drv files
Another type of input file is one containing exports, such as a DLL. Exports may be in DLL, OCX, DRV or other EXE files and you can provide the names of these in the command line or in a command file, or in your source code by using the directive #dynamiclinkfile if you are using GoAsm (see the GoAsm help file how to do this). Unlike other linkers there is no need to use LIB files. During the linking process GoLink looks at the list of files one by one for the required imports. When it has found all the required imports it will stop looking at the listed files. Because of this, GoLink will run more quickly if you list these files in order of popularity, by putting at the top of the list those which contains most of the required imports. Usually this order for the system DLLs is a good one:-
these contain most of the APIs you will be using:-
Kernel32.dll
User32.dll
Gdi32.dll
http://www.godevtool.com/GolinkFrame.htm
Thanks for the pointer Vortex. Now I know that even if .OBJ is a universal format, linkers may impose they own specific requirements.
I've updated the code by inserting an example on how to view the shadow space as per requirement (https://msdn.microsoft.com/en-us/library/ew5tede7.aspx) of MS ABI. It is much clearer to me now. This is the output from the example code.
;; Demo: viewing the shadow space
;; ml64 stack64.asm /c
;; gcc -m64 stack64.obj -o stack64.exe
externdef printf:proc
option casemap:none
.code
main PROC
mov rcx,1 ;populate all four params
mov rdx,2
mov r8,3
mov r9,4
sub rsp,20h ;allocate shadow space for Function A
mov [rsp ],rcx ;save volatiles (optional)
mov [rsp+ 8],rdx
mov [rsp+16],r8
mov [rsp+24],r9
call FunctionA
mov rcx,[rsp ] ;restore volatiles (optional)
mov rdx,[rsp+8]
mov r8,[rsp+16]
mov r9,[rsp+24]
add rsp,20h ;delete shadow space of FunctionA
ret
main ENDP
;;---------------------------------;;
;; Test function ;;
;;---------------------------------;;
align 16
FunctionA PROC
mov rcx,5
call dispStack ;view shadow space
ret
FunctionA ENDP
0000000000000004 <000000000062FE50 ;R9
0000000000000003 <000000000062FE48 ;R8
0000000000000002 <000000000062FE40 ;RDX
0000000000000001 <000000000062FE38 ;RCX
0000000000402D08 <000000000062FE30 ;caller's RIP
format string "%p %p" is shorter, if small letters are not a problem ;)
That's even better TWell. Thanks. I've uploaded the shorter version according to your suggestion.
Increasing the stack count by 10 reveals that by default PROC/ENDP does not create a stack frame or prologue/epilogue for FunctionA. Wonder if it holds true if I used LOCAL inside FunctionA. Here's the output if I increased the magnitude to 10 instead of just 5
0000000000000000 <000000000062FE78
0000000000407A20 <000000000062FE70
0000000000000008 <000000000062FE68
0000000000000000 <000000000062FE60
00000000004013E8 <000000000062FE58 ;caller's /initial TOS
------- FunctionA shadow space -------
0000000000000004 <000000000062FE50 ;R9
0000000000000003 <000000000062FE48 ;R8
0000000000000002 <000000000062FE40 ;RDX
0000000000000001 <000000000062FE38 ;RCX
0000000000402D08 <000000000062FE30 ;caller's RIP / return address
Thanks for your help.
Had 1 hour spare time. I wrote the same code for 32-bit version. Link it with GCC.
Report bugs to me or modify it yourself. Have fun playing / experimenting with the stack.
EDIT: repair a bug in using (.if .endif. I am really that bad at using high-level statements ;D . Added example of negative view of the stack.
Am I missing something?
Quote0000000000000001 <000000000062FE38 ;RCX
If that is the first param then it isn't aligned to 16
Hi sinsi. Alignment is initiated by the user. This is a utility to view the stack, much like a debugger output. It makes no attempt to align the stack. What it does is to chase the Top of Stack at any point of choice. I hope this makes it clear.
:t