2

I have this homework assignment where I am supposed to write a c program that displays arguments passed to previous functions in the call stack to the console. I have this piece of code:

struct stack_frame
{
  struct stack_frame* next;
};

typedef struct stack_frame stack_frame;

void recover()
{
   register stack_frame* sfptr __asm__("ebp");
   stack_frame * frame = sfptr;

   traverse_stack(frame, show_bytes);
}

Several recursive calls to a function foo(int, short, char) occur before the call to recover(), which initiates a traversal of the stack, resulting in the output of the arguments to all previous calls to foo.

When I compile this with gcc 4.8.2 without optimization, it runs perfectly. With -O1 it breaks because frame gets assigned an unreachable address. 0xffffffd9. I believe this is because the asm is getting optimized out.

I have tried many other ways of getting the value of the ebp register, including

stack_frame frame;
asm volatile("movl %%ebp, %0\n" : "=r"(frame->next));

and many others. I have tried adding clobbers. I have also tried designating frame as volatile. I have already reviewed many SO posts on this subject, including [question]: gcc removes inline assembler code and others. No matter what I have tried, I am the king of SegFault City.

The segfault occurs here:

void traverse_stack(stack_frame* frame, byte_processor show_bytes)
{
  if (frame->next != NULL)  // segfault here!!!!!!!
    traverse_stack(frame, show_bytes);

  show_args(frame, show_bytes);
}

show_bytes is just a function pointer to one of two functions that will display the bytes in hex for either big or little endian systems.

Anyway, I have to compile with -m32. I do not have the option of supplying any additional arguments.

Any help is greatly appreciated. Also, I am a bit of a newb, so if at all possible, please be gentle with your responses. Thanks in advance.

Community
  • 1
  • 1
  • I am not sure that stack frames always start by their previous frame pointer. – Basile Starynkevitch Feb 24 '15 at 07:53
  • 1
    You want [__builtin_frame_address](https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html) – Basile Starynkevitch Feb 24 '15 at 07:54
  • 2
    Just to make sure, you *are* actually disassembling the generated code, and single-stepping in a debugger at the assembly level when testing this, right? I just get the feeling you're simply compiling and running it and watching for the segfault, which would be like trying to cook an omelette by setting fire to a hen house. Or some other colorful metaphor. – unwind Feb 24 '15 at 07:57
  • 1
    -fno-omit-frame-pointer? – Marc Glisse Feb 24 '15 at 08:04
  • @unwind: I regret to say that I am indeed trying to cook the omelette. If I stepped thru at the assembly level, I would have a difficult time deciphering what I was looking at. I am single stepping at the c level. – Nathan Milton Feb 24 '15 at 08:18
  • @BasileStarynkevitch I tried __builtin_frame_address as you suggested. This time, instead of getting a bunk address, I got one frame up through the traversal, and then bunk. I know that there are 13 frames before recover(). I'm not sure what is happening... – Nathan Milton Feb 24 '15 at 08:26
  • BTW, compile your code with `gcc -O1 -fverbose-asm -S` then look inside the generated `.s` assembler file. – Basile Starynkevitch Feb 24 '15 at 08:31
  • @MarcGlisse That fixed my problem. Is there another way to prevent the frame pointer from being omitted. I will not be the one compiling my program. Some TA I've never met will be doing it. Is it possible to specify in the code that I need the frame pointer? Pre-processor directive? – Nathan Milton Feb 24 '15 at 09:08
  • @NathanMilton `void myfunc() __attribute__ ((optimize("no-omit-frame-pointer")));` or `#pragma GCC optimize("no-omit-frame-pointer")` – Matt Feb 24 '15 at 09:22
  • @user4419802 Sorry for my ongoing ignorance. Are the attribute or pragma options supposed to work if I compile with -O1? So far, the only thing that is working is compiling with -fno-omit-frame-pointer, but as I said earlier, I will not have the option to specify arguments to the compiler. – Nathan Milton Feb 24 '15 at 10:21
  • @NathanMilton They should work as expected. BTW. It's possible to re-define `-On` flag directly in the source, using `#pragma GCC optimization_level n`. – Matt Feb 24 '15 at 11:12
  • @user4419802 I tried #pragma GCC optimization_level n as well, but got an error saying that "optimization_level" is an unrecognized pragma. I'm compiling with gcc 4.8.2 on Ubuntu. You guys chimed in with all kinds of great help, but for some reason it just isn't working unless I compile without optimization. Frustrating. Thanks to everybody for your input... – Nathan Milton Feb 24 '15 at 11:48
  • @NathanMilton And what about `#pragma GCC optimize("O0")`? Is the target X86 (32-bit)? – Matt Feb 24 '15 at 11:56
  • Note that you will want to be sure that *all* your sources are compiled with the `-fomit-frame-pointer` optimization disabled (i.e. not just the one containing function `recover()`). You may also need to ensure that the `-foptimize-sibling-calls` optimization and all the `-finline-*` optimizations are disabled, though most of those are not included in `-O1`. – John Bollinger Feb 24 '15 at 16:12
  • I'm inclined to suppose that the point of the homework is *not* to struggle with GCC-specific pragmas for control over optimization. The best solution is probably to compile with -O0, and if you think that's not permitted to you then I'd advise you to consult your instructor about the issue. – John Bollinger Feb 24 '15 at 16:15

0 Answers0