How to generate gcc debug symbol outside the build

2019-01-01 06:23发布

问题:

I know I can generate debug symbol using -g option. However the symbol is embeded in the target file. Could gcc generate debug symbol outside the result executable/library? Like .pdb file of windows VC++ compiler did.

回答1:

You need to use objcopy to separate the debug information:

objcopy --only-keep-debug \"${tostripfile}\" \"${debugdir}/${debugfile}\"
strip --strip-debug --strip-unneeded \"${tostripfile}\"
objcopy --add-gnu-debuglink=\"${debugdir}/${debugfile}\" \"${tostripfile}\"

I use the bash script below to separate the debug information into files with a .debug extension in a .debug directory. This way I can tar the libraries and executables in one tar file and the .debug directories in another. If I want to add the debug info later on I simply extract the debug tar file and voila I have symbolic debug information.

This is the bash script:

#!/bin/bash

scriptdir=`dirname ${0}`
scriptdir=`(cd ${scriptdir}; pwd)`
scriptname=`basename ${0}`

set -e

function errorexit()
{
  errorcode=${1}
  shift
  echo $@
  exit ${errorcode}
}

function usage()
{
  echo \"USAGE ${scriptname} <tostrip>\"
}

tostripdir=`dirname \"$1\"`
tostripfile=`basename \"$1\"`


if [ -z ${tostripfile} ] ; then
  usage
  errorexit 0 \"tostrip must be specified\"
fi

cd \"${tostripdir}\"

debugdir=.debug
debugfile=\"${tostripfile}.debug\"

if [ ! -d \"${debugdir}\" ] ; then
  echo \"creating dir ${tostripdir}/${debugdir}\"
  mkdir -p \"${debugdir}\"
fi
echo \"stripping ${tostripfile}, putting debug info into ${debugfile}\"
objcopy --only-keep-debug \"${tostripfile}\" \"${debugdir}/${debugfile}\"
strip --strip-debug --strip-unneeded \"${tostripfile}\"
objcopy --add-gnu-debuglink=\"${debugdir}/${debugfile}\" \"${tostripfile}\"
chmod -x \"${debugdir}/${debugfile}\"


回答2:

Compile with debug information:

gcc -g -o main main.c

Separate the debug information:

objcopy --only-keep-debug main main.debug

or

cp main main.debug
strip --only-keep-debug main.debug

Strip debug information from origin file:

objcopy --strip-debug main

or

strip --strip-debug --strip-unneeded main

debug by debuglink mode:

objcopy --add-gnu-debuglink main.debug main
gdb main

You can also use exec file and symbol file separatly:

gdb -s main.debug -e main

or

gdb
(gdb) exec-file main
(gdb) symbol-file main.debug

For details:

(gdb) help exec-file
(gdb) help symbol-file

Ref:
https://sourceware.org/gdb/onlinedocs/gdb/Files.html#Files https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html



回答3:

Check out the \"--only-keep-debug\" option of the strip command.

From the link:

The intention is that this option will be used in conjunction with --add-gnu-debuglink to create a two part executable. One a stripped binary which will occupy less space in RAM and in a distribution and the second a debugging information file which is only needed if debugging abilities are required.



回答4:

NOTE: Programs compiled with high-optimization levels (-O3, -O4) cannot generate many debugging symbols for optimized variables, in-lined functions and unrolled loops, regardless of the symbols being embedded (-g) or extracted (objcopy) into a \'.debug\' file.

Alternate approaches are

  1. Embed the versioning (VCS, git, svn) data into the program, for compiler optimized executables (-O3, -O4).
  2. Build a 2nd non-optimized version of the executable.

The first option provides a means to rebuild the production code with full debugging and symbols at a later date. Being able to re-build the original production code with no optimizations is a tremendous help for debugging. (NOTE: This assumes testing was done with the optimized version of the program).

Your build system can create a .c file loaded with the compile date, commit, and other VCS details. Here is a \'make + git\' example:

program: program.o version.o 

program.o: program.cpp program.h 

build_version.o: build_version.c    

build_version.c: 
    @echo \"const char *build1=\\\"VCS: Commit: $(shell git log -1 --pretty=%H)\\\";\" > \"$@\"
    @echo \"const char *build2=\\\"VCS: Date: $(shell git log -1 --pretty=%cd)\\\";\" >> \"$@\"
    @echo \"const char *build3=\\\"VCS: Author: $(shell git log -1 --pretty=\"%an %ae\")\\\";\" >> \"$@\"
    @echo \"const char *build4=\\\"VCS: Branch: $(shell git symbolic-ref HEAD)\\\";\" >> \"$@\"
    # TODO: Add compiler options and other build details

.TEMPORARY: build_version.c

After the program is compiled you can locate the original \'commit\' for your code by using the command: strings -a my_program | grep VCS

VCS: PROGRAM_NAME=my_program
VCS: Commit=190aa9cace3b12e2b58b692f068d4f5cf22b0145
VCS: BRANCH=refs/heads/PRJ123_feature_desc
VCS: AUTHOR=Joe Developer  joe.developer@somewhere.com
VCS: COMMIT_DATE=2013-12-19

All that is left is to check-out the original code, re-compile without optimizations, and start debugging.