setjmp
and longjmp
are a
pair of C function facilitating cross-procedure transfer of
control. Typically they are used to allow resumption of execution at
a known good point after an error.
Both take as first argument a buffer, which is used to hold the
machine state at the jump destination. When
setjmp
is called it populates that buffer with
the current location state (which includes stack and frame pointers
and the return address for the call to setjmp
,
and returns zero.
longjmp
takes a buffer previously populated by
setjmp
. It also takes a (non-zero) second
argument, which will ultimately be the result of the function
call. longjmp
restores the machine state from
the buffer. It then jumps to the return address it has just
restored, passing its second argument as the result. That return
address is the return address from the original call to
setjmp
, so the effect will be as if
setjmp
has just returned with a non-zero
argument.
setjmp
and longjmp
are
typically used in a top level function in the following way.
#include <setjmp.h> ... jmp_buf buf; if (0 == setjmp (buf)) { normal processing passing in buf } else { error handling code } ...
During normal processing if an error is found, the state held in
buf
can be used to return control back to the top
level using longjmp
.
#include <setjmp.h>
...
if (error detected)
{
longjmp (buf, 1);
}
...
The program will behave as though the original call to
setjmp
had just returned with result 1.
It will be appreciated that this is behavior that cannot usually be
written in C. The OpenRISC 1000 implementation is given as an example. This
processor has 32 registers, r0
through
r31
, each of 32-bits. r0
is
always tied to zero, so need not be saved. r11
is
the function result register, which is always set by
setjmp
and longjmp
, so
also need not be saved. In addition we should save and restore the
machine's 32-bit supervision register, which holds the branch flag.
Thus we need the buffer to be 31 32-bit words long. This is defined
in the setjmp
header (see Section 4.2.2).
In the Application Binary Interface (ABI)
for the OpenRISC 1000 , function arguments are passed in registers
r3
through r8
and the function
return address is in r9
.
When defining these two functions, in assembler, be aware of any
prefix conventions used by the C compiler. It is common for symbols
defined in C to have an underscore prepended (this is the case for
the OpenRISC 1000 ). Thus in this case the assembler should define
_setjmp
and _longjmp
.
This is the implementation of setjmp
.
.global _setjmp _setjmp: l.sw 4(r3),r1 /* Slot 0 saved for flag in future */ l.sw 8(r3),r2 l.sw 12(r3),r3 l.sw 16(r3),r4 l.sw 20(r3),r5 l.sw 24(r3),r6 l.sw 28(r3),r7 l.sw 32(r3),r8 l.sw 36(r3),r9 l.sw 40(r3),r10 /* Skip r11 */ l.sw 44(r3),r12 l.sw 48(r3),r13 l.sw 52(r3),r14 l.sw 56(r3),r15 l.sw 60(r3),r16 l.sw 64(r3),r17 l.sw 68(r3),r18 l.sw 72(r3),r19 l.sw 76(r3),r20 l.sw 80(r3),r21 l.sw 84(r3),r22 l.sw 88(r3),r23 l.sw 92(r3),r24 l.sw 96(r3),r25 l.sw 100(r3),r26 l.sw 104(r3),r27 l.sw 108(r3),r28 l.sw 112(r3),r29 l.sw 116(r3),r30 l.sw 120(r3),r31 l.jr r9 l.addi r11,r0,0 /* Zero result */
In this simplified implementation, the status flags are not
saved—that is a potential future enhancement. All the general
registers, with the exception of r0
(always zero)
and r11
(result register) are saved in the
buffer, which, being the first argument, is pointed to by
r3
.
Finally the result register, r11
is set to zero
and the function returns using r9
(the OpenRISC 1000 has
delayed branches, so the setting of r11
is placed
after the branch to return.).
The implementation of longjmp
is slightly more
complex, since the second argument will be returned as the effective
result from setjmp
, unless
the second argument is zero in which case 1 is used.
The result must be dealt with first and placed in the result
register, r11
, because the second argument, in
r4
will be subsequently overwritten when the
machine state is restored. Similarly we must ensure that
r3
, which holds the first argument pointing to
the restore buffer must itself be the last register restored.
.global _longjmp _longjmp: /* Sort out the return value */ l.sfne r4,r0 l.bf 1f l.nop l.j 2f l.addi r11,r0,1 /* 1 as result */ 1: l.addi r11,r4,0 /* val as result */ /* Restore all the other registers, leaving r3 to last. */ 2: l.lwz r31,120(r3) l.lwz r30,116(r3) l.lwz r29,112(r3) l.lwz r28,108(r3) l.lwz r27,104(r3) l.lwz r26,100(r3) l.lwz r25,96(r3) l.lwz r24,92(r3) l.lwz r23,88(r3) l.lwz r22,84(r3) l.lwz r21,80(r3) l.lwz r20,76(r3) l.lwz r19,72(r3) l.lwz r18,68(r3) l.lwz r17,64(r3) l.lwz r16,60(r3) l.lwz r15,56(r3) l.lwz r14,52(r3) l.lwz r13,48(r3) l.lwz r12,44(r3) l.lwz r10,40(r3) /* Omit r11 */ l.lwz r9,36(r3) l.lwz r8,32(r3) l.lwz r7,28(r3) l.lwz r6,24(r3) l.lwz r5,20(r3) l.lwz r4,16(r3) l.lwz r2,8(r3) /* Skip r3 */ l.lwz r1,4(r3) /* Slot 0 saved for flag in future */ l.lwz r3,12(r3) /* Now safe */ /* Result is already in r11. Having restored r9, it will appear as though we have returned from the earlier call to _setjmp. The non-zero result gives it away though. */ l.jr r9 l.nop
The return address, stack pointer and frame pointer having been
restored, the return from the function, will place the execution
point immediately after the original call to
setjmp
.
The following is a simple test program, which can be used to verify
that setjmp
and longjmp
are working correctly.
#include <setjmp.h> #include <stdio.h> void testit (jmp_buf env, int prev_res) { int res = (0 == prev_res) ? prev_res : prev_res + 1; printf ("Long jumping with result %d\n", res); longjmp (env, res); } /* testit () */ int main (int argc, char *argv[]) { jmp_buf env; int res = setjmp (env); printf ("res = 0x%08x\n", res); if (res > 1) { return 0; } testit (env, res); return 256; /* We should never actually get here */ } /* main () */