为什么 setjmp() 在 windows 上接受 2 个参数,而在 linux 上只接受 1 个参数?
Why does setjmp() take 2 args on windows but only 1 arg on linux?
此 setjmp.c 在 Microsoft Visual Studio Professional 2019(版本 16.10.1)
下的 windows 中构建
Rebuild started...
1>------ Rebuild All started: Project: setjmp, Configuration: Release x64 ------
1>setjmp.c
1>C:\Users\john\source\repos\setjmp\setjmp.c(48,10): warning C4013: 'setjmp' undefined; assuming extern returning int
1>Generating code
1>Previous IPDB not found, fall back to full compilation.
1>All 1 functions were compiled because no usable IPDB/IOBJ from previous compilation was found.
1>Finished generating code
1>setjmp.vcxproj -> C:\Users\john\source\repos\setjmp\x64\Release\setjmp.exe
1>Done building project "setjmp.vcxproj".
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========
实际的cl.exe C编译器命令是
1> C:\Program Files (x86)\Microsoft Visual Studio19\Professional\VC\Tools\MSVC.29.30037\bin\HostX86\x64\CL.exe /c /Zi /nologo /W3 /WX- /diagnostics:column /sdl /O2 /Oi /GL /D NDEBUG /D _CONSOLE /D _UNICODE /D UNICODE /Gm- /EHsc /MD /GS /Gy /fp:precise /permissive- /Zc:wchar_t /Zc:forScope /Zc:inline /Fo"x64\Release\" /Fd"x64\Release\vc142.pdb" /external:env:EXTERNAL_INCLUDE /external:W3 /Gd /TC /FC /errorReport:prompt setjmp.c
源代码为
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
typedef uint64_t QWORD;
typedef uint32_t DWORD;
typedef uint16_t WORD;
typedef uint8_t BYTE;
typedef struct MSTJMP_FLOAT128
{
QWORD Part[2];
} MSTJMP_FLOAT128;
typedef struct JUMP_BUFFER
{
QWORD Frame0;
QWORD Rbx0;
QWORD Rsp0;
QWORD Rbp0;
QWORD Rsi0;
QWORD Rdi0;
QWORD R120;
QWORD R130;
QWORD R140;
QWORD R150;
QWORD Rip0;
DWORD MxCsr4;
WORD FpCsr2;
WORD Spare2;
struct MSTJMP_FLOAT128 Xmm6x;
struct MSTJMP_FLOAT128 Xmm7x;
struct MSTJMP_FLOAT128 Xmm8x;
struct MSTJMP_FLOAT128 Xmm9x;
struct MSTJMP_FLOAT128 Xmm10x;
struct MSTJMP_FLOAT128 Xmm11x;
struct MSTJMP_FLOAT128 Xmm12x;
struct MSTJMP_FLOAT128 Xmm13x;
struct MSTJMP_FLOAT128 Xmm14x;
struct MSTJMP_FLOAT128 Xmm15x;
} JUMP_BUFFER;
int main() {
struct JUMP_BUFFER jumpbuffer;
setjmp(&jumpbuffer, 0);
return 0;
}
setjmp() 调用的反汇编是
setjmp(&jumpbuffer, 0);
00007FF641B51019 xor edx,edx
00007FF641B5101B lea rcx,[jumpbuffer]
00007FF641B51020 call __intrinsic_setjmp (07FF641B51C20h)
这导致 setjmp() 代码
--- d:\a01\_work\s\src\vctools\crt\vcruntime\src\eh\amd64\setjmp.asm ---------
00007FF82B6F0300 mov qword ptr [rcx],rdx
00007FF82B6F0303 mov qword ptr [rcx+8],rbx
00007FF82B6F0307 mov qword ptr [rcx+18h],rbp
所以一切都正常运行,没有任何错误,因为 windows 在 setjmp.asm
中使用了 C 运行时内部函数
然而,在linux下,它会产生一个错误:
error: macro "setjmp" passed 2 arguments, but takes just 1
Google'ing 找到了这段代码:
#if defined(_WIN64)
/* On w64, setjmp is implemented by _setjmp which needs a second parameter.
* If this parameter is NULL, longjump does no stack unwinding.
* That is what we need for QEMU. Passing the value of register rsp (default)
* lets longjmp try a stack unwinding which will crash with generated code. */
# undef setjmp
# define setjmp(env) _setjmp(env, NULL)
#endif
综上所述,windows可以带2个参数:
setjmp(&jumpbuffer, 0);
和 linux 可以带 1 个参数:
setjmp(&jumpbuffer);
为了验证 linux 喜欢那个 setjmp,我用 gdb 追踪如下:
你的问题是你没有 #include <setjmp.h>
所以编译器不知道函数签名并且 假设 setjmp
是返回 int
的函数并接受未知类型的未知数量的参数。当您声明 func()
时,它是一个在旧 C 标准中返回 int 的函数,您可以调用 func()
、func(1, 2, 3)
或 func(1, "abc", 'd', 2, 3)
... - 都是有效的
注意编译器输出中的警告
setjmp.c(48,10): warning C4013: 'setjmp' undefined; assuming extern returning int
也可以是reproducible on godbolt,MSVC也有同样的警告。你可以看到编译器没有报告任何错误,即使我添加了一些虚拟 setjmp
调用,如
setjmp(&jumpbuffer, 0);
setjmp();
setjmp(1, 2, "abc");
setjmp(&jumpbuffer, 0, 3, 4, 5);
见
- Implicit function declarations sometimes work in C?
- Why does declaration of function with int return type is not compulsory in C?
- Are prototypes required for all functions in C89, C90 or C99?
- warning: implicit declaration of function
- Implicit function declarations in C
- Is implicit function declaration legal in C89?
- Is it portable to define and declare the function after calling it in main in C?
此 setjmp.c 在 Microsoft Visual Studio Professional 2019(版本 16.10.1)
下的 windows 中构建Rebuild started...
1>------ Rebuild All started: Project: setjmp, Configuration: Release x64 ------
1>setjmp.c
1>C:\Users\john\source\repos\setjmp\setjmp.c(48,10): warning C4013: 'setjmp' undefined; assuming extern returning int
1>Generating code
1>Previous IPDB not found, fall back to full compilation.
1>All 1 functions were compiled because no usable IPDB/IOBJ from previous compilation was found.
1>Finished generating code
1>setjmp.vcxproj -> C:\Users\john\source\repos\setjmp\x64\Release\setjmp.exe
1>Done building project "setjmp.vcxproj".
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========
实际的cl.exe C编译器命令是
1> C:\Program Files (x86)\Microsoft Visual Studio19\Professional\VC\Tools\MSVC.29.30037\bin\HostX86\x64\CL.exe /c /Zi /nologo /W3 /WX- /diagnostics:column /sdl /O2 /Oi /GL /D NDEBUG /D _CONSOLE /D _UNICODE /D UNICODE /Gm- /EHsc /MD /GS /Gy /fp:precise /permissive- /Zc:wchar_t /Zc:forScope /Zc:inline /Fo"x64\Release\" /Fd"x64\Release\vc142.pdb" /external:env:EXTERNAL_INCLUDE /external:W3 /Gd /TC /FC /errorReport:prompt setjmp.c
源代码为
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
typedef uint64_t QWORD;
typedef uint32_t DWORD;
typedef uint16_t WORD;
typedef uint8_t BYTE;
typedef struct MSTJMP_FLOAT128
{
QWORD Part[2];
} MSTJMP_FLOAT128;
typedef struct JUMP_BUFFER
{
QWORD Frame0;
QWORD Rbx0;
QWORD Rsp0;
QWORD Rbp0;
QWORD Rsi0;
QWORD Rdi0;
QWORD R120;
QWORD R130;
QWORD R140;
QWORD R150;
QWORD Rip0;
DWORD MxCsr4;
WORD FpCsr2;
WORD Spare2;
struct MSTJMP_FLOAT128 Xmm6x;
struct MSTJMP_FLOAT128 Xmm7x;
struct MSTJMP_FLOAT128 Xmm8x;
struct MSTJMP_FLOAT128 Xmm9x;
struct MSTJMP_FLOAT128 Xmm10x;
struct MSTJMP_FLOAT128 Xmm11x;
struct MSTJMP_FLOAT128 Xmm12x;
struct MSTJMP_FLOAT128 Xmm13x;
struct MSTJMP_FLOAT128 Xmm14x;
struct MSTJMP_FLOAT128 Xmm15x;
} JUMP_BUFFER;
int main() {
struct JUMP_BUFFER jumpbuffer;
setjmp(&jumpbuffer, 0);
return 0;
}
setjmp() 调用的反汇编是
setjmp(&jumpbuffer, 0);
00007FF641B51019 xor edx,edx
00007FF641B5101B lea rcx,[jumpbuffer]
00007FF641B51020 call __intrinsic_setjmp (07FF641B51C20h)
这导致 setjmp() 代码
--- d:\a01\_work\s\src\vctools\crt\vcruntime\src\eh\amd64\setjmp.asm ---------
00007FF82B6F0300 mov qword ptr [rcx],rdx
00007FF82B6F0303 mov qword ptr [rcx+8],rbx
00007FF82B6F0307 mov qword ptr [rcx+18h],rbp
所以一切都正常运行,没有任何错误,因为 windows 在 setjmp.asm
中使用了 C 运行时内部函数然而,在linux下,它会产生一个错误:
error: macro "setjmp" passed 2 arguments, but takes just 1
Google'ing 找到了这段代码:
#if defined(_WIN64)
/* On w64, setjmp is implemented by _setjmp which needs a second parameter.
* If this parameter is NULL, longjump does no stack unwinding.
* That is what we need for QEMU. Passing the value of register rsp (default)
* lets longjmp try a stack unwinding which will crash with generated code. */
# undef setjmp
# define setjmp(env) _setjmp(env, NULL)
#endif
综上所述,windows可以带2个参数:
setjmp(&jumpbuffer, 0);
和 linux 可以带 1 个参数:
setjmp(&jumpbuffer);
为了验证 linux 喜欢那个 setjmp,我用 gdb 追踪如下:
你的问题是你没有 #include <setjmp.h>
所以编译器不知道函数签名并且 假设 setjmp
是返回 int
的函数并接受未知类型的未知数量的参数。当您声明 func()
时,它是一个在旧 C 标准中返回 int 的函数,您可以调用 func()
、func(1, 2, 3)
或 func(1, "abc", 'd', 2, 3)
... - 都是有效的
注意编译器输出中的警告
setjmp.c(48,10): warning C4013: 'setjmp' undefined; assuming extern returning int
也可以是reproducible on godbolt,MSVC也有同样的警告。你可以看到编译器没有报告任何错误,即使我添加了一些虚拟 setjmp
调用,如
setjmp(&jumpbuffer, 0);
setjmp();
setjmp(1, 2, "abc");
setjmp(&jumpbuffer, 0, 3, 4, 5);
见
- Implicit function declarations sometimes work in C?
- Why does declaration of function with int return type is not compulsory in C?
- Are prototypes required for all functions in C89, C90 or C99?
- warning: implicit declaration of function
- Implicit function declarations in C
- Is implicit function declaration legal in C89?
- Is it portable to define and declare the function after calling it in main in C?