Skip to content

[Linux/x86] GenericPInvokeCalliHelper problem #58191

Open
@t-mustafin

Description

@t-mustafin

GenericPInvokeCalliHelper produces a problem on linux.

GenericPInvokeCalliHelper takes away VASigCookie argument from the stack and moves on its place return address value. It breaks 16 byte stack alignment and leads to further problems on esp usage: execution could break inside CHECK_STACK_ALIGNMENT macro or on movap [esp+offset] instruction. The problem spreads until caller of GenericPInvokeCalliHelper will restore esp from ebp on self epilog.

// ==========================================================================
// Invoked for marshaling-required unmanaged CALLI calls as a stub.
// EAX - the unmanaged target
// ECX, EDX - arguments
// [ESP + 4] - the VASigCookie
//
LEAF_ENTRY GenericPInvokeCalliHelper, _TEXT
// save the target
push eax
// EAX <- VASigCookie
mov eax, [esp + 8] // skip target and retaddr
mov eax, [eax + VASigCookie__StubOffset]
test eax, eax
jz LOCAL_LABEL(GoCallCalliWorker)
// ---------------------------------------
push eax
// stack layout at this point:
//
// | ... |
// | stack arguments | ESP + 16
// +----------------------+
// | VASigCookie* | ESP + 12
// +----------------------+
// | return address | ESP + 8
// +----------------------+
// | CALLI target address | ESP + 4
// +----------------------+
// | stub entry point | ESP + 0
// ------------------------
// remove VASigCookie from the stack
mov eax, [esp + 8]
mov [esp + 12], eax
// move stub entry point below the RA
mov eax, [esp]
mov [esp + 8], eax
// load EAX with the target address
pop eax
pop eax
// stack layout at this point:
//
// | ... |
// | stack arguments | ESP + 8
// +----------------------+
// | return address | ESP + 4
// +----------------------+
// | stub entry point | ESP + 0
// ------------------------
// CALLI target address is in EAX
ret

I see couple of options to resolve the problem:

  1. Besides VASigCookie GenericPInvokeCalliHelper takes the unmanaged target argument via eax register. Caller could create a structure {the unmanaged target; VASigCookie} and pass a pointer to the structure via eax. GenericPInvokeCalliHelper able to read the structure and place the unmanaged target in eax to the moment of IL_STUB_PIvoke call. So this option changes only caller->GenericPInvokeCalliHelper interface, GenericPInvokeCalliHelper->IL_STUB_PIvoke interface remains unchanged.
  2. Change all caller->IL_STUB_PIvoke calling convention to use only stack instead of ecx + edx + stack to pass arguments. It will free ecx register which could be used during caller->GenericPInvokeCalliHelper to keep VASigCookie value.

@jkotas Will such changes break some code outside of GenericPInvokeCalliHelper case? Which variant is preferable?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    No status

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions