News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Is this AI response (GitHub Copilot) regarding the Win64 ABI correct?

Started by _japheth, March 16, 2025, 07:36:53 PM

Previous topic - Next topic

_japheth

My question, inspired by https://masm32.com/board/index.php?topic=12621.0, was:

In a Win64 leaf function, is it allowed to define stack variables and still be SEH compliant?

and was answered with:

------------------------------------------------------------------------------------------------------
In a Win64 leaf function, it is allowed to define stack variables and still be SEH (Structured Exception Handling) compliant. However, you must adhere to certain constraints to ensure compliance with SEH. Specifically, you need to ensure that the function's prologue and epilogue follow the conventions expected by the SEH system.

Here are some key points to consider:

  • Prologue and Epilogue: Ensure that the prologue and epilogue of the function are correctly set up to save and restore non-volatile registers, including the stack pointer.
  • Stack Alignment: Maintain proper stack alignment as required by the Windows 64-bit ABI (Application Binary Interface), which typically requires a 16-byte alignment.
  • Frame Pointer Omission: Even though leaf functions can omit the frame pointer, if you are using stack variables, you might need to use the frame pointer (RBP) to reference these variables safely.
  • Exception Handlers: If the function needs to participate in SEH, it should correctly register and unregister exception handlers.
By following these guidelines, you can define stack variables in a Win64 leaf function while remaining compliant with SEH requirements.[/i]
------------------------------------------------------------------------------------------------------

My knowledge so far was:
  • a leaf function cannot modify non-volatile registers if SEH compliant
  • in a leaf function, the return address to the caller must be at [rsp+0]. So no rbp stack frame is possible

As a consequence of those restrictions, I did assume that in a leaf function local variables, if possible at all, had to be addressed with RSP and a negative displacement.
Dummheit, gepaart mit Dreistigkeit - eine furchtbare Macht.

zedd151

I wouldn't trust AI with any type of answers to code related questions. You must check and verify.  :smiley:

I have done my fair share of experimentation with asking several AI models for both assembly code and C code. During my experiments, I found only around 25% of the code generated produced code that would compile/assemble without errors, run without errors, and importantly produced accurate results.

Just some food for thought about AI. I don't think that Github Copilot would be much different. You must verify for accuracy.  :smiley:  The AI models I have used clearly state as a disclaimer, a sentence to the affect of "Results may contain errors, or mistake, etc." something of that nature.

The above are my opinions about AI, after having tried several AI models.
More specific to your question, I don't know of anyone here that even uses SEH in their code.
¯\_(ツ)_/¯   :azn:

'As we don't do "requests", show us your code first.'  -  hutch—

six_L

Q:
QuoteIn a Win64 leaf function, is it allowed to define stack variables and still be SEH compliant?
A:(from DeepSeek)
QuoteYes, in a Win64 leaf function (a function that does not call other functions), it is allowed to define stack variables and still be SEH-compliant (Structured Exception Handling compliant). However, there are specific requirements to ensure SEH compatibility. Here's a detailed explanation:
1. What is a Leaf Function?
A leaf function is a function that does not call any other functions. Because it does not call other functions, it typically does not need to:
    Save non-volatile registers (e.g., rbx, rsi, rdi).
    Allocate a call stack frame (e.g., shadow space for function calls).
2. Requirements for SEH Compliance in Win64
To support SEH in Win64, a function must meet the following requirements:
    Stack Alignment: The stack pointer (rsp) must remain 16-byte aligned at all times.
    Stack Frame Chain: If the function might trigger an exception, it must establish a stack frame chain (via mechanisms like RtlUnwind or RtlLookupFunctionEntry).
3. Defining Stack Variables in a Leaf Function
Defining stack variables in a leaf function is allowed, but you must ensure:
    Stack Alignment: When allocating stack space, ensure rsp remains 16-byte aligned.
    Stack Frame Chain: If the function might trigger an exception, you must explicitly establish a stack frame chain (even if it does not call other functions).
4. Example Code
Here's an example of a leaf function that defines stack variables while remaining SEH-compliant:
leaf_function PROC
    ; Save non-volatile registers (optional)
    push    rbx
    push    rsi
    push    rdi

    ; Allocate stack space (maintain 16-byte alignment)
    sub     rsp, 32  ; Allocate 32 bytes of stack space (16-byte aligned)

    ; Define stack variables
    mov     DWORD PTR [rsp + 16], 1234  ; Example variable

    ; Free stack space
    add     rsp, 32

    ; Restore non-volatile registers
    pop     rdi
    pop     rsi
    pop     rbx
    ; Return
    ret
leaf_function ENDP
5. Ensuring SEH Compliance
To ensure SEH compliance:
    Establish a Stack Frame Chain: If the function might trigger an exception, use .seh_prolog and .seh_epilog directives to establish a stack frame chain.
    Maintain Stack Alignment: Ensure rsp remains 16-byte aligned at all times.
