To use a custom prologue we need a macro
testprolog macro procname,flag,parmbytes,localbytes,regs,macroargs:vararg
With 32-bit programs, parmbytes is correct - it's always paramcount*4
With 64-bit programs, parmbytes is incorrect - it's always paramcount*8 + 8.
Any idea why?
Do you have a test example of its usage?
Stack alignment to 16 bytes?
Quote from: zedd151 on October 18, 2024, 04:24:01 AMDo you have a test example of its usage?
Microsoft (R) Macro Assembler (x64) Version 14.41.34123.0 10/18/24 03:57:56
C:\asm\testprologue\testprologue32.asm Page 1 - 1
testprolog macro procname,flag,parmbytes,localbytes,regs,macroargs:vararg
mov eax,parmbytes
exitm <0>
endm
testepilog macro procname,flag,parmbytes,localbytes,regs,macroargs:vararg
endm
00000000 .code
option prologue:testprolog
00000000 test0 proc
00000000 B8 00000008 1 mov eax,08H
ret
00000006 test0 endp
00000006 test1 proc param1:word
00000006 B8 00000010 1 mov eax,010H
ret
0000000D test1 endp
end
.386
.model flat,stdcall
testprolog macro procname,flag,parmbytes,localbytes,regs,macroargs:vararg
mov eax,parmbytes
exitm <0>
endm
testepilog macro procname,flag,parmbytes,localbytes,regs,macroargs:vararg
endm
00000000 .code
option prologue:testprolog
00000000 test0 proc
00000000 B8 00000000 1 mov eax,00H
ret
00000006 test0 endp
00000006 test1 proc param1:word
00000006 B8 00000004 1 mov eax,04H
ret
0000000F test1 endp
end
Quote from: Vortex on October 18, 2024, 04:27:34 AMStack alignment to 16 bytes?
But then LOCAL/USES would possibly wreck that alignment. I thought that the whole thing about the 64-bit ABI is that the stack
will be aligned to 8 on entry.
It's supposed to be the number of bytes the parameters take, which makes sense for 32-bit STDCALL when you need to use
RET nn to clean the stack, but FASTCALL leaves the stack as it was (as far as cleaning up goes).
Here's part of my prologue
if save_args EQ 1
if parmbytes GT 24
mov [rbp+40],r9
endif
if parmbytes GT 16
mov [rbp+32],r8
endif
if parmbytes GT 8
mov [rbp+24],rdx
endif
if parmbytes GT 0
mov [rbp+16],rcx
endif
endif
Because parmbytes is always at least 8, it always spills rcx. No big deal, but it's sloppy coding :biggrin:
Here is what I had come up with...
include \masm64\include64\masm64rt.inc
testprolog macro procname,flag,parmbytes,localbytes,regs,macroargs:vararg
mov eax,parmbytes
exitm <0>
endm
testepilog macro procname,flag,parmbytes,localbytes,regs,macroargs:vararg
endm
.data
.code
.code
option prologue:testprolog
Main proc
invoke test1, 1, 2, 3, 4, 5, 6
invoke ExitProcess, 0
Main endp
test0 proc
mov eax,08H
ret
test0 endp
test1 proc param1:word, param2:qword, param3:qword, param4:qword, param5:qword, param6:qword
mov eax,010H
ret
test1 endp
option epilogue:testepilog
end
And the result in x64dbg...
(https://i.postimg.cc/qMNFy7cX/untitled.png)
I can make neither heads nor tales out of it though :tongue: me I'm just playing around in 64 bit-land. :biggrin:
It crashes upon executing 'leave' in test1 procedure...
I was originally missing one argument in the call.... I fixed the above example. :rolleyes:
Quote from: zedd151 on October 18, 2024, 04:52:31 AMIt crashes upon executing 'leave' in test1 procedure...
Using "option epilogue:none" removes the 'leave' instruction. :smiley:
Quote from: sinsi on October 18, 2024, 04:36:37 AM testepilog macro procname,flag,parmbytes,localbytes,regs,macroargs:vararg
endm
Is this right for the epilogue??
Also, what are the arguments supposed to be (
and their sizes - I only see 'word' for the first param???), what are they used for? I used generic arguments (1,2,3,4,5,6) just to see how it looks in the disassembly.
Sorry, it isn't working code, just an example to assemble and see the resulting .list file.
I'll post an actual working example when I get home from work, but you can see the problem even here.
Quote from: sinsi on October 18, 2024, 09:21:20 AMI'll post an actual working example when I get home from work, but you can see the problem even here.
okay :smiley:
testprolog macro procname,flag,parmbytes,localbytes,regs,macroargs:vararg
push rbp
mov rbp,rsp
mov eax,parmbytes
exitm <localbytes>
endm
testepilog macro procname,flag,parmbytes,localbytes,regs,macroargs:vararg
pop rbp
ret
endm
.code
option prologue:testprolog
option epilogue:testepilog
start proc public
;The custom prologue macro runs here
;It loads parmbytes from ML into EAX
;EAX should load 0, since there are no parameters to this proc
ret
start endp
end
The only thing you might need to change is the entry point name.
eax returns the following when the moving the following args to eax. Notice the pattern...
testprolog macro procname,flag,parmbytes,localbytes,regs,macroargs:vararg
push rsp
mov rbp,rsp
mov eax, flag ;;; eax returns "10h"
mov eax, parmbytes ;;; eax returns "8"
mov eax, localbytes ;;; eax returns "0"
; mov eax, regs ;;; chokes on assembly couldnt test it
; mov eax, macroargs ;;; chokes on assembly couldnt test it
exitm <localbytes>
endm
The last two would return negative values if the pattern would continue. Not sure what this means, but only my observation. :cool:
But those values depend on how you invoke the macro. What arguments did you invoke it with?
Quote from: NoCforMe on October 18, 2024, 12:37:34 PMBut those values depend on how you invoke the macro. What arguments did you invoke it with?
No invoke. The macros are called upon startup, since 'start' is a procedure.
It seems to be enough that the testprolog and testepilog macros are decleared as "prologue" and "epilogue", in place of the deafult prologue and epilogue.
So in other words they're being invoked without any arguments, correct? So whatever values you see there are defaults. (Shouldn't they be zero then, though?)
Quote from: zedd151 on October 18, 2024, 12:32:02 PMeax returns the following when the moving the following args to eax. Notice the pattern...
testprolog macro procname,flag,parmbytes,localbytes,regs,macroargs:vararg
push rsp
mov rbp,rsp
mov eax, flag ;;; eax returns "10h"
mov eax, parmbytes ;;; eax returns "8"
mov eax, localbytes ;;; eax returns "0"
; mov eax, regs ;;; chokes on assembly couldnt test it
; mov eax, macroargs ;;; chokes on assembly couldnt test it
exitm <localbytes>
endm
The last two would return negative values if the pattern would continue. Not sure what this means, but only my observation. :cool:
"flag" is a bitfield
Quote from: MASM ReferenceBit 0, 1, 2 For calling conventions (000=unspecified language type, 001=C, 010=SYSCALL, 011=STDCALL, 100=PASCAL, 101=FORTRAN, 110=BASIC).
Bit 3 Undefined (not necessarily zero).
Bit 4 Set if the caller restores the stack (use RET, not RETn).
Bit 5 Set if procedure is FAR.
Bit 6 Set if procedure is PRIVATE.
Bit 7 Set if procedure is EXPORT.
Bit 8 Set if the epilogue is generated as a result of an IRET
Bits 9–15 Undefined (not necessarily zero).
"parmbytes" is the problem
"localbytes" is correct (there are no locals)
Add this code to the end of yours and check the values
testproc proc private arg1:dword
local local1:dword
lea rax,arg1
lea rax,local1
ret
testproc endp
Quote from: NoCforMe on October 18, 2024, 12:56:33 PMSo in other words they're being invoked without any arguments, correct? So whatever values you see there are defaults. (Shouldn't they be zero then, though?)
The macros are used by ML64,
testprolog is called when ML64 sees
PROC and
testepilog when ML64 sees
RET. ML64 sets the macro parameters/arguments.
Thanks sinsi, as I have no clue how that all works otherwise.
I'll run the next test in a little while, when I'm back at my computer. I'm on the porch at the moment on my iPad.
Quote from: sinsi on October 18, 2024, 01:04:33 PMAdd this code to the end of yours and check the values
testproc proc private arg1:dword
local local1:dword
lea rax,arg1
lea rax,local1
ret
testproc endp
Results from x64dbg...
000000013FFE1000 | 54 | push rsp ; start proc
000000013FFE1001 | 48:8BEC | mov rbp,rsp
000000013FFE1004 | B8 10000000 | mov eax,10
000000013FFE1009 | B8 08000000 | mov eax,8
000000013FFE100E | B8 00000000 | mov eax,0
000000013FFE1013 | 5D | pop rbp
000000013FFE1014 | C3 | ret
000000013FFE1015 | 54 | push rsp ; testproc
000000013FFE1016 | 48:8BEC | mov rbp,rsp
000000013FFE1019 | B8 50000000 | mov eax,50
000000013FFE101E | B8 10000000 | mov eax,10
000000013FFE1023 | B8 08000000 | mov eax,8
000000013FFE1028 | 48:8D45 10 | lea rax,qword ptr ss:[rbp+10]
000000013FFE102C | 48:8D45 FC | lea rax,qword ptr ss:[rbp-4]
000000013FFE1030 | 5D | pop rbp
000000013FFE1031 | C3 | ret
Looking through hutchs macros, I found this:
AltStackFrame MACRO procname, flag, argbytes, localbytes, reglist, userparms:VARARG
LOCAL num, var, alt, argb, algn
argb = argbytes
argb = (argb / 8) - 1 ;; <------ Here
Notice the "- 1". That seems to be subtracting one for some reason (argbytes 8 bytes too long???). Maybe for the same problem that you are finding.
Anyway, this stuff is obviously way 'over my head'. But I am willing to test your code when you have perfected these macros. :smiley:
I am more used to the much simpler macros that simply echo the code (in a macro) when a macro is invoked.
Quote from: zedd151 on October 18, 2024, 01:32:16 PMMaybe for the same problem that you are finding.
Hah, exactly the same problem.
Well, I'll hard code the "fix" and hope that Microsoft keep ignoring it :rolleyes:
For anyone following, I have edited some code here (https://masm32.com/board/index.php?msg=134152).
The original code had push rsp when it should have been push rbp.
Wow. No idea how that happened...
istockphoto-458700325-612x612.jpg
Hmm; is that (the bottle) the problem or the solution?
Or both?
(https://imgs.xkcd.com/comics/ballmer_peak.png)
Not to prolong this topic excursion too much longer, but it looks like someone took that concept a little too seriously (https://ballmerpeakdistillery.com/).
[last post on this tangent, I promise]
Goddamn, these guys actually did a controlled study on the Ballmer Peak (https://arxiv.org/abs/2404.10002)!
Quote from: sinsi on October 18, 2024, 04:17:47 PMFor anyone following, I have edited some code here (https://masm32.com/board/index.php?msg=134152).
The original code had push rsp when it should have been push rbp.
Wow. No idea how that happened...
:badgrin:
That's ok. I never tried to run it, just opened the exe in my debugger for visual inspection of the output. :biggrin:
Quote from: zedd151 on October 18, 2024, 12:32:02 PMeax returns the following when the moving the following args to eax. Notice the pattern...
testprolog macro procname,flag,parmbytes,localbytes,regs,macroargs:vararg
push rsp
mov rbp,rsp
mov eax, flag ;;; eax returns "10h"
mov eax, parmbytes ;;; eax returns "8"
mov eax, localbytes ;;; eax returns "0"
; mov eax, regs ;;; chokes on assembly couldnt test it
; mov eax, macroargs ;;; chokes on assembly couldnt test it
exitm <localbytes>
endm
The last two would return negative values if the pattern would continue. Not sure what this means, but only my observation. :cool:
Macro arguments are text strings, neither numbers nor addresses nor register names.
testprolog macro procname,flag,parmbytes,localbytes,regs,macroargs:vararg
push rsp
mov rbp,rsp
mov eax, flag ;;; eax returns "10h"
mov eax, parmbytes ;;; eax returns "8"
mov edx, localbytes ;;; edx returns "0"
db '*®s' ;;;
db '*¯oargs' ;;;
exitm <localbytes>
endm
testepilog macro procname,flag,parmbytes,localbytes,regs,macroargs:vararg
mov eax, flag ;;; eax returns "10h"
mov eax, parmbytes ;;; eax returns "8"
mov edx, localbytes ;;; edx returns "0"
db '*®s' ;;;
db '*¯oargs' ;;;
pop rbp
ret
endm
test0par2loc2use proc uses rsi rdi
local var1:qword,var2:qword
0000057E 54 1 push rsp
0000057F 48/ 8B EC 1 mov rbp,rsp
00000582 B8 00000010 1 mov eax, 010H
00000587 B8 00000008 1 mov eax, 08H
0000058C BA 00000010 1 mov edx, 010H
00000591 2A 3C 72 73 69 1 db '*<rsi,rdi>'
2C 72 64 69 3E
0000059B 2A 1 db '*'
0000059C 48/ 8B 45 F8
000005A0 48/ 8B 45 F0 mov rax,var1
mov rax,var2
000005A4 B8 00000010 1 mov eax, 010H
000005A9 B8 00000008 1 mov eax, 08H
000005AE BA 00000010 1 mov edx, 010H
000005B3 2A 3C 72 64 69 1 db '*<rdi,rsi>'
2C 72 73 69 3E
000005BD 2A 1 db '*'
000005BE 5D 1 pop rbp
000005BF C3 1 ret
000005C0 ret
test0par2loc2use endp
The regs argument is a text/character string which contains a list of register names separated by commas and enclosed by angle brackets, < >. This makes it easy to insert the register list into an IRP directive. The list sent to the epilog macro is a reversal of the list sent to the prolog macro, providing easy matching of PUSHes and POPs.
proctest.zip