2

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

vengy
  • 1,548
  • 10
  • 18
  • 3
    What compiler? MS's compiler's `setjmp` [only takes one parameter](https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setjmp?view=msvc-160). – ikegami Aug 20 '21 at 03:12
  • 3
    Why microsoft does anything is a mystery to us all. It may be that DOS prefers `setjmp` to be the same as `sigsetjmp` is in the normal world. Probably best to check your proprietary system's documentation for clarification. – mevets Aug 20 '21 at 03:14
  • 1
    `(jmp_buf)&jumpbuffer` That doesn't look at all right. – Shawn Aug 20 '21 at 03:20
  • Updated the question...the 2 args are for "On w64, setjmp is implemented by _setjmp which needs a second parameter. * If this parameter is NULL, longjump does no stack unwinding." – vengy Aug 20 '21 at 03:22
  • Sounds like you answered your question. – Shawn Aug 20 '21 at 03:28
  • You didn't `#include `. And which compiler are you using? I'm getting `Error C2660 '_setjmp': function does not take 2 arguments` on MSVC 19.29.30038.1. Same on godbolt: https://godbolt.org/z/ccETMGzz4 – phuclv Aug 20 '21 at 11:42
  • I'm using VS 16.10.2 with cl 19.29.30038.1 and has no way to reproduce your observation – phuclv Aug 20 '21 at 11:45
  • In C mode I get `error C2167: '_setjmp': too many actual parameters for intrinsic function` https://godbolt.org/z/hfEE7hKTs – phuclv Aug 20 '21 at 12:01

1 Answers1

4

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

phuclv
  • 37,963
  • 15
  • 156
  • 475
  • Thank you! I was rolling my own version of setjmp so intentionally didn't include as you noticed. What I didn't know was that "assumes that setjmp is some function returning int and accepts an unknown number of parameters." – vengy Aug 20 '21 at 12:06
  • @vengy that's one of the painful things when using older C. You should always check compiler warnings and do some research on it. Besides qemu may also roll out its own C stdlib because it needs to emulate many things, and that lib may include other non-standard things – phuclv Aug 20 '21 at 12:10
  • I'll definitely pay more attention to compiler warnings. Thanks again phuclv! :) – vengy Aug 20 '21 at 12:12
  • Re “That's why `main` must be declared as `int main(void)` in C instead of just `int main()` like in C++”: The C standard does not require this. C 2018 5.1.2.2.1 1 says `main` should be defined (not just declared) as `int main(void)` or `int main(int argc, char *argv[])` or equivalent. A definition with `int main() { … }` has an empty *declaration-list* (that is the list of parameter declarations after the `)` and before the `{`, not declarations inside the parentheses), and that tells the compiler it uses no parameters, making it equivalent to `int main(void) { … }`. – Eric Postpischil Aug 20 '21 at 12:17