Search code examples
cansi-c

Why does setjmp() take 2 args on windows but only 1 arg on linux?


This setjmp.c builds in windows under Microsoft Visual Studio Professional 2019 (Version 16.10.1)

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 ==========

The actual cl.exe C compiler command is

1>    C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.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

The source code is

#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;
}

The disassembly for setjmp() call is

setjmp(&jumpbuffer, 0);
00007FF641B51019  xor         edx,edx  
00007FF641B5101B  lea         rcx,[jumpbuffer]  
00007FF641B51020  call        __intrinsic_setjmp (07FF641B51C20h)  

which leads to the setjmp() code

--- d:\a01\_work\2\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  

so everything is functioning correctly without any errors because windows is using a C-runtime intrinsic within setjmp.asm

However, under linux, it generates an error:

error: macro "setjmp" passed 2 arguments, but takes just 1

Google'ing found this code snippet:

#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

To sum up, windows can take 2 args:

setjmp(&jumpbuffer, 0);

and linux can take 1 arg:

setjmp(&jumpbuffer);

To verify that linux likes that setjmp, I traced with gdb as follows:

linux_gdb


Solution

  • Your issue is that you didn't #include <setjmp.h> so the compiler doesn't know the function signature and assumes that setjmp is some function returning int and accepts an unknown number of parameters of unknown types. When you declare func() then it's a function returning int in older C standards and you can call func(), func(1, 2, 3) or func(1, "abc", 'd', 2, 3)... - all are valid

    Notice the warning right from your compiler output

    setjmp.c(48,10): warning C4013: 'setjmp' undefined; assuming extern returning int

    It can also be reproducible on godbolt, with the same warning from MSVC. And you can see that the compiler doesn't report any errors even though I've added some dummy setjmp calls like

    setjmp(&jumpbuffer, 0);
    setjmp();
    setjmp(1, 2, "abc");
    setjmp(&jumpbuffer, 0, 3, 4, 5);
    

    See