MSVC中_setjmp的定义、链接匹配机制及相关疑问
MSVC中_setjmp的定义、链接匹配机制及相关疑问
这个问题确实戳中了MSVC与统一C运行时库(ucrtbase.dll)交互里比较隐晦的实现细节,我来一步步拆解清楚:
一、先确认你观察到的符号差异
首先你通过objdump查到的两个ucrtbase.dll里的setjmp相关符号差异是准确的:
SysWOW64(32位ucrtbase)的符号:
C:\Users\_____\Desktop\b>objdump -x C:\Windows\SysWOW64\ucrtbase.dll | findstr setjmp [ 88] +base[ 89] 0058 __intrinsic_setjmp [1860] +base[1861] 0744 _setjmp3
System32(64位ucrtbase)的符号:
C:\Users\_____\Desktop\b>objdump -x C:\Windows\System32\ucrtbase.dll | findstr setjmp [ 74] +base[ 75] 004a __intrinsic_setjmp [ 75] +base[ 76] 004b __intrinsic_setjmpex [2370] +base[2371] 0942 setjmp
二、MSVC头文件的定义逻辑
在MSVC的头文件(比如你用的2019 BuildTools版本)里,setjmp被宏替换为_setjmp,同时声明了对应的函数原型:
#ifndef _INC_SETJMPEX #define setjmp _setjmp #endif // Function prototypes int __cdecl setjmp( _Out_ jmp_buf _Buf );
这里的矛盾点很明显:所有setjmp都被替换成_setjmp,但_setjmp的符号在两个ucrtbase.dll里都找不到,编辑器也搜不到它的函数定义——这也是你疑惑的核心。
三、对比mingw-w64的处理方式
先看mingw-w64的实现,它是明着根据架构切换函数的:
x86(32位):
#define setjmp(BUF) _setjmp3((BUF), NULL) ... int __cdecl __attribute__ ((__nothrow__,__returns_twice__)) _setjmp3(jmp_buf _Buf, void *_Ctx);
直接把setjmp映射到_setjmp3,手动传NULL作为上下文参数。
x64(64位):
#define _setjmp __intrinsic_setjmpex ... #define setjmp(BUF) _setjmp((BUF), __builtin_frame_address (0)) ... int __cdecl __attribute__ ((__nothrow__,__returns_twice__)) _setjmp(jmp_buf _Buf, void *_Ctx);
通过多层宏,最终把setjmp映射到未公开的__intrinsic_setjmpex,同时自动获取栈帧地址作为上下文参数。
四、MSVC的核心实现逻辑:_setjmp是编译器处理的特殊内在函数
你的猜测完全正确:MSVC里的_setjmp确实是文档未公开的编译器内在函数(intrinsic),它的工作机制是:
- 当你在代码中使用
setjmp(被宏替换为_setjmp)时,MSVC编译器不会把它当成普通的外部函数符号去ucrtbase里查找链接,而是会根据目标架构直接做以下处理:- x86(32位)程序:编译器自动把
_setjmp的调用转换成对ucrtbase里_setjmp3的调用,同时隐式传入NULL作为上下文参数(和mingw-w64的逻辑一致)。 - x64(64位)程序:编译器自动把
_setjmp的调用转换成对ucrtbase里__intrinsic_setjmpex或setjmp的调用,同时自动生成获取栈帧地址的代码作为上下文参数。
- x86(32位)程序:编译器自动把
- 你提到msvcrt.dll里有
_setjmp,但那是旧版CRT的符号,现在MSVC默认链接的ucrtbase是统一CRT,确实没有导出_setjmp——但编译器通过内在函数机制绕开了普通的符号查找流程,直接生成适配目标架构的机器码和函数调用。 - 至于为什么它是“未公开”的?因为微软只把对外推荐使用的intrinsic函数列在官方文档里,这类和CRT深度绑定的实现细节函数,一般不会对外文档化,属于编译器和CRT的私有约定。
总结
MSVC的这套设计是为了给开发者提供统一的setjmp接口,不用关心底层架构的差异:头文件用宏把setjmp统一替换成_setjmp,编译器再把_setjmp这个“伪符号”转换成对应架构下ucrtbase里实际存在的函数调用,全程不需要开发者手动处理参数或切换函数名——这就是为什么你看不到_setjmp的定义,程序却能正常链接运行的原因。
内容来源于stack exchange
相关产品推荐
相关产品推荐

