||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|                                                                              |
|   $ there is no spoon                                                        |
|                                      ___           ___            ___        |
|                                     /\__\         |\__\          /\  \       |
|                                    /  |  |        | |  |        /  \  \      |
|                                   /   |  |        | |  |       / /\ \  \     |
|                                  / /| |__|__      | |__|___   _\ \ \ \  \    |
|                                 / / |___ \__\ ____/ ____\__\ /\ \ \ \ \__\   |
|                                 \/__/  / /  / \__  /  |      \ \ \ \ \/__/   |
|                                       / /  /     | |  |       \ \ \ \__\     |
|                                      / /  /      | |  |        \ \/ /  /     |
|                                     / /  /       | |  |         \  /  /      |
|                                     \/__/         \|__|          \/__/       |
|                                                                              |
|   ------------------------------------------------------------------------   |
|   code/backtracing C function calls                                          |
|   by mxs, March 01 2010 22:08:28                                             |
|   ------------------------------------------------------------------------   |
|   warning: code in this article isn't portable, it was tested on a           |
|   i386/32 bits machine, perhaps it won't work on yours. I'd be strongly      |
|   interested in seeing other behaviors of this on different                  |
|   architectures, if you have any experiences about it and time to waste,     |
|   feel free to contact me :-)                                                |
|                                                                              |
|   I'll try to explain here how debuggers can trace the list of last          |
|   function calls when a program crashes (gdb backtrace).                     |
|                                                                              |
|   By default (without -fomit-frame-pointer), assembly code generated by      |
|   GCC for a function includes a prolog/epilog, which can be summed up to :   |
|                                                                              |
|   func:                                                                      |
|                                                                              |
|     push    ebp         ; same previous frame pointer                        |
|     mov     ebp, esp    ; set the new frame pointer                          |
|                                                                              |
|     ; ... stuff here                                                         |
|                                                                              |
|     mov     esp, ebp   ; restore position in the stack                       |
|     pop     ebp        ; restore previous frame pointer                      |
|                                                                              |
|     ret                ; jump back where we were before the call             |
|                                                                              |
|   This allows a function to have its own stack with an automatic release     |
|   of the allocated resources. Here is a quick attempt to summerize the       |
|   state of the stack after a call of main() followed by a call to func() :   |
|                                                                              |
|                                                                              |
|                    high addrs                                                |
|                                                                              |
|              [  top of the stack  ]                                          |
|              [       ...          ]                                          |
|                                                                              |
|              ; call main(int, char **)                                       |
|                                                                              |
|              [   return address   ] ; pushed by call main                    |
|   0x1c00085d [   previous frame   ] ; pushed by the prolog of main           |
|       ^                                                                      |
|       |      ; here peacily live arguments and local variables of main()     |
|       |                                                                      |
|       |      [       ...          ]                                          |
|       |      [       ...          ]                                          |
|       |                                                                      |
|       |      ; func()                                                        |
|       |                                                                      |
|       |      [   return address   ] ; pushed by call func                    |
|       -----  [     0x1c00085d     ] ; pushed by the prolog of func           |
|                                                                              |
|              ; here peacily live local variables of func()                   |
|                                                                              |
|              [       ...          ]                                          |
|                                                                              |
|                     |   |                                                    |
|                     |   |                                                    |
|                     |   |                                                    |
|                    \     /                                                   |
|                     \   /                                                    |
|                       v                                                      |
|                                                                              |
|                   low addrs                                                  |
|                                                                              |
|   (yaw, that's ugly)                                                         |
|                                                                              |
|   When a call to func is made, the return address is pushed on               |
|   the stack, then a jump to the label is performed, and the function is      |
|   executed. So what we have on the stack before each frame can be summed     |
|   up with the following structure.                                           |
|                                                                              |
|   struct frame                                                               |
|   {                                                                          |
|     void *                previous_frame;                                    |
|     void *                return_addr;                                       |
|   } __attribute__((packed));                                                 |
|                                                                              |
|   I don't know if ((packed)) attribute is really relevant here, but I        |
|   prefer to be sure the compiler doesn't try to optimize this with           |
|   paddings.                                                                  |
|                                                                              |
|   This can be seen as a linked list, where we can navigate between each      |
|   return addresses, until we reach the main, and other stuff generated by    |
|   GCC.                                                                       |
|                                                                              |
|   Now we can dump return addresses until we reach the main:                  |
|                                                                              |
|   void                                                                       |
|   backtrace(void)                                                            |
|   {                                                                          |
|     int                   count;                                             |
|     struct frame *        current_frame;                                     |
|                                                                              |
|     __asm__("movl %%ebp, %0"                                                 |
|             : "=r" (current_frame));                                         |
|                                                                              |
|     count = 0;                                                               |
|     fprintf(stderr, "backtrace");                                            |
|     while (current_frame->return_addr < (void *) &main)                      |
|       {                                                                      |
|         fprintf(stderr, "%d\t- %p\n", count, current_frame->return_addr);    |
|         current_frame = current_frame->previous;                             |
|         ++count;                                                             |
|       }                                                                      |
|     fprintf(stderr, "end of backtrace (total=%d)\n", count);                 |
|     fflush(stderr);                                                          |
|   }                                                                          |
|                                                                              |
|   I haven't found any nicer way to stop tracing than comparing with          |
|   main's adress, that's ugly but works well if there's no library (as the    |
|   code section of libraries may be located in higher addresses).             |
|   Something interesting is that there are other calls performed before       |
|   arriving to the main, if you are curious about them, you know what         |
|   to do :-)                                                                  |
|                                                                              |
|   A call to backtrace() will dump something like:                            |
|                                                                              |
|   backtrace                                                                  |
|   0       - 0x1c00083c                                                       |
|   1       - 0x1c000849                                                       |
|   2       - 0x1c00085d                                                       |
|   3       - 0x1c000876                                                       |
|   end of backtrace (total=4)                                                 |
|                                                                              |
|   Now to have a little diagnostic of what happened after a crash, without    |
|   launching any debugger, you can redirect the signal to that function and   |
|   use a tool which will print informations about the return address          |
|   (man addr2line for more informations).                                     |
|                                                                              |
|   --- [download as html]                                                     |
|                                                                              |
| +--------------------------------------------------------------------------+ |
| + - quotes/Ummon and the gardener (February 22 2010 22:46:54)              + |
| + - sys/freebsd and jails (February 14 2010 19:29:43)                      + |
| + - randoms/buffout is out (February 12 2010 15:15:28)                     + |
| +--------------------------------------------------------------------------+ |
|                                                                              |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||