c++ 调用汇编函数

默认分类 · 2024-12-06 · 31 人浏览

使用汇编可以直接控制 CPU 指令, 使用 c++ 可以调用到汇编代码, 这里记录一下调用汇编的流程
以一个汇编实现的swap 函数为例

  1. 首先在 c++ 层我们需要有一个函数声明

    extern "C" {
     extern 返回类型 函数名(参数列表) asm("汇编符号名");
    };

    示例
    extern "C" void asm_swap(void *a, void *b) asm("asm_swap");
    这里要注意几点

    • extern "C" 要防止 c++ 编译器进行名称修饰,C++ 的名称修饰会导致链接失败
    • 函数名与 asm() 内 汇编符号一致
    • asm() 内代表汇编符号名, 外部汇编函数必须与声明保持一致
  2. 添加一个汇编文件
    示例 :
    swap.S

    .global asm_swap # 声明 swap 为全局符号,使得该符号可以被其他文件或模块引用。
    asm_swap: # 表明后续的指令是该函数的实现。
     movq (%rdi), %rax # 实现 swap 逻辑, movq 是移动 8 个字节
     movq (%rsi), %rbx
     movq %rax, (%rsi)
     movq %rbx, (%rdi)
     ret # 它将控制权返回给调用者

    同样有要注意的点

    • 汇编代码要符合 调用约定 (Calling Convention):定义了函数如何传递参数、如何返回值、如何保存和恢复寄存器等细节

    调用约定在不同的平台和操作系统中通常是不同的,

以下是一些常见平台的调用约定:(来自 ChatGPT, 注意辨别)

  1. x86-64 System V ABI (Linux/macOS) 参数传递:前 6 个整数/指针类型参数通过寄存器传递: %rdi:第 1 个参数 %rsi:第 2 个参数 %rdx:第 3 个参数 %rcx:第 4 个参数 %r8:第 5 个参数 %r9:第 6 个参数 额外参数通过栈传递。 返回值:通过 %rax 寄存器返回。 对齐:栈按 16 字节对齐。
  2. Windows x64 ABI 参数传递:前 4 个整数/指针类型参数通过寄存器传递: %rcx:第 1 个参数 %rdx:第 2 个参数 %r8:第 3 个参数 %r9:第 4 个参数 额外参数通过栈传递。 返回值:通过 %rax 返回(整数类型),浮点值通过 %xmm0 返回。 对齐:栈按 16 字节对齐。
  3. x86 (32-bit) cdecl (C 默认调用约定) 参数传递:所有参数通过栈传递。 返回值:通过 %eax 返回。 栈清理:调用者负责清理栈。
  4. x86 (32-bit) stdcall (Windows API 默认调用约定) 参数传递:所有参数通过栈传递。 返回值:通过 %eax 返回。 栈清理:被调用者负责清理栈。
  5. ARM 32-bit 参数传递:前 4 个整数参数通过寄存器 %r0 到 %r3 传递,额外的通过栈传递。 返回值:通过 %r0 返回。 栈对齐:通常按 8 字节对齐。
  6. ARM 64-bit (AArch64) 参数传递:前 8 个参数通过寄存器 %x0 到 %x7 传递,额外的通过栈传递。 返回值:通过 %x0 返回。 栈对齐:按 16 字节对齐。
  1. 编译链接
    汇编文件编译为 .o 文件 与其他 .o 文件链接即可 示例: clang++ -c swap.S -o swap.o
Theme Jasmine by Kent Liao