old-www/LDP/LG/issue90/raghu.html

257 lines
13 KiB
HTML

<!--startcut ==============================================-->
<!-- *** BEGIN HTML header *** -->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML><HEAD>
<title>setjmp/longjmp Illustrated LG #90</title>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#0000AF"
ALINK="#FF0000">
<!-- *** END HTML header *** -->
<!-- *** BEGIN navbar *** -->
<A HREF="puryear.html">&lt;&lt;&nbsp;Prev</A>&nbsp;&nbsp;|&nbsp;&nbsp;<A HREF="index.html">TOC</A>&nbsp;&nbsp;|&nbsp;&nbsp;<A HREF="../index.html">Front Page</A>&nbsp;&nbsp;|&nbsp;&nbsp;<A HREF="http://www.linuxgazette.com/cgi-bin/talkback/all.py?site=LG&article=http://www.linuxgazette.com/issue90/raghu.html">Talkback</A>&nbsp;&nbsp;|&nbsp;&nbsp;<A HREF="../faq/index.html">FAQ</A>&nbsp;&nbsp;|&nbsp;&nbsp;<A HREF="ward.html">Next&nbsp;&gt;&gt;</A>
<!-- *** END navbar *** -->
<!--endcut ============================================================-->
<TABLE BORDER><TR><TD WIDTH="200">
<A HREF="http://www.linuxgazette.com/">
<IMG ALT="LINUX GAZETTE" SRC="../gx/2002/lglogo_200x41.png"
WIDTH="200" HEIGHT="41" border="0"></A>
<BR CLEAR="all">
<SMALL>...<I>making Linux just a little more fun!</I></SMALL>
</TD><TD WIDTH="380">
<CENTER>
<BIG><BIG><STRONG><FONT COLOR="maroon">setjmp/longjmp Illustrated</FONT></STRONG></BIG></BIG>
<BR>
<STRONG>By <A HREF="../authors/raghu.html">Raghu J Menon</A></STRONG>
</CENTER>
</TD></TR>
</TABLE>
<P>
<!-- END header -->
<p>The setjmp/longjmp set of macros implemented in the C
programming language provide the perfect platform to perform complex
flow-control, but make sure that you have gained adequate knowledge about them,
before you actually use them, or else&nbsp; your programs could become so complex
that it would be impossible to discern them. </p>
<p>What do they do?</p>
<p>The setjmp function saves the state of a program. The state of
a program to be precise are the values of sp (stack pointer), fp (frame
pointer), pc (program counter). A program state is completely defined by these
set of registers and the contents of the memory, which includes the heap and the
stack. The next obvious question would be, why do i need to save the state for?
Well simple to restore it later through longjmp. So these functions hunt
in pairs i.e. setjmp saves the state, longjmp restores it.</p>
<H2>The syntax....</H2>
<p>The syntax is quite simple. setjmp stores the state of the
program in a variable of type jmp_buf (defined in the header file setjmp.h).
Always include the header file while working with these functions.</p>
<CODE>
<p>int setjmp (jmp_buf env);</p>
<p>int longjmp(jmp_buf env , int val);</p>
</CODE>
<p>The longjmp function restores the state of the program that is
stored in env. The purpose of the parameter val will be explained later. So what
does all this add upto? Simply that the longjmp function never returns (another
one after exec). Before encountering a longjmp there has to be a setjmp which
saves the state in env and returns a value 0. When you encounter longjmp next
the state stored in env is restored and the program execution resumes at the
instruction after setjmp. It is as though the longjmp returned through setjmp.
This return should yield a value though and that value is what is specified
through the parameter val.</p>
<CODE>
<p>i = setjmp (env);//Stores the state in env and returns 0</p>
<p>...........&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Resume execution
at this point after the longjmp call as though the setjmp call</p>
<p>.......&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//returned. </p>
<p></p>
<p></p>
<p>&nbsp;</p>
<p>longjmp(env,val)</p>
</CODE>
<p>As a last point, try printing the value of i. You would get 2
values, the first one is that obtained when setjmp saves the state and will be 0
always. The second one will be the value that you pass to longjmp through the
val parameter. So the code after setjmp seems to get executed more than once.
That calls for some exploration. We therefore have our first code and an
interesting one too. <a href="misc/raghu/if-else.c.txt">if-else.c</a></p>
<p>&nbsp;Compile and run it. I hope you noticed it, both the if
and else part of the condition are executed! Now, that is not how if-else
condition is supposed to work. Looks like fork() (parent executing the if part
and child executing the else or vice-versa). Well in fork we have two different
threads of execution, that is not the case here. The setjmp call saves the state
in env and returns 0. The if condition evaluates to true and you get the first
message. Now later in the code when longjmp is executed the state is restored
and you return to the statement following setjmp with a return value 2. </p>
<p>This
return value is specified in the longjmp call. Now you see why the if condition
failed and the else was executed. In addition the program showed disparity by
not executing the last line. Well as i said earlier longjmp never returns and so
it is quite obvious why the last line is not executed. If you take out the exit
statement the code falls into a never ending loop alternating between the else
part and the longjmp call.</p>
<H2>Some thing more useful please.....</H2>
<p>As programmers you might have written code by dividing it&nbsp;
into functions or subroutines (If not learn the art of functional programming. I
started off by writing a C program as one big main function, gradually though i
was able to split my program into functions. Why?&nbsp; It is easier to debug,
that's why.). In implementing your program as functions there are bound to be
function calls that are nested, that have pretty complex flow as well. Whenever an
error occurs you need to find the function that caused it. This way it is easier
to debug the program. The code below illustrates the use of setjmp/longjmp pair
in debugging such programs. <a href="misc/raghu/nest.c.txt">nest.c</a></p>
<p>Well the program does not do anything useful other than serve
the purpose of illustrating graceful error handling. The code defines 4
functions, each one of them apart from accepting specified number of integer
parameters, also has env as its parameter. The env holds the state of the
program saved by the setjmp call in the main function. The failure in executing
each of the function is specified in the if condition. Compile the program and
execute it. Enter the following sets of values for l, m, n.</p>
<CODE>
<p>Enter values (integer) for l m and n please</p>
<p>1 </p>
<p>4</p>
<p>7</p>
<p>The functions executed normally</p>
<p><br>
Enter values (integer) for l m and n please</p>
<p>0</p>
<p>0</p>
<p>0</p>
<p>There is an error in function 1 exiting..<br>
&nbsp;</p>
<p>Enter values (integer) for l m and n please</p>
<p>1</p>
<p>1</p>
<p>2</p>
<p>There is an error in function 2 exiting..<br>
&nbsp;</p>
<p>Enter values (integer) for l m and n please</p>
<p>0</p>
<p>1</p>
<p>2</p>
<p>There is an error in function 3 exiting..</p>
<p><br>
Enter values (integer) for l m and n please</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>There is an error in function 4 exiting..<br>
&nbsp;</p>
</CODE>
<p>&nbsp;Well that was useful i suppose. The error message could
tell you where the error occurred. Let us trace the code. The setjmp in the main
function saves the state of the program and returns 0. The if condition equates
to false and therefore is not executed. The next statement calls the function
fun1 with parameters env, l, m, n, fun1 in turn calls fun2 and so on. Whenever
an error occurs in any of these functions a longjmp is executed, the val
parameter being the function number where the longjmp was executed. The program
returns to the main function (to the statement after setjmp) whenever a longjmp
is executed. The value in s now is either 1, 2, 3, 4, depending on where the
longjmp was made from. The if condition now equates to truth value and therefore
an appropriate error message is flagged indicating the function in which the
error occurred. If no error occurred during the execution of the program the
functions return normally and the last statement of the main function is
executed. Why don't i just
use the goto statement to make a jump during an error? Try compiling the code below <a href="misc/raghu/goto.c.txt">goto.c</a>. The error flagged is
because goto can be used only for local jumps. The jumps in the previous program
made by longjmp where non-local ones, goto searches for local labels and hence
cannot make non-local jumps.</p>
<p>&nbsp;</p>
<H2>Vulnerability Of The Programmer........</H2>
<p>There is a subtle bug in setjmp/longjmp, not in its
implementation, but in the way we use it. Most of us are quite unaware and
rightly so of the stack state when we write a program. It is when there is an
error we try and trace it by inspecting the stack (through gdb). Whenever there
is a function call the stack is manipulated. First the arguments to the called
function are pushed in the reverse order. Next the JSR is called to push the
return address (pc) and then the fp, fp and sp are then emptied to make a new
stack frame for the called function. The called function immediately on entry
creates space on the stack for the local variables that might have been declared
in the function. Now that you have an idea of the stack structure, try running
the code below <a href="misc/raghu/seg.c.txt">seg.c</a>.&nbsp; It compiles fine, but alas it fails to run completely
and faults. Could you find the reason? </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Let us trace the
code. The main function calls&nbsp; me_first with 2 arguments,&nbsp; the
arguments are pushed onto the stack env followed by the string &quot;IC-Labs&quot;, the
JSR then pushes the pc and fp values on the stack. On entry&nbsp; the function
creates a local variable i on the stack. This is followed by a call to the setjmp
function which saves the current state, that of me_first function.
The local variable now contains the value 0, value returned by setjmp. After
returning from me_first the&nbsp; stack is returned to the original state, one
in which it left the main function. The i_follow function is called next with a
value 3 and the env variable. The stack is modified as above (when me_first was
called). In the function
the state stored in env is restored by longjmp. The values in the stack remain
the same i.e. as they where during the execution of function&nbsp; i_follow .
The state
though is that of function me_first. The stack frame of this state has a variable of type
(char * )&nbsp; which previously had a string &quot;IC-Labs&quot;. Now after the state has
been restored the value that variable s holds is 3 (the value that i_follow was passed
from the main). As a result of the longjmp the statement following setjmp in
me_first is executed.&nbsp; In executing the statement after setjmp (printf),
there is an illegal memory access since in trying to print out s the program
tries to find a string at memory location 0x3 which causes a memory protection
error and causes the program to fault. This bug is very subtle and often goes
unnoticed, this is because the stack frame of both the functions look almost the
same. In cases were the stack frames are the same there is no such error. Try
replacing the argument &quot;char *&quot; with one of type int, and rerun it. Did it fault?</p>
<H2>Signal Handling........</H2>
<p>One of the beauties of these functions is that you can longjmp
from a signal handler and return to your program and catch those signals again.
Check out the program below <a href="misc/raghu/sig.c.txt">sig.c</a></p>
<p>The main function installs a signal handler using the signal
system call, parameters are the signo(SIGALRM), which indicates the signal
for which you are setting up the handler and the handler routine which is
executed when the signal occurs. The alarm call sends the&nbsp;&nbsp; SIGALRM
signal to the program every second. The alarm_handler basically longjmps out
after 8 seconds have passed.&nbsp;&nbsp;&nbsp; </p>
<!-- *** BEGIN author bio *** -->
<P>&nbsp;
<P>
<!-- *** BEGIN bio *** -->
<P>
<img ALIGN="LEFT" ALT="[BIO]" SRC="../gx/2002/note.png">
<em>
I am almost through with my graduate studies in computer
science and engineering. I hail from Trichur (a small town in god's own country,
Kerala). Any constructive criticism with regards to the style and content are
welcome. Please feel free to contact me via e-mail.
</em>
<br CLEAR="all">
<!-- *** END bio *** -->
<!-- *** END author bio *** -->
<!-- *** BEGIN copyright *** -->
<hr>
<CENTER><SMALL><STRONG>
Copyright &copy; 2003, Raghu J Menon.
Copying license <A HREF="../copying.html">http://www.linuxgazette.com/copying.html</A><BR>
Published in Issue 90 of <i>Linux Gazette</i>, May 2003
</STRONG></SMALL></CENTER>
<!-- *** END copyright *** -->
<HR>
<!--startcut ==========================================================-->
<!-- *** BEGIN navbar *** -->
<A HREF="puryear.html">&lt;&lt;&nbsp;Prev</A>&nbsp;&nbsp;|&nbsp;&nbsp;<A HREF="index.html">TOC</A>&nbsp;&nbsp;|&nbsp;&nbsp;<A HREF="../index.html">Front Page</A>&nbsp;&nbsp;|&nbsp;&nbsp;<A HREF="http://www.linuxgazette.com/cgi-bin/talkback/all.py?site=LG&article=http://www.linuxgazette.com/issue90/raghu.html">Talkback</A>&nbsp;&nbsp;|&nbsp;&nbsp;<A HREF="../faq/index.html">FAQ</A>&nbsp;&nbsp;|&nbsp;&nbsp;<A HREF="ward.html">Next&nbsp;&gt;&gt;</A>
<!-- *** END navbar *** -->
</BODY></HTML>
<!--endcut ============================================================-->