0

just as a disclaimer this question has to do with a particular assignment I have an I am not asking for anyone to do my homework for me.

So basically I am supposed to implement a way to return directly to main from a function call of a function call.

Part of the stipulations are that we cannot use an assembly language instructions, gcc's asm(), or gcc built ins. After doing a lot of research on this on google I couldn't really find any examples to look at or even the source code for setjmp/longjmp (the purpose of this assignment is to copy those functionalities). I've asked some more older CS students for advice and most couldn't help or told me they are pretty sure it is not possible with the stipulations given. Any advice or pointers (haha) will be appreciated. Even a nudge in the right direction or confirmation that the assignment is not as complicated as I think will be greatly appreciated!

So far my best attempt:

-Use a setjmp function to store the address of where we left off in main (so something like x = foo1(); and pass x into setjmp(x)) and then have foo2 call my longjmp function where in longjmp I'll have the function set a pointer (*p) to my argument and then (*p-1) = address of x in main.

This didn't work but I thought it was the right idea trying change the return address in the call stack since if I understood it correctly, the arguments of the function are directly on top of the return address in the stack.

Here is the code I wrote:

int setjmp(int v);
int longjmp(int v);
int fun1(void); 
int fun2(void); 
int *add; //using global, not sure if best idea
int main(void)
{
    int x = setjmp(x);
    foo1();
    return 0;
}
int setjmp(int v)
{
  add = &v; //used a global variable
  return 0;
}
int longjmp(int v)
{
  int *p; //pointer
  p = &v; //save argument address
  *(p-1) = *add; //return address = address in main
  return 1;
}
int foo1(void)
{
    printf("hi1");
    foo2();
    printf("hi2");
    return 0;
}
int foo2(void)
{
    int a;      
    longjmp(a);
    return 0;
}//output SHOULD be "hi1"
 //output is currently "hi1" "hi2"

for what it's worth every line I have not commented was given as a skeleton and I cannot change it.

Excuse me in advance if something is off, I am quite new to C. Thank you.

Daniel
  • 21
  • 4
  • You might want to think about how to get the stack pointer to have the value you want it to have, rather than focussing on the return address. – rici May 17 '17 at 05:48
  • Continuing from the previous comment, you may want to look at [**Function Prologue and Epilogue in C**](http://stackoverflow.com/questions/14765406/function-prologue-and-epilogue-in-c) – David C. Rankin May 17 '17 at 07:07
  • This is impossible in standard C. C doesn't "know" anything about the representation of the call stack. For standard C calls on x86, it's impossible without assembler, because you need to manipulate `esp`/`ebp` and C has no notion of registers. It might be possible on some different architecture or with some different calling conventions where the stack pointer lives in a memory location. Of course this wouldn't be portable at all. –  May 17 '17 at 07:34
  • WHY you need this? – i486 May 17 '17 at 08:37
  • If you need to do this, (eg. to raise notice of errors or other 'abnormal' conditions up a stack of function calls), you are using the wrong language. Use a language that supports exceptions, eg. C++ – ThingyWotsit May 17 '17 at 09:20

2 Answers2

4

"So basically I am supposed to implement a way to return directly to main from a function call of a function call. "

This requirement is nonsense. Any attempt to sate that requirement will result in trash code. Training to write things like this is directly harmful practice. This is a very bad assignment and your teacher should be ashamed for teaching you bad practice with no disclaimer. There is also never a reason to do things like this in real-world programs.

You can't implement the setjmp/longjmp functions in pure C, you would have to use inline assembler. They save the program counter and other such things needed by the specific system. They also meddle with the stack pointer, which is one reason they are dangerous.

So the only way to do this in standard C is to use the standard library functions setjmp/longjmp from setjmp.h. These are widely considered very bad and dangerous since they lead to unreadable spaghetti programming and many forms of undefined behavior. One example of undefined behavior from the C standard:

After a longjmp, there is an attempt to access the value of an object of automatic storage duration that does not have volatile-qualified type, local to the function containing the invocation of the corresponding setjmp macro, that was changed between the setjmp invocation and longjmp call

Don't use these functions ever.


That being said, this is how you write horrible, dangerous programs:

// BAD! NEVER WRITE SPAGHETTI CODE LIKE THIS!

#include <stdio.h>
#include <setjmp.h>
#include <stdbool.h>

static jmp_buf jmp_main;

void func2 (bool one_more_time)
{
  puts(__func__);
  if(one_more_time)
  {
    longjmp(jmp_main, !one_more_time);
  }
  printf("end of "); puts(__func__);
}

void func1 (bool one_more_time)
{
  puts(__func__);
  func2(one_more_time);
  printf("end of "); puts(__func__);
}

int main (void)
{
  bool one_more_time = (bool)!setjmp(jmp_main);
  puts(__func__);  
  func1(one_more_time);
  printf("end of "); puts(__func__);
}

Output:

main
func1
func2
main
func1
func2
end of func2
end of func1
end of main
Lundin
  • 195,001
  • 40
  • 254
  • 396
0

I don't think problem is that complicated. You should be calling another function with a condition on returning value such that if it is certain value (say 0) then return otherwise continue on the function. So you can just replace foo2(); with :

if (foo2() == 0) return 0;

Now in foo2() function return 0; whenever you wish to return to main function call of foo1() otherwise continue with foo1() statements. You need not use setjmp here.

You may also find this article helpful: Tread programming examples

  • 1
    Welcome to StackOverflow and thanks for your attempt to help. As far as I understand your proposal, it is about changing the code around the call to `foo2();`. That code, as I understand the question, is part of the non-editable skeleton, which OP describes in the question. Hence I think your proposal does not solve the stated problem. Can you elaborate? – Yunnosch May 17 '17 at 05:39