6. Key Points
    Defining stack variables in a Win64 leaf function is allowed.
    To be SEH-compliant, ensure stack alignment and establish a stack frame chain if necessary.
    If the function does not involve exception handling, you can omit the stack frame chain setup.
Say you, Say me, Say the codes together for ever.

_japheth

Quote from: six_L on March 17, 2025, 01:51:42 AM1. What is a Leaf Function?
A leaf function is a function that does not call any other functions.

That's probably the crucial point. It's somewhat intuitive to agree to the definition supplied by the AI, but according to MS the definition is as follows:

Leaf functions are functions that don't change any non-volatile registers.

And only in the context of this latter definition the problem touched by my original question becomes clear (because RSP is a non-volatile register).
Dummheit, gepaart mit Dreistigkeit - eine furchtbare Macht.

jj2007

Quote from: _japheth on March 17, 2025, 02:55:52 AMLeaf functions are functions that don't change any non-volatile registers.

Raymond Chen: But those rules are relaxed for leaf functions (functions which call no other functions).

tenkey

That article is about the Itanium, which is not an x86 CPU. It is possible that a call (whatever mnemonic it uses) instruction stores the return address in a register. There would not be any change to a stack pointer.

AFAIK there is no red zone defined by the Win64 ABI. So I would not store anything below RSP.

What I do know is that RSP must not be changed by the leaf function. The leaf function must NOT align RSP to a 16-byte boundary.

_japheth

Quote from: jj2007 on March 17, 2025, 09:05:11 AMRaymond Chen: But those rules are relaxed for leaf functions (functions which call no other functions).

Raymond Chen is definitely your Guru  :biggrin: Well, why not, he's very knowledgeable - but even he might sometimes simplify things not to overstrain his readers.

If you'd bother to make yourself familiar with Win64 SEH, you'll realize that the SEH code has a lot to do if an exception occurs (may forget some details, my Windows knowledge has become a bit rusty ):

  • walk the stack for the chain of currently "active" functions.
  • each function may have installed one or more exception handlers.
  • the exception handlers may decide at runtime whether they handle the exception or not.
  • if not, there still may be some "cleanup" code to be called (to free temp memory and such).
  • if a function does not handle the exception, SEH has to restore all non-volatile registers and walk up the stack to the calling function. Btw, that's why all those strange pseudo-directives like .PUSHREG, .SAVEREG, .ENDPROLOG exist
  • for the latter to work, the system must know where the caller's return address has been stored. This is at [RSP+0] AFTER that non-volatile register has been restored.

That's why, if I may quote myself, wrote:

in a leaf function, the return address to the caller must be at [rsp+0]. So no rbp stack frame is possible
Dummheit, gepaart mit Dreistigkeit - eine furchtbare Macht.

_japheth


This YASM site gives a rather good overview about the differences between "leaf" and "frame" functions.
Also, it describes in detail what's required if you want to dynamically alloc stack space ( _alloca() in C ) in a SEH-safe way.
Dummheit, gepaart mit Dreistigkeit - eine furchtbare Macht.

tenkey

The YASM document is much better. It is consistent with the parts I have figured out.
This includes:

* The position of saved nonvolatile registers.
    I'm displeased with UASM's auto-placement of "used" 64-bit registers
    in the stack.
    In its current implementation (ver. 2.57), it can unalign the stack.
* The difference between presence and absence of alloca() behavior.
    This is an indicator that a frame pointer (separate from RSP)
    is needed or not.
* The stack alignment of leaf functions.
    My definition of leaf functions (or procedures) depends on context.
    If I'm talking about MS function implementation, it means one thing.
    If I'm talking about call structure, it means something else.

tenkey

After my statements mentioning two meanings of leaf functions, it occurred to me that the AI might be referring to my second meaning. I spell it out now.

If leaf function refers to a function in a call structure (info is who calls whom) then a leaf function is a function that doesn't call another function. If that is the case, an MS "leaf function" is really an optimized leaf function (call structure meaning). That is an implementation detail.

The confusion is a term originally defining a concept (a function that doesn't call other functions) being used to define an implementation (a function with implementation restrictions).

The "restrictions" are conditions that compilers can detect to optimize leaf functions (by removing or not generating stack realignment code), because the exception handler system can handle the specific code change.

sinsi

I was looking through the SDK/DDK files and found this little snippet in MACAMD64.INC
; LEAF_ENTRY_ALIGNED <Name>, <Section>, <Alignment>, <NoPad>
;
; Macro Description:
;
;  This macro indicates the beginning of a leaf function with custom alignment.
;
;  A leaf function is one that DOES NOT:
;
;  - manipulate non-volatile registers
;  - manipulate the stack pointer
;  - call other functions
;  - reference an exception handler
;  - contain a prologue
;  - have any unwind data associated with it