1

How a stack backtrace can be implemented when the compiler is explicitly told not to use ebp as stack frame pointer?

balajimc55
  • 2,192
  • 2
  • 13
  • 15
  • 1
    It depends on the format, but some compilers and linkers output debug information (like gcc/ld using dwarf) that can be used to create backtraces in the absence of conventional frame pointers. I think other paltforms compilers/linkers do similar things. – Michael Petch Dec 03 '15 at 00:52

1 Answers1

4

The answer to this was only ever in comments on the accepted answer on What is the purpose of the EBP frame pointer register?.

Modern debuggers can do stack backtraces even in code compiled with -fomit-frame-pointer. That setting is the default in recent gcc.

gcc puts the necessary stack-unwind info into a .eh_frame_hdr section. See this blog post for more details. It's used for runtime exceptions, too. You'll find it (with objdump -h) in most binaries on a Linux system. It's about 16k for /bin/bash, vs. 572B for GNU /bin/true, 108k for ffmpeg.

There is a gcc option to disable generating it, but it's a "normal" data section, not a debug section that strip removes by default. Otherwise you couldn't backtrace through a library function that didn't have debug symbols. That section may be bigger than the push/mov/pop instructions it replaces, but it has near zero runtime cost (e.g. uop cache).


I think the info stored in that section is a mapping from return-address to size of stack frame. Since every call instruction pushes the address of the following instruction onto the stack, you can identify the parent caller from that address. Instead of pushing ebp to make a linked list of stack frames on the stack, the offset to the next return address is stored in the .eh_frame_hdr section, so it can be used if needed by code that needs to backtrace.

Community
  • 1
  • 1
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • Can a debugger attached to a running program (that was compiled with -fomit-frame-pointer) perform the backtrace using the .eh_frame_hdr? Should the program be compiled with debug symbols for doing so? – balajimc55 Dec 06 '15 at 00:31
  • 1
    @balajimc55: Given a PID, a debugger can find the executable to get access to the `.eh_frame_hdr` section, just as easily as it can find the executable to find the debug symbols. If you are actually going to debug, you should compile with debug symbols. A backtrace without symbols correctly identifies the stack frames and how big they are, but not the names of the functions or the parameters. So without debug symbols you can produce a correct backtrace, but not a useful one. – Peter Cordes Dec 06 '15 at 01:54
  • Than you! And I couldn't find much about the .eh_frame_hdr, could you please point me to some resources for learning more about how the debugger uses this section to do a backtrace? – balajimc55 Dec 06 '15 at 01:57
  • @balajimc55: Everything I found was just research out of curiosity. I don't know any details. If google searches don't turn up anything about what's in `.eh_frame_hdr`, then check the gdb source code. Hopefully it has some comments to explain the overall process without having to actually follow a ton of logic. – Peter Cordes Dec 06 '15 at 03:18
  • Thanks a lot Peter :) – balajimc55 Dec 06 '15 at 04:04
  • @PeterCordes so it uses the unwind info in the image to traverse the stack if there is no base pointer and then debugging symbols will actually show you the symbols that the addresses correspond to. I would think that if there is no base pointer chain, and the image isn't SEH enabled, i.e. has no exception directory on windows, the debugger wont be able to identify stack frames for the image? – Lewis Kelsey Mar 27 '21 at 00:56
  • @LewisKelsey: IDK what Windows does, but on GNU/Linux GDB (and C++ exception handling) always uses `.en_frame` if available. I think GDB can fall back to an EBP chain if it doesn't find `.eh_frame` metadata from `.cfi_*` directives. For functions that did choose to use use EBP as a frame pointer, normally that just simplifies / compacts the `.eh_frame` metadata because there's a fixed reference point that isn't moving around with push/pop. So the compiler can just use one .cfi directive to specify where call-preserved regs are saved relative to that frame pointer. – Peter Cordes Mar 27 '21 at 01:36
  • @PeterCordes on windows I've seen `IMAGE_DLLCHARACTERISTICS_NO_SEH` but I'm also reading that an exception directory with unwind codes for every function is mandatory for a x64 PE image. If there isn't an exception directory then the exception handler would have to terminate the process because it won't be able to get the to base SEH handler. I may have to practically experiment myself – Lewis Kelsey Mar 27 '21 at 14:33