从x64汇编调用返回std::string的C++函数出现读取访问违规
问题成因
- C ABI 不支持非平凡类型传递:
extern "C"仅遵循C语言的ABI约定,而C++的std::string是带自定义构造、析构、拷贝逻辑的非POD类型,C ABI没有定义这类对象的传递规则。按照微软x64调用约定,返回大于8字节的非平凡对象时,要求调用方提前分配返回值内存,并将该内存指针作为隐藏的第一个参数传入被调用方,你当前的汇编代码完全没有处理这个隐藏参数,直接从rax取到的根本不是合法的string对象地址。 - 对象生命周期管理错误:就算你侥幸拿到了第一次返回的string地址,
inputString中定义的局部string会在函数返回后自动触发析构,对应的字符串内存要么被释放,要么在第二次调用inputString时被复用覆盖,后续访问自然会触发读取违规。 - x64栈对齐不符合约定:微软x64调用约定要求执行
call指令前,rsp必须16字节对齐。你进入asm_main时,栈上已经压了8字节的返回地址,此时rsp为16n+8,仅执行sub rsp,20h后rsp为16n-24=16(n-2)+8,调用函数前没有满足16字节对齐要求,会导致C++函数内部的对齐敏感指令直接崩溃。
修复方案
1. 修改C++接口
将接口调整为符合C ABI的平凡类型传递,避免直接传递std::string对象:
#include <iostream> #include <string> #include <cstdlib> #include <cstring> using namespace std; extern "C" char* inputString() { string strInput; cout << "Enter string input: "; cin >> strInput; // 动态分配内存返回C风格字符串,调用方负责释放 char* res = (char*)malloc(strInput.size() + 1); strcpy_s(res, strInput.size() + 1, strInput.c_str()); return res; } extern "C" int outputStringLen(const char* strInput) { if (!strInput) return 0; return (int)strlen(strInput); } // 用于释放返回的字符串内存 extern "C" void freeString(char* str) { free(str); }
2. 修改汇编代码
修正栈对齐逻辑,正确保存非易失寄存器和返回的字符串指针:
extern inputString : proto C extern outputStringLen : proto C extern freeString : proto C .data .CODE asm_main PROC ; 先保存用到的非易失寄存器,符合x64调用约定 push rbx push rsi push r12 push r13 ; 预留32字节阴影空间,此时rsp刚好满足16字节对齐要求 sub rsp, 20h ; 第一次获取字符串 call inputString mov rbx, rax ; 保存第一个字符串指针 mov rcx, rax call outputStringLen mov r12, rax ; 保存第一个字符串长度 ; 第二次获取字符串 call inputString mov rsi, rax ; 保存第二个字符串指针 mov rcx, rax call outputStringLen mov r13, rax ; 保存第二个字符串长度 ; 此处可添加你的字符串拼接逻辑,直接使用rbx、r12、rsi、r13即可 ; 释放内存避免泄漏 mov rcx, rbx call freeString mov rcx, rsi call freeString ; 恢复栈和寄存器 add rsp, 20h pop r13 pop r12 pop rsi pop rbx ret asm_main ENDP END
内容的提问来源于stack exchange,提问作者Suezzeus
相关产品推荐
相关产品推荐

