如何为MinGW-w64编译的裸汇编函数实现自定义SEH?
纯汇编函数在MinGW-w64下实现自定义SEH的实用技巧
1. 用C函数做异常感知的包装层(最省心的方案)
你之前尝试用C调用汇编没成功,大概率是调用约定或栈帧没对齐的问题。按下面的步骤来:
- 写一个普通C函数,在里面用
__try/__except或者注册向量异常处理器(AddVectoredExceptionHandler),然后在C函数内部调用你的汇编函数。 - 汇编函数必须严格遵循MinGW默认的
cdecl调用约定:- 调用者负责清理栈
- 保存非易失性寄存器(rbp, rbx, rsi, rdi, r12-r15)
- 栈要保持16字节对齐(调用函数前,rsp必须是16的倍数)
- 示例结构:
汇编里要保证栈对齐,比如开头:#include <windows.h> #include <excpt.h> // 声明汇编函数 extern void my_asm_func(void); int main() { __try { my_asm_func(); } __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { // 处理异常 } return 0; }global my_asm_func my_asm_func: push rbp mov rbp, rsp sub rsp, 16 ; 保证栈16字节对齐(如果有局部变量的话) ; 你的汇编代码 mov rsp, rbp pop rbp ret
2. 手动给汇编函数添加MinGW兼容的Unwind元数据
如果不想用C包装,直接给汇编加 unwind 信息,让系统能识别栈帧。MinGW用的是DWARF unwind格式,用.cfi_*伪指令:
global my_asm_func my_asm_func: .cfi_startproc push rbp .cfi_def_cfa_offset 16 .cfi_offset rbp, -16 mov rbp, rsp .cfi_def_cfa_register rbp ; 你的汇编代码 mov rsp, rbp pop rbp .cfi_def_cfa rsp, 8 ret .cfi_endproc
这些伪指令告诉编译器栈帧的变化,异常处理时能正确展开栈,这样你注册的向量异常处理器就能捕获汇编函数里的异常。
3. 用setjmp/longjmp实现轻量级异常捕获
如果不需要系统级SEH的展开能力,用setjmp/longjmp更简单:
- 在调用汇编函数前用
setjmp保存上下文 - 汇编函数里如果检测到异常(比如触发了访问错误,或者自己判断的错误),调用
longjmp跳回处理点 - 示例:
汇编里调用#include <setjmp.h> jmp_buf env; extern void my_asm_func(void); int main() { if (setjmp(env) == 0) { my_asm_func(); } else { // 处理异常 } return 0; }longjmp的方式(注意参数传递,cdecl下第一个参数在rcx):global my_asm_func extern longjmp my_asm_func: ; 假设这里触发了异常,跳回env mov rcx, env mov rdx, 1 call longjmp ret
注意事项
- 用
AddVectoredExceptionHandler时,要在处理器函数里判断异常来源是否是你的汇编函数,可以通过CONTEXT里的rip寄存器范围来确认。 - 汇编里的栈操作必须严格对齐,否则不仅异常处理会出问题,还可能触发CPU错误。
内容的提问来源于stack exchange,提问作者Edenia
相关产品推荐
相关产品推荐

