You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++中可移植的调用栈参数入栈实现方案问询

Feasible, But Platform-Specific Assembly Is Required

Absolutely doable! The key challenge here is accounting for different calling conventions across your target platforms (Windows, macOS/iOS, Android) and architectures (x86, x86_64, ARM, ARM64), since embedded assembly is inherently tied to how each system expects function arguments to be passed.

Below’s a breakdown of how to implement this with C++ inline assembly, using conditional compilation to target each toolchain:

1. Visual Studio (Windows: x86/x86_64)

Windows uses distinct calling conventions depending on the architecture:

  • 32-bit x86: Most C/C++ functions use cdecl (caller cleans the stack) or stdcall (callee cleans the stack). For pushing arguments to the stack, you can use MSVC’s __asm block:

    // Example: Call int add(int a, int b) using cdecl
    int a = 5, b = 10;
    int result;
    
    __asm {
        push b          // Push rightmost argument first
        push a
        call add        // Call the function
        add esp, 8      // Clean up stack (2 args × 4 bytes) for cdecl
        mov result, eax // Store return value from EAX
    }
    

    For stdcall, you’d omit the add esp, 8 line since the callee handles stack cleanup.

  • 64-bit x86_64: MSVC uses the fastcall convention, where the first 4 integer arguments are passed via registers (rcx, rdx, r8, r9), with remaining args pushed to the stack. Inline assembly support here is limited, so you may want to use a __declspec(naked) function to manually construct the call frame:

    __declspec(naked) int call_add(int a, int b) {
        __asm {
            // Setup args: a in rcx, b in rdx
            mov rcx, [rsp + 8]  // First arg (since return address is at rsp)
            mov rdx, [rsp + 16] // Second arg
            call add
            ret
        }
    }
    

2. Xcode (macOS/iOS: x86_64/ARM64)

Apple’s toolchain uses Clang, which supports GNU-style inline assembly. The calling conventions follow System V standards:

  • x86_64: First 6 integer args go to rdi, rsi, rdx, rcx, r8, r9; remaining args are pushed to the stack (right-to-left). Example:

    int a = 5, b = 10;
    int result;
    
    asm volatile (
        "pushq %%rdx\n\t"    // Push b (stored in rdx)
        "pushq %%rdi\n\t"    // Push a (stored in rdi)
        "call _add\n\t"      // Call the function (leading underscore is legacy but safe)
        "addq $16, %%rsp\n"  // Clean up stack
        : "=a"(result)       // Return value in rax -> result
        : "D"(a), "d"(b)     // Inputs: a in rdi, b in rdx
        : "rsp", "memory"    // Clobbered registers/memory
    );
    
  • ARM64 (Apple Silicon): Uses the AArch64 calling convention, where the first 8 integer args go to x0–x7, with extra args pushed to the stack. Stack must stay 16-byte aligned:

    int a = 5, b = 10;
    int result;
    
    asm volatile (
        "str %1, [sp, #-16]!\n\t"  // Push b to stack, adjust sp (16-byte align)
        "str %0, [sp, #8]\n\t"     // Push a to stack (offset 8)
        "bl add\n\t"               // Call the function
        "add sp, sp, #16\n"        // Restore stack
        : "=r"(result)             // Return value in x0 -> result
        : "r"(b), "r"(a)           // Inputs: b and a in general registers
        : "sp", "memory"           // Clobbered stack and memory
    );
    

3. Android Studio (ARM/ARM64/x86/x86_64)

Android uses Clang and follows Linux-style calling conventions, which align closely with Apple’s:

  • ARM 32-bit: Uses the APCS convention, where args are pushed right-to-left, with the first 4 args optionally in registers (r0–r3). For stack-based passing:
    int a = 5, b = 10;
    int result;
    
    asm volatile (
        "push {r1}\n\t"    // Push b (in r1)
        "push {r0}\n\t"    // Push a (in r0)
        "bl add\n\t"       // Call function
        "add sp, sp, #8\n" // Clean stack
        : "=r"(result)     // Return in r0 -> result
        : "r"(a), "r"(b)
        : "sp", "memory"
    );
    
  • ARM64/x86/x86_64: Use the same patterns as the Xcode ARM64/x86_64 examples—just omit the leading underscore on function names (Android doesn’t use this).

Critical Considerations

  • Conditional Compilation: Wrap each platform/architecture-specific block in preprocessor directives like #ifdef _WIN32, #ifdef __APPLE__, #ifdef __ANDROID__, #ifdef __x86_64__, and #ifdef __arm64__ to ensure the right code is compiled for each toolchain.
  • Stack Alignment: All modern architectures require strict stack alignment (16 bytes for x86_64/ARM64, 4 bytes for x86/ARM 32-bit). Failing to align the stack before pushing args will cause crashes.
  • Compiler Optimizations: Use asm volatile to prevent the compiler from optimizing away your assembly block, and explicitly list clobbered registers/memory to avoid conflicts.
  • Alternative (If Assembly Gets Too Cumbersome): If maintaining inline assembly across all platforms becomes a headache, you could use libffi (a portable foreign function interface library) to handle argument pushing dynamically. But since you prioritized inline assembly, this is just a backup option.

In short: Yes, it’s feasible. You’ll just need to write architecture/platform-specific inline assembly snippets and wrap them in conditional compilation to support Visual Studio, Xcode, and Android Studio.

内容的提问来源于stack exchange,提问作者Raptisoft

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 03:47:53