How Does The Debugging Option -g Change the Binary Executable?

4,082

Solution 1

-g tells the compiler to store symbol table information in the executable. Among other things, this includes:

  • symbol names
  • type info for symbols
  • files and line numbers where the symbols came from

Debuggers use this information to output meaningful names for symbols and to associate instructions with particular lines in the source.

For some compilers, supplying -g will disable certain optimizations. For example, icc sets the default optimization level to -O0 with -g unless you explicitly indicate -O[123]. Also, even if you do supply -O[123], optimizations that prevent stack tracing will still be disabled (e.g. stripping frame pointers from stack frames. This has only a minor effect on performance).

With some compilers, -g will disable optimizations that can confuse where symbols came from (instruction reordering, loop unrolling, inlining etc). If you want to debug with optimization, you can use -g3 with gcc to get around some of this. Extra debug info will be included about macros, expansions, and functions that may have been inlined. This can allow debuggers and performance tools to map optimized code to the original source, but it's best effort. Some optimizations really mangle the code.

For more info, take a look at DWARF, the debugging format originally designed to go along with ELF (the binary format for Linux and other OS's).

Solution 2

A symbol table is added to the executable which maps function/variable names to data locations, so that debuggers can report back meaningful information, rather than just pointers. This doesn't effect the speed of your program, and you can remove the symbol table with the 'strip' command.

Solution 3

In addition to the debugging and symbol information
Google DWARF (A Developer joke on ELF)

By default most compiler optimizations are turned off when debugging is enabled.
So the code is the pure translation of the source into Machine Code rather than the result of many highly specialized transformations that are applied to release binaries.

But the most important difference (in my opinion)
Memory in Debug builds is usually initialized to some compiler specific values to facilitate debugging. In release builds memory is not initialized unless explicitly done so by the application code.

Check your compiler documentation for more information:
But an example for DevStudio is:

  • 0xCDCDCDCD Allocated in heap, but not initialized
  • 0xDDDDDDDD Released heap memory.
  • 0xFDFDFDFD "NoMansLand" fences automatically placed at boundary of heap memory. Should never be overwritten. If you do overwrite one, you're probably walking off the end of an array.
  • 0xCCCCCCCC Allocated on stack, but not initialized

Solution 4

-g adds debugging information in the executable, such as the names of variables, the names of functions, and line numbers. This allows a debugger, such as gdb to step through code line by line, set breakpoints, and inspect the values of variables. Because of this additional information using -g increases the size of the executable.

Also, gcc allows to use -g together with -O flags, which turn on optimization. Debugging an optimized executable can be very tricky, because variables may be optimized away, or instructions may be executed in a different order. Generally, it is a good idea to turn off optimization when using -g, even though it results in much slower code.

Solution 5

Just as a matter of interest, you can crack open a hexeditor and take a look at an executable produced with -g and one without. You can see the symbols and things that are added. It may change the assembly (-S) too, but I'm not sure.

Share:
4,082

Related videos on Youtube

Scott Barnes
Author by

Scott Barnes

Updated on July 05, 2022

Comments

  • Scott Barnes
    Scott Barnes almost 2 years

    I'm trying to find a way to extract a HashMap from a private static field within another class via Java.

    eg.

    Inside FooClass there is a static field that looks like this:

    private Map entityRenderMap;
    

    Then in its construct it has:

    entityRenderMap = new HashMap();

    How do you get the values within entityRenderMap via Reflection in Java? I've tried this but get errors:

    cl = RenderManager.class.getDeclaredField("entityRenderMap");
            cl.setAccessible(true);
            Object foo = cl.get(this.entityRenderMap);
            Mod.log(cl.getName());
    

    The error I get is:

    java.lang.IllegalArgumentException: Can not set java.util.Map field RenderManager.entityRenderMap to java.util.HashMap
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(Unknown Source)
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(Unknown Source)
    at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(Unknown Source)
    at sun.reflect.UnsafeObjectFieldAccessorImpl.get(Unknown Source
    
    • simpatico
      simpatico over 12 years
      your entityRenderMap is not static.
    • JB Nizet
      JB Nizet over 12 years
      You say that the field is static, but the declaration shows that it's not. And what is the type of this.entityRenderMap that is passed to cl.get()?
    • Scott Barnes
      Scott Barnes over 12 years
      I just realised that the field is not static after all.... I totally screwed the pooch then.. (long night).
  • Mike
    Mike over 15 years
    Just to add to this, it can also slow down the executable. I was testing some OpenMP code with the Sun Studio compiler, and with debugging information the code ran much slower. Just something to keep in mind.
  • Todd Gamblin
    Todd Gamblin over 15 years
    Unless the -g flag in the Sun compiler disables some optimizations, debug info should NOT slow down your code.
  • Mike
    Mike over 15 years
    This is OpenMP code, and it did slow it down. I was playing with fractals, and working on using the OpenMP compiler extensions. The code on a single thread, ran slower than the non OpenMP code on a single thread. I disabled debugging and the speed equalised.
  • Todd Gamblin
    Todd Gamblin over 15 years
    Noted. That's actually kind of interesting. Maybe it's putting extra stuff in there to tell the debugger about parallel regions... They say here (docs.sun.com/source/819-3683/OpenMP.html) that you can get map the master thread back to source but not slaves, which seems odd, too.
  • Mike
    Mike over 15 years
    I think that's the case, doesn't affect GCC of course, certainly gave me a surprise when the single thread code went from 11secs to 22. :/ With debugging disabled and 4 threads (I have a Q6600) it dropped to about 3 secs.
  • Todd Gamblin
    Todd Gamblin over 15 years
    gcc4 actually supports OpenMP, so it's possible you'd see similar issues there. I hear the performance isn't that good to begin with, though.
  • Todd Gamblin
    Todd Gamblin over 15 years
    Just out of curiosity, did you supply additional optimization options when you compiled with -g (e.g. -g -O3) or did you just add -g without explicitly specifying -O[123]? The former could drop you to -O0, at least on icc.
  • Mike
    Mike over 15 years
    I did have the project setup for maximum optimisation (with debugging), SSE, MMX, etc, when I get home I'll post the exact options, maybe there's something I missed.
  • Mike
    Mike over 15 years
    Ok, using -# to list what -fast expands to: cc -xopenmp -fast -# Expands to: -D__MATHERR_ERRNO_DONTCARE -fns -nofstore -fsimple=2 -fsingle -xalias_level=basic -xarch=ssse3 -xbuiltin=%all -xcache=32/64/8:4096/64/16 -xchip=core2 -xdepend -xlibmil -xlibmopt -xO5 -xopenmp -xregs=frameptr
  • Mike
    Mike over 15 years
    The -g flag is included as well, comments just don't give me the space to post the full line. :) Debug version: real time: 6.060s user time: 22.815s Release version: 3.774s user time: 13.902s (Both using 4 threads)
  • Scott Barnes
    Scott Barnes over 12 years
    Yes sorry it wasnt static after all for some reason (fatigue maybe) i was running my head into a wall on "Why wont this work!" only to realise it was a human bug not software :)