In a sequence of function calls, such as
main() --> A() --> B()-->C();
when a called function finishes, it normally returns to the calling function, e.g. C() returns to B(), which returns to A(), etc.
i'm wondering if it is also possible to return directly to an earlier function in the calling sequence
so C() return to main() and skip B() and A()
if it is possible how can i do it ? and can you explain how it work and when it used in reality.
this is my code
#include <stdio.h>
int A(void);
int B(void);
void main(void )
{
A();
}
int A()
{
printf("enter A()\n");
B();
printf("exit A()\n");
}
int B()
{
printf("enter B()\n");
printf("exit B()\n");
}
and i want to skip return to function A() from B() so that the printf("exit A()\n"); will not execute and give me this result:
enter A()
enter B()
exit B()
before everything check out #PeterCordes is answers the question in more than a way
okey let's start :
this type of stuff can be done using something known as long jump so your code after editing will look like this :
#include <stdio.h>
#include <setjmp.h>//c standard library header
jmp_buf env; // for saving longjmp environment
main()
{
int r, a=100;
printf("call setjmp to save environment\n");
if ((r=setjmp(env)) == 0){
A();
printf("normal return\n");
}
else
printf("back to main() via long jump, r=%d a=%d\n", r, a);
}
int A()
{
printf("enter A()\n");
B();
printf("exit A()\n");
}
int B()
{
printf("enter B()\n");
printf("long jump? (y|n) ");
if (getchar()=='y')
longjmp(env, 1234);
printf("exit B()\n");
}
let's understand what just happened
In the above program, setjmp() saves the current execution environment in a jmp_buf
structure and returns 0.
The program proceeds to call A(), which calls B().
While
in the function B(), if the user chooses not to return by long jump, the functions will
show the normal return sequence.
If the user chooses to return by longjmp(env,1234),
execution will return to the last saved environment with a nonzero value.
In
this case, it causes B() to return to main() directly, bypassing A().
The principle of
long jump is very simple. When a function finishes, it returns by the
(caller_EIP, caller_EBP)
in the current stack frame,
If we replace (caller_EIP, caller_EBP) with
(saved_EIP, saved_EBP) of an earlier function
in the calling sequence, execution would return to that function directly.
In addition
to the (saved_EIP, saved_EBP), setjmp() may also save CPU’s general registers and the
original ESP, so that longjmp() can restore the complete environment of the returned
function.
Long jump can be used to abort a function in a calling sequence, causing
execution to resume from a known environment saved earlier.
Although rarely used
in user mode programs, it is a common technique in systems programming.
For example,
it may be used in a signal catcher to bypass a user mode function that caused
an exception or trap error.
you can check this is also good
Related
I want to use setjmp/longjmp to reuse some code inside the main function (NOTE: this is only an exercise and not something I ever seriously plan on doing in the real world).
The following code is what I've came up with:
#include <stdio.h>
#include <setjmp.h>
jmp_buf jmp_body, jmp_ret;
void func(void)
{
if (setjmp(jmp_ret) == 0)
longjmp(jmp_body, 1);
}
int main()
{
int x = 0;
if (setjmp(jmp_body) == 1) {
printf("Body %d\n", ++x);
longjmp(jmp_ret, 1);
}
func();
func();
func();
return 0;
}
The way I expected this code to work is the following:
The main() function is going to remember where the 'body' part is and skip it using if (setjmp(jmp_body) == 1).
The func() call is going to temporarily jump to the body using longjmp(jmp_body) after remembering where the body is supposed to return using if (setjmp(jmp_ret) == 0)
The body is going to execute and jump back to the func() call using longjmp(jmp_ret, 1)
The func() is just going to return to main() as expected.
Therefore, what I expected the code to print is the following:
Body 1
Body 2
Body 3
Instead, it loops forever continually executing the body which indicates to me the func() call isn't returning where it's supposed to and instead might be returning above itself executing itself over and over again.
In comparison, the following code prints just what I expected:
#include <stdio.h>
#include <setjmp.h>
jmp_buf jmp_body, jmp_ret;
int main()
{
int x = 0;
if (setjmp(jmp_body) == 1) {
printf("Body %d\n", ++x);
longjmp(jmp_ret, 1);
}
if (setjmp(jmp_ret) == 0)
longjmp(jmp_body, 1);
if (setjmp(jmp_ret) == 0)
longjmp(jmp_body, 1);
if (setjmp(jmp_ret) == 0)
longjmp(jmp_body, 1);
return 0;
}
What is it about putting if (setjmp(jmp_ret) == 0) longjmp(jmp_body, 1) inside a function call that makes the original approach invalid?
TL/DR - you can't jump back into a function you jumped out of.
7.13.2.1 The longjmp function
...
2 The longjmp function restores the environment saved by the most recent invocation of
the setjmp macro in the same invocation of the program with the corresponding
jmp_buf argument. If there has been no such invocation, or if the invocation was from
another thread of execution, or if the function containing the invocation of the setjmp
macro has terminated execution248) in the interim, or if the invocation of the setjmp
macro was within the scope of an identifier with variably modified type and execution has
left that scope in the interim, the behavior is undefined.
248) For example, by executing a return statement or because another longjmp call has caused a
transfer to a setjmp invocation in a function earlier in the set of nested calls.
C 2011 Online Draft
When you execute longjmp(jump_body, 1); in func, you invalidate jump_ret.
longjmp isn't bidirectional - it unwinds the stack as though any of the function calls between the setjmp and longjmp never happened.
You tried to longjmp back down the stack from main() to func(). This is not defined. Since longjmp is itself a function, you most likely ended up longjmping into func with the return address being the longjmp call itself thus an infinite loop.
#include <setjmp.h>
#include <stdio.h>
jmp_buf jmp;
int test() {
setjmp(jmp);
puts("Birds are crying");
return 0;
}
int main() {
longjmp(jmp,1);
return 0;
}
Above code doesn't work and crashes, why?
I am using the GCC compiler on Windows 10 Pro.
Your usage of longjmp() is invalid, take a look at the documentation:
longjmp() restores the environment saved by the last call of setjmp()
You need to call setjmp() first in order to "set" where to jump before calling longjump(). That's why your code doesn't work. It's undefined behavior at best.
In other words, you cannot just use longjmp() as a simple "jump to global label". It has a different purpose.
after putting test(); call before the line longjmp it works and print it twice but why does it still crashes?
Let's again take a look at the documentation:
setjmp() saves the stack context/environment in env for later use by longjmp(). The stack context will be invalidated if the function which called setjmp() returns.
You cannot longjmp() to a buffer that was previously set by a function that has now returned. It's invalid. Your usage of setjmp()/longjmp() is really not what those functions are meant for.
Jumping out to another function isn't really the purpose of setjmp()/longjmp(), their purpose is to save the context and "jump back" to a function that has still not returned (see below example). It's not simple to come up with a meaningful example usage of such functions, since they are meant for advanced usage.
Here's a correct usage example (while still not that meaningful) from Wikipedia:
#include <stdio.h>
#include <setjmp.h>
static jmp_buf buf;
void second() {
printf("second\n"); // prints
longjmp(buf,1); // jumps back to where setjmp was called - making setjmp now return 1
}
void first() {
second();
printf("first\n"); // does not print
}
int main() {
if (!setjmp(buf))
first(); // when executed, setjmp returned 0
else // when longjmp jumps back, setjmp returns 1
printf("main\n"); // prints
return 0;
}
I do not understand why in the function middleFunc(), a segmentation fault is raisen when entry_point(arg) is invoked inside the if ( setjmp(middle) ) statement.
#include <stdio.h>
#include <setjmp.h>
jmp_buf start,middle,end;
void finalFunc(void *v)
{
printf("hello\n");
return ;
}
void middleFunc(void (*entry_point)(void *), void *arg)
{
//just debug : this does not cause segmentation fault
entry_point(arg);
if ( setjmp(middle) ){
//this casues the segmentation fault
entry_point(arg);
//once the entry point (finalFunc) is executed go to jmp_buffer end
longjmp(end,1);
}
else {
longjmp(start,1);
}
}
int main(){
if (setjmp(end)){
//exit since finalFunc has been executed
return 0;
}
if (setjmp(start)){
//the middleFunc has previously set the jmp_buffer middle
longjmp(middle,1);
}
else{
int x = 1;
middleFunc(finalFunc,(void*)&x);
}
}
In your code the behavior is undefined. You are not allowed to long-jump to middle after middleFunc finished execution (either by normal completion or by another longjmp).
7.13.2.1 The longjmp function
2 The longjmp function restores the environment saved by the most recent invocation of the setjmp macro in the same invocation of the program with the corresponding jmp_buf argument. If there has been no such invocation, [...] or if the function containing the invocation of the setjmp macro has terminated execution248) in the interim [...] the behavior is undefined.
248) For example, by executing a return statement or because another longjmp call has caused a transfer to a setjmp invocation in a function earlier in the set of nested calls.
In your code middleFunc sets up middle and after that immediately exits to main by doing longjmp(start,1). After that jump middle is no longer valid. You are no longer allowed to jump to middle from anywhere. setjmp/longjmp mechanism only supports jumps up the call stack. You cannot do side-jumps or down-jumps. Only up-jumps are supported.
From the practical point of view, you are attempting to jump into a "dead" function invocation and somehow expecting that function parameter values are still valid (like, preserved from the previous invocation or something). But they are not. setjmp/longjmp do not preserve/restore parameter values. Value of entry_point in that "dead" invocation is probably some garbage. When you attempt to make a call through entry_point, the code coredumps.
P.S. It is true that side-jumping with setjmp/longjmp is sometimes used to implement co-routines. However, such usage falls outside the boundaries of standard library specification. And in any case such usage will never expect preservation of parameter values.
I'm trying to implement reinversion of control in c using longjmp, currently i have this code:
#include <stdio.h>
#include <setjmp.h>
jmp_buf env;
int arg;
typedef void (*fptr)(int);
fptr callback;
void cb(int a)
{
arg = a;
longjmp(env, 1);
}
#define cont1(f, x) do { if(!setjmp(env)) { f(x, &cb); return; } } while(0)
void callback_func(int num, fptr cb)
{
printf("in a func, num = %d\n", num);
callback = cb;
}
void task1()
{
printf("before continuation\n");
cont1(callback_func, 7);
printf("after continuation\n");
}
void task2()
{
printf("in thread 2\n");
(*callback)(5);
}
int main()
{
task1();
task2();
printf("arg = %d\n", arg);
return 0;
}
And my question is: doesn't this invoke undefined behavior or could cause any problem in real world use and if yes, then is there any better way to do this?
This program has undefined behavior. See C11 7.13.2.1 The longjmp function (emphasis mine):
If there has been no such invocation, or if the invocation was from another thread of execution, or if the function containing the invocation of the setjmp macro has terminated execution248 in the interim, or if the invocation of the setjmp macro was within the scope of an identifier with variably modified type and execution has left that scope in the interim, the behavior is undefined.
248) For example, by executing a return statement or because another longjmp call has caused a transfer to a setjmp invocation in a function earlier in the set of nested calls.
At the time you cb calls longjmp, the function which invoked setjmp to fill env, task1, has returned. Thus calling longjmp is undefined.
The C language has no means to do what you're trying to do except for (now with C11) threads, using condition variables to control which thread runs.
What is difference between return and exit statement in C programming when called from anywhere in a C program?
return returns from the current function; it's a language keyword like for or break.
exit() terminates the whole program, wherever you call it from. (After flushing stdio buffers and so on).
The only case when both do (nearly) the same thing is in the main() function, as a return from main performs an exit().
In most C implementations, main is a real function called by some startup code that does something like int ret = main(argc, argv); exit(ret);. The C standard guarantees that something equivalent to this happens if main returns, however the implementation handles it.
Example with return:
#include <stdio.h>
void f(){
printf("Executing f\n");
return;
}
int main(){
f();
printf("Back from f\n");
}
If you execute this program it prints:
Executing f
Back from f
Another example for exit():
#include <stdio.h>
#include <stdlib.h>
void f(){
printf("Executing f\n");
exit(0);
}
int main(){
f();
printf("Back from f\n");
}
If you execute this program it prints:
Executing f
You never get "Back from f". Also notice the #include <stdlib.h> necessary to call the library function exit().
Also notice that the parameter of exit() is an integer (it's the return status of the process that the launcher process can get; the conventional usage is 0 for success or any other value for an error).
The parameter of the return statement is whatever the return type of the function is. If the function returns void, you can omit the return at the end of the function.
Last point, exit() come in two flavors _exit() and exit(). The difference between the forms is that exit() (and return from main) calls functions registered using atexit() or on_exit() before really terminating the process while _exit() (from #include <unistd.h>, or its synonymous _Exit from #include <stdlib.h>) terminates the process immediately.
Now there are also issues that are specific to C++.
C++ performs much more work than C when it is exiting from functions (return-ing). Specifically it calls destructors of local objects going out of scope. In most cases programmers won't care much of the state of a program after the processus stopped, hence it wouldn't make much difference: allocated memory will be freed, file ressource closed and so on. But it may matter if your destructor performs IOs. For instance automatic C++ OStream locally created won't be flushed on a call to exit and you may lose some unflushed data (on the other hand static OStream will be flushed).
This won't happen if you are using the good old C FILE* streams. These will be flushed on exit(). Actually, the rule is the same that for registered exit functions, FILE* will be flushed on all normal terminations, which includes exit(), but not calls to _exit() or abort().
You should also keep in mind that C++ provide a third way to get out of a function: throwing an exception. This way of going out of a function will call destructor. If it is not catched anywhere in the chain of callers, the exception can go up to the main() function and terminate the process.
Destructors of static C++ objects (globals) will be called if you call either return from main() or exit() anywhere in your program. They wont be called if the program is terminated using _exit() or abort(). abort() is mostly useful in debug mode with the purpose to immediately stop the program and get a stack trace (for post mortem analysis). It is usually hidden behind the assert() macro only active in debug mode.
When is exit() useful ?
exit() means you want to immediately stops the current process. It can be of some use for error management when we encounter some kind of irrecoverable issue that won't allow for your code to do anything useful anymore. It is often handy when the control flow is complicated and error codes has to be propagated all way up. But be aware that this is bad coding practice. Silently ending the process is in most case the worse behavior and actual error management should be preferred (or in C++ using exceptions).
Direct calls to exit() are especially bad if done in libraries as it will doom the library user and it should be a library user's choice to implement some kind of error recovery or not. If you want an example of why calling exit() from a library is bad, it leads for instance people to ask this question.
There is an undisputed legitimate use of exit() as the way to end a child process started by fork() on Operating Systems supporting it. Going back to the code before fork() is usually a bad idea. This is the rationale explaining why functions of the exec() family will never return to the caller.
I wrote two programs:
int main(){return 0;}
and
#include <stdlib.h>
int main(){exit(0)}
After executing gcc -S -O1. Here what I found watching
at assembly (only important parts):
main:
movl $0, %eax /* setting return value */
ret /* return from main */
and
main:
subq $8, %rsp /* reserving some space */
movl $0, %edi /* setting return value */
call exit /* calling exit function */
/* magic and machine specific wizardry after this call */
So my conclusion is: use return when you can, and exit() when you need.
In C, there's not much difference when used in the startup function of the program (which can be main(), wmain(), _tmain() or the default name used by your compiler).
If you return in main(), control goes back to the _start() function in the C library which originally started your program, which then calls exit() anyways. So it really doesn't matter which one you use.
For the most part, there is no difference in a C program between using return and calling exit() to terminate main().
The time when there is a difference is if you've created code that will be executed after you return from main() that relies on variables local to main(). One way that manifests itself is with setvbuf():
int main(void)
{
char buffer[BUFSIZ];
setvbuf(stdout, buffer, _IOFBF, BUFSIZ);
…code using stdout…
return 0;
}
In this example, the buffer provided via setvbuf() goes out of scope when main() returns, but the code that flushes and closes stdout will be attempting to use that buffer. This leads to undefined behaviour.
The other mechanism is to invoke atexit() with a function that accesses data from main() — via a pointer. This is harder to set up as the functions called via the atexit() mechanism are not given any arguments. So, you have to do something like this:
static void *at_exit_data = 0;
static void at_exit_handler(void)
{
char *str = at_exit_data;
printf("Exiting: %s\n", str);
}
int main(void);
{
char buffer[] = "Message to be printed via functions registered with at_exit()";
at_exit_data = buffer;
at_exit(at_exit_handler);
…processing…
return 0;
}
Again, the buffer pointed at by at_exit_data has ceased to exist when the program returned from main() and so the handler function invokes undefined behaviour.
There is a related function, at_quick_exit(), but the functions registered with it are only called if the quick_exit() function is called, which precludes the functions being called after main() returns.
the return statement exits from the current function and exit() exits from the program
they are the same when used in main() function
also return is a statement while exit() is a function which requires stdlb.h header file