Saturday, September 13, 2014

Unix Prog: Non-Local Jump(1)

1. Goto

c program allows "goto" call to transfer control to another place, IN LOCAL FUNCTION.

proc.c:
 #include<stdio.h>  
 #include<stdlib.h>  
   
 int main(int argc, char* argv[])  
 {  
  int count = 0;  
  bebe:  
  printf("Hello world!\n");  
  count++;  
  if(count >= 3)  
   exit(0);  
  goto bebe;  
 }  

shell:
 ubuntu@ip-172-31-23-227:~$ ./proc.out  
 Hello world!  
 Hello world!  
 Hello world!  

Note: we can't use goto call to transfer control to another function. Because from kernel perspective, the stack just contains existing function and its callers, and program should work in local address inside the function and can't jump to another "stack entry"(function).

2. setjmp, longjmp
setjmp and longjmp functions can be used to transfer control to another function.

Definition:
jmp_buf is the structure containing all the environment information needed to restore the status of the stack.

Calling setjmp will make it prepare all the stack environment information needed to restore. returning 0 means being ready, non-0 means it is a request coming from the longjmp.

longjmp will use the glboal jmp_buf structure prepared by the setjmp and the returning value for setjmp. It will transfer control back to the setjmp call, and make it return value specified by "longjmp's 2nd argument"
 ubuntu@ip-172-31-23-227:~$ less /usr/include/setjmp.h  
 ......  
 /* Store the calling environment in ENV, also saving the signal mask.  
   Return 0. */  
 extern int setjmp (jmp_buf __env) __THROWNL;  
 ......  
 /* Jump to the environment saved in ENV, making the  
   `setjmp' call there return VAL, or 1 if VAL is 0. */  
 extern void longjmp (struct __jmp_buf_tag __env[1], int __val)  
    __THROWNL __attribute__ ((__noreturn__));  
 ......  

proc.c:
 #include<stdio.h>  
 #include<stdlib.h>  
 #include<setjmp.h>  
   
 jmp_buf buf;  
   
 void test()  
 {  
  static int rc = 0;  
  printf("Hello world!\n");  
  longjmp(buf, rc++);  
 }  
   
 int main(int argc, char* argv[])  
 {  
  int rc;  
  int count = 0;  
  if((rc = setjmp(buf)) != 0) {  
   printf("return code: %d\n", rc);  
  }  
   
  if(count >= 3) exit(0);  
   
  count++;  
  test();  
  exit(0);  
 }  

shell:
1) First round, setjmp is called directly from main function, it return 0, indicating setup information correctly.
Then it comes to test function, at this time, the stack contains two entry: main function in the bottom, and test function on the top.
At test function, it calls longjmp with buf(main stack information), and return value rc, at this time, rc is 0, if rc is 0, setjmp will return 1 by default.
So the control get transferred to setjmp in the main function, return 1. At this time, the stack get rid of test function on the top, now only leave main function.

2) Second round, it comes to test function, now rc is 1. longjmp transfer control to the setjmp in main function, it make it return 1.

3) third round, it comes to test function, now rc is 2, longjmp transfer control to the setjmp in main function, it make it return 2.
 ubuntu@ip-172-31-23-227:~$ ./proc.out  
 Hello world!  
 return code: 1  
 Hello world!  
 return code: 1  
 Hello world!  
 return code: 2  


No comments:

Post a Comment