8.6.Nonlocal Jumps

\(8.6.\)Nonlocal Jumps

1.Basic Concepts

  • Nonlocal jumps transfers control directly from one function to another currently executing function without having to go through the normal call-and-return sequence.

  Nonlocal jumps are provided by the following functions:

1
2
3
4
5
6
#include <setjmp.h>

int setjmp(jmp_buf env);
int sigsetjmp(sigjmp_buf env, int savesigs);

/* Returns: 0 from setjmp, nonzero from longjmps */
  • The setjmp function saves the current calling environment in the env buffer for later use by longjmp.

    • The calling environment includes the program counter, stack pointer, and general-purpose registers.
1
2
3
4
5
6
#include <setjmp.h>

void longjmp(jmp_buf env, int retval);
void siglongjmp(sigjmp_buf env, int retval);

/* Never returns */
  • The longjmp function restores the calling environment from the env buffer and then triggers a return from the most recent setjmp call that initialized env.

  • The setjmp then returns with the nonzero return value retval.

    • The setjmp function is called once but returns multiple times: once when the setjmp is first called and the calling environment is stored in the env buffer, and once for each corresponding longjmp call.

  An important application of nonlocal jumps is to branch out of a signal handler to a specific code location, rather than returning to the instruction that was interrupted by the signal:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include "csapp.h"

sigjmp_buf buf;

void handler(int sig)
{
siglongjmp(buf, 1);
}

int main()
{
if (!sigsetjmp(buf, 1)) {
Signal(SIGINT, handler);
Sio_puts("starting\n");
}
else
Sio_puts("restarting\n");

while(1) {
Sleep(1);
Sio_puts("processing...\n");
}
exit(0); /* Control never reaches here */
}
  1. The initial call to the sigsetjmp function saves the calling environment and signal context.

  2. When the user types Ctrl+C, the kernel sends a SIGINT signal to the process.

  3. Instead of returning from the signal handler, which would pass control back to the interrupted processing loop, the handler performs a nonlocal jump back to the beginning of the main program.

  The output of the program is as below:

1
2
3
4
5
6
7
8
9
10
linux> ./restart
starting
processing...
processing...
Ctrl+C
restarting
processing...
Ctrl+C
restarting
processing...