Skip to content

Clang on macOS does not emit stack-passed arguments when calling __attribute__((ms_abi)) functions from System V callers #144164

Closed
@Inakito

Description

@Inakito

Description:

When compiling with Apple Clang 17.0.0 on macOS, calls from System V ABI functions (e.g., main) to functions marked with attribute((ms_abi)) do not correctly follow the Microsoft x64 ABI. Specifically, arguments that exceed the first four (passed in RCX, RDX, R8, R9) are not passed at all — they are not placed on the stack as required.

This causes serious runtime issues in ms_abi variadic functions which expect those parameters.

Environment:

  • macOS 14.x (Apple Silicon or Intel, both reproducible)
  • Apple Clang 17.0.0 (Xcode 15.3)
  • Target: x86_64 (System V by default)
  • Tested with -O0 and -O1

Reproduction Case:

#include <stdio.h>

__attribute__((ms_abi)) void systemv_printf(const char *fmt, ...);

int main() {
    double f1 = 1.1, f2 = 2.2, f3 = 3.3, f4 = 4.4, f5 = 5.5;

    // Only the first 4 arguments are passed
    systemv_printf("%g %g %g %g %g\n", f1, f2, f3, f4, f5);
    return 0;
}

Implementation of systemv_printf attempts to read the 5th argument from the stack using frame pointer offset — but it's missing.

Expected behavior:

According to the Microsoft x64 ABI specification:

  • First four arguments → in RCX, RDX, R8, R9
  • Floating-point args duplicated into XMM0–XMM3
  • Additional arguments → placed on the stack, 8-byte aligned
  • Callee uses va_start or manual stack access to retrieve extra args

Actual behavior:

Clang emits code for the ms_abi call with only the first 4 arguments passed in registers. The 5th and beyond are not passed at all — no stack space is used, and the values are never pushed or stored.

Disassembly confirms this — even with volatile variables and -O0:

; systemv_printf("%g %g %g %g %g\n", f1, f2, f3, f4, f5);

leaq   ... -> %rcx    ; format string
movq   ... -> %rdx    ; f1
movq   ... -> %r8     ; f2
movq   ... -> %r9     ; f3
; NO instruction for f4 or f5 (e.g. mov to [rsp + N])
callq  systemv_printf

Control Test (ms_abi → ms_abi):

When both caller and callee are marked attribute((ms_abi)), all arguments including the 5th float are properly passed — the last argument is correctly copied to [rsp+0x20] and accessible.

This suggests that Clang fails to emit proper Microsoft ABI code only when the caller is System V.

Impact:

This breaks interoperability between System V code (the default on macOS) and ms_abi functions — especially for printf-like variadic trampolines or ABI bridging layers, which depend on full argument delivery.

Suggested Fix:

Ensure that when a System V function calls an attribute((ms_abi)) function:

  • The Microsoft ABI rules are fully applied
  • Additional arguments beyond the 4th are pushed on the stack

Thank you!

Let me know if you'd like a full reproducer or test case archive.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions