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

如何将C++的KeysCpp函数转为x86_64汇编?栈溢出问题排查

问题描述

需要将C中的KeysCpp函数转换为x86_64汇编实现KeysAsm,并在原C文件中调用,保留crim的C实现。以下是原C代码:

#include <iostream>
using namespace std;

extern "C" void KeysAsm(int arr[], int n, int thetha, int rho);

// Keep this and call it from assembler
extern "C"
void crim(int *xp, int *yp) {
    int temp = *xp;
    *xp = *yp;
    *yp = temp+2;
}

// Translate this into Intel assembler
void KeysCpp(int arr[], int n, int thetha, int rho){
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                crim(&arr[j], &arr[j + 1]);
            }
        }
        arr[i]= arr[i] + thetha / rho * 2 - 4;
    }
}

// Function to print an array 
void printArray(int arr[], int size){
    int i;
    for (i = 0; i < size; i++)
        cout << arr[i] << "\n";
    cout << endl;
}


int main() {
    int gamma1[]{
        9,
        270,
        88,
        -12,
        456,
        80,
        45,
        123,
        427,
        999
    };

    int gamma2[]{
        900,
        312,
        542,
        234,
        234,
        1,
        566,
        123,
        427,
        111
    };

    printf("Array:\n");
    printArray(gamma1, 10);

    KeysAsm(gamma1, 10, 5, 6);
    printf("Array Result Asm:\n");
    printArray(gamma1, 10);

    KeysCpp(gamma2, 10, 5, 6);
    printf("Array Result Cpp:\n");
    printArray(gamma2, 10);
}

编写的汇编文件如下:

PUBLIC KeysAsm

includelib kernel32.lib

_DATA   SEGMENT
EXTERN crim:PROC
_DATA ENDS

_TEXT   SEGMENT

KeysAsm PROC
    push    rbp
    mov     rbp, rsp
    sub     rsp, 40
    mov     QWORD PTR [rbp-24], rdi
    mov     DWORD PTR [rbp-28], esi
    mov     DWORD PTR [rbp-32], edx
    mov     DWORD PTR [rbp-36], ecx
    mov     DWORD PTR [rbp-4], 0
    jmp      L3

 L3:
    mov     eax, DWORD PTR [rbp-28]
    sub     eax, 1
    cmp     DWORD PTR [rbp-4], eax
    jl       L7

 L4:
    mov     eax, DWORD PTR [rbp-28]
    sub     eax, DWORD PTR [rbp-4]
    sub     eax, 1
    cmp     DWORD PTR [rbp-8], eax
    jl       L6

 L5:
    add     DWORD PTR [rbp-8], 1

 L6:
    mov     eax, DWORD PTR [rbp-8]
    cdqe
    lea     rdx, [0+rax*4]
    mov     rax, QWORD PTR [rbp-24]
    add     rax, rdx
    mov     edx, DWORD PTR [rax]
    mov     eax, DWORD PTR [rbp-8]
    cdqe
    add     rax, 1
    lea     rcx, [0+rax*4]
    mov     rax, QWORD PTR [rbp-24]
    add     rax, rcx
    mov     eax, DWORD PTR [rax]
    cmp     edx, eax
    jle      L5
    mov     eax, DWORD PTR [rbp-8]
    cdqe
    add     rax, 1
    lea     rdx, [0+rax*4]
    mov     rax, QWORD PTR [rbp-24]
    add     rdx, rax
    mov     eax, DWORD PTR [rbp-8]
    cdqe
    lea     rcx, [0+rax*4]
    mov     rax, QWORD PTR [rbp-24]
    add     rax, rcx
    mov     rsi, rdx
    mov     rdi, rax
    call    crim

 L7:
    mov     DWORD PTR [rbp-8], 0
    jmp      L4

KeysAsm ENDP

_TEXT   ENDS



END

运行时出现错误:

Unhandled exception at 0x00007FF74B0E429C in MatrixMultiplication.exe: Stack cookie instrumentation code detected a stack-based buffer overrun. occurred

问题排查与修复

错误原因分析

  1. 调用约定不符合MSVC规范:x86_64 MSVC调用约定中,函数前两个参数通过rcx、rdx传递,但原汇编调用crim时用了rsi、rdi,导致参数传递错误。
  2. 栈空间分配不足且未对齐:MSVC要求栈保持16字节对齐,原代码sub rsp,40后栈未满足对齐要求,且局部变量和寄存器保存的空间计算错误,引发栈溢出。
  3. 循环逻辑缺失且跳转混乱:原汇编未实现外层循环中arr[i] = arr[i] + thetha / rho *2 -4的核心逻辑,同时循环跳转逻辑错误,导致循环无法正常终止,越界访问内存。
  4. 寄存器未正确恢复:原代码保存了非易失性寄存器rdi,但未在函数结束时恢复,破坏了调用者的上下文。

修正后的汇编代码

PUBLIC KeysAsm

_DATA   SEGMENT
EXTERN crim:PROC
_DATA ENDS

_TEXT   SEGMENT

; MSVC x86_64调用约定:rcx=arr, rdx=n, r8=thetha, r9=rho
KeysAsm PROC
    push    rbp
    mov     rbp, rsp
    sub     rsp, 32             ; 分配栈空间,满足16字节对齐,用于保存寄存器和局部变量
    mov     QWORD PTR [rbp-8], rbx  ; 保存非易失性寄存器rbx
    mov     QWORD PTR [rbp-16], rsi ; 保存非易失性寄存器rsi
    mov     QWORD PTR [rbp-24], rdi ; 保存非易失性寄存器rdi

    mov     rsi, rcx            ; rsi = arr(数组指针)
    mov     ecx, edx            ; ecx = n
    mov     r8d, r8d            ; r8d = thetha
    mov     r9d, r9d            ; r9d = rho

    mov     ebx, 0              ; ebx = i(外层循环计数器)
outer_loop:
    cmp     ebx, ecx
    jge     outer_exit          ; 如果i >=n,退出外层循环

    mov     edx, ecx
    sub     edx, ebx
    sub     edx, 1              ; edx = n - i -1
    mov     edi, 0              ; edi = j(内层循环计数器)
inner_loop:
    cmp     edi, edx
    jge     inner_exit          ; 如果j >=n-i-1,退出内层循环

    ; 比较arr[j]和arr[j+1]
    mov     eax, edi
    lea     rcx, [rsi+rax*4]    ; rcx = &arr[j]
    mov     eax, DWORD PTR [rcx]
    lea     rdx, [rsi+rdi*4+4]  ; rdx = &arr[j+1]
    mov     r8d, DWORD PTR [rdx]
    cmp     eax, r8d
    jle     inner_inc           ; 如果arr[j] <= arr[j+1],跳过交换

    ; 调用crim(&arr[j], &arr[j+1])
    call    crim

inner_inc:
    inc     edi
    jmp     inner_loop

inner_exit:
    ; 执行arr[i] = arr[i] + thetha/rho*2 -4
    mov     eax, r8d            ; eax = thetha
    cdq
    idiv    r9d                 ; eax = thetha / rho(整数除法)
    imul    eax, 2              ; eax *=2
    sub     eax,4               ; eax -=4
    lea     rcx, [rsi+rbx*4]    ; rcx = &arr[i]
    add     DWORD PTR [rcx], eax

    inc     ebx
    jmp     outer_loop

outer_exit:
    ; 恢复寄存器
    mov     rbx, QWORD PTR [rbp-8]
    mov     rsi, QWORD PTR [rbp-16]
    mov     rdi, QWORD PTR [rbp-24]
    add     rsp,32
    pop     rbp
    ret
KeysAsm ENDP

_TEXT   ENDS

END

修正说明

  1. 遵循MSVC调用约定:使用rcx、rdx传递crim的两个参数,符合x86_64 MSVC规范。
  2. 栈空间正确分配与对齐:分配32字节栈空间,满足16字节对齐要求,同时保存所有需要保留的非易失性寄存器(rbx、rsi、rdi)。
  3. 补全循环逻辑:实现了外层循环中arr[i]的计算逻辑,同时修正了循环跳转条件,确保循环正常终止。
  4. 寄存器正确恢复:函数结束时恢复所有保存的非易失性寄存器,保证调用者上下文不受破坏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 06:55:26