In many space-restricted environments it is necessary to avoid including symbols in the released binaries, making debugging more complex. Even though many problems can be replicated by using a separate debug build, not all of them can, forcing the use of map files and cumbersome procedures. In this post we will see how to use separate debug info when building with GCC.
Generating a test binary
To avoid using third-party source code, we can produce our own big source file programatically:
import sys print 'int main(void)\n{\n unsigned x0 = 1234;' for i in range(int(sys.argv[1])): j = i + 1 print ' unsigned x%(j)d = x%(i)d * %(j)d * %(j)d;' % locals() print ' return (int)x%(j)d;\n}' % locals()
Generating, compiling and stripping the binary (the compilation takes quite long in this netbook),
$ python bigcgen.py 50000 >a.c $ time gcc -g3 -std=c99 -Wall -pedantic a.c -o a-with-dbg real 1m3.559s user 1m2.032s sys 0m0.912s $ cp a-with-dbg a-stripped && strip --strip-all a-stripped bash: syntax error near unexpected token `;&' $ cp a-with-dbg a-stripped && strip --strip-all a-stripped $ ls -l total 5252 -rw-rw-r-- 1 mchouza mchouza 2255639 Jun 4 20:37 a.c -rwxrwxr-x 1 mchouza mchouza 907392 Jun 4 20:44 a-stripped -rwxrwxr-x 1 mchouza mchouza 2204641 Jun 4 20:38 a-with-dbg -rw-rw-r-- 1 mchouza mchouza 225 Jun 4 20:33 bigcgen.py
we can see there are substantial space savings by removing extra information from the binary, at the cost of being unable to debug it:
mchouza@nbmchouza:~/Desktop/release-debug-exp$ gdb ./a-with-dbg GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1 [...] Reading symbols from ./a-with-dbg...done. (gdb) q $ gdb ./a-stripped GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1 Copyright (C) 2014 Free Software Foundation, Inc. [...] Reading symbols from ./a-stripped...(no debugging symbols found)...done. (gdb) q
Separating the debug info and linking it
We can use objcopy to get a copy of the debug information and then to link it back to the original file.
$ cp a-with-dbg a-stripped-dbg $ objcopy --only-keep-debug a-stripped-dbg a-stripped-dbg.dbg $ strip --strip-all a-stripped-dbg $ objcopy --add-gnu-debuglink=a-stripped-dbg.dbg a-stripped-dbg $ ls -l total 7412 -rw-rw-r-- 1 mchouza mchouza 2255639 Jun 4 20:37 a.c -rwxrwxr-x 1 mchouza mchouza 907392 Jun 4 20:44 a-stripped -rwxrwxr-x 1 mchouza mchouza 907496 Jun 4 20:46 a-stripped-dbg -rwxrwxr-x 1 mchouza mchouza 1300033 Jun 4 20:46 a-stripped-dbg.dbg -rwxrwxr-x 1 mchouza mchouza 2204641 Jun 4 20:38 a-with-dbg -rw-rw-r-- 1 mchouza mchouza 225 Jun 4 20:33 bigcgen.py
The file size is slightly bigger, but now we can debug normally:
$ gdb ./a-stripped-dbg GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1 [...] Reading symbols from ./a-stripped-dbg...Reading symbols from /home/mchouza/Desktop/mchouza/stripped-bins-dbg/a-stripped-dbg.dbg...done. done. (gdb) b 4567 Breakpoint 1 at 0x4145e2: file a.c, line 4567. (gdb) r Starting program: /home/mchouza/Desktop/mchouza/stripped-bins-dbg/a-stripped-dbg Breakpoint 1, main () at a.c:4567 4567 unsigned x4564 = x4563 * 4564 * 4564; (gdb) c Continuing. [Inferior 1 (process 19304) exited normally]
Limitations and a question
Of course, when applied to optimized code it can be hard to debug it anyway because of the restructuring done as part of the optimization process… but that is a limitation intrinsic in debugging optimized code.
Finally, a question: why does this program exits with code 0 when executed?
[EDITED THE PYTHON CODE TO MAKE THE QUESTION MORE INTERESTING]