The MASM Forum

General => The Workshop => Topic started by: sinsi on October 18, 2024, 02:04:52 AM

Title: Custom prologue
Post by: sinsi on October 18, 2024, 02:04:52 AM
To use a custom prologue we need a macro
testprolog macro procname,flag,parmbytes,localbytes,regs,macroargs:varargWith 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?
Title: Re: Custom prologue
Post by: zedd151 on October 18, 2024, 04:24:01 AM
Do you have a test example of its usage?
Title: Re: Custom prologue
Post by: Vortex on October 18, 2024, 04:27:34 AM
Stack alignment to 16 bytes?
Title: Re: Custom prologue
Post by: sinsi on October 18, 2024, 04:36:37 AM
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).
Title: Re: Custom prologue
Post by: sinsi on October 18, 2024, 04:40:05 AM
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:
Title: Re: Custom prologue
Post by: zedd151 on October 18, 2024, 04:52:31 AM
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...
Title: Re: Custom prologue
Post by: zedd151 on October 18, 2024, 04:58:24 AM
I was originally missing one argument in the call.... I fixed the above example.   :rolleyes:
Title: Re: Custom prologue
Post by: zedd151 on October 18, 2024, 07:11:03 AM
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.
Title: Re: Custom prologue
Post by: sinsi on October 18, 2024, 09:21:20 AM
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.
Title: Re: Custom prologue
Post by: zedd151 on October 18, 2024, 09:34:36 AM
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:
Title: Re: Custom prologue
Post by: sinsi on October 18, 2024, 10:20:46 AM
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.
Title: Re: Custom prologue
Post by: zedd151 on October 18, 2024, 12:32:02 PM
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:
Title: Re: Custom prologue
Post by: NoCforMe on October 18, 2024, 12:37:34 PM
But those values depend on how you invoke the macro. What arguments did you invoke it with?
Title: Re: Custom prologue
Post by: zedd151 on October 18, 2024, 12:39:28 PM
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.
Title: Re: Custom prologue
Post by: NoCforMe on October 18, 2024, 12:56:33 PM
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?)
Title: Re: Custom prologue
Post by: sinsi on October 18, 2024, 01:04:33 PM
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.
Title: Re: Custom prologue
Post by: zedd151 on October 18, 2024, 01:08:51 PM
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.
Title: Re: Custom prologue
Post by: zedd151 on October 18, 2024, 01:17:56 PM
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
Title: Re: Custom prologue
Post by: zedd151 on October 18, 2024, 01:32:16 PM
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.
Title: Re: Custom prologue
Post by: sinsi on October 18, 2024, 02:19:31 PM
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:
Title: Re: Custom prologue
Post by: sinsi on October 18, 2024, 04:17:47 PM
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
Title: Re: Custom prologue
Post by: NoCforMe on October 18, 2024, 05:15:30 PM
Hmm; is that (the bottle) the problem or the solution?
Or both?
Title: Re: Custom prologue
Post by: sinsi on October 18, 2024, 05:26:26 PM
(https://imgs.xkcd.com/comics/ballmer_peak.png)
Title: Re: Custom prologue
Post by: NoCforMe on October 18, 2024, 06:12:42 PM
Not to prolong this topic excursion too much longer, but it looks like someone took that concept a little too seriously (https://ballmerpeakdistillery.com/).
Title: Re: Custom prologue
Post by: NoCforMe on October 18, 2024, 06:43:25 PM
[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)!
Title: Re: Custom prologue
Post by: zedd151 on October 18, 2024, 07:19:46 PM
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:
Title: Re: Custom prologue
Post by: tenkey on October 21, 2024, 05:10:57 AM
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      '*&regs'              ;;;
    db      '*&macroargs'         ;;;
   
    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      '*&regs'              ;;;
    db      '*&macroargs'         ;;;

    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