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.
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}"
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
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
Embed the versioning (VCS, git, svn) data into the program, for compiler optimized executables (-O3, -O4).
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.
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.
No answer so far mentions eu-strip --strip-debug -f <out.debug> <input>.
This is provided by elfutils package.
The result will be that <input> file has been stripped of debug symbols which are now all in <out.debug>.
Related
I am trying to time how long each file in my codebase takes to compile.
According to this SO answer, you can write a script which does times the actual compilation and then stores the results in a file
/tmp/time-build:
#!/bin/bash
{ time g++ "$#"; } 2> >(cat <(echo "g++ $#") - >> /tmp/results.txt)
You can then override CMAKE_CXX_COMPILER when calling cmake so that make uses the script to perform the compilation
cmake .. -DCMAKE_CXX_COMPILER=/tmp/time-build
make
This works as advertised, and yields results similar to the following:
real 0m1.190s
user 0m1.044s
sys 0m0.140s
g++ -Werror -Wall -Wextra ... /src/test/foo.cpp
However, to ease processing, I would like to store only the real time, and have it on the same line as the g++ command.
Question:
My command line fu is not up to the task of turning this:
{ time g++ "$#"; } 2> >(cat <(echo "g++ $#") - >> /tmp/results.txt)
Into a command which captures only the real output of time, and includes it along with the echo "g++ $#" on a single line.
I don't know what the >(cat <( and ) - parts of the above command mean, and my attempts to incorporate a grep real and echo have failed
How can I do that?
Alternately, if there is a more idiomatic way to get cmake to output timings for each file processed, that too would be ideal.
Probably the best way to get compile times for a CMake-based project is to use ninja instead of make.
To do this you have to specify ninja generator while configuring:
cmake -G Ninja <...>
Then build using ninja:
ninja
After that, look at the file called .ninja_log. First column is start time in milliseconds, second is end time, forth is target name and the last one is a hash of the command line used.
There are even some solutions for viewing and analyzing this log: ninjatracing converts .ninja_log to a format readable by Chrome's tracing tool.
cmd=(clang "$#")
{ time "${cmd[#]}"; } 2> >( tee >( real=$(grep -Po 'real\K.*'); echo "${cmd[*]} $real" >>result.txt ) )
EDIT2:
THIS POST IS SOLVED (if you would like to see the product that tis post resulted in please visit the site http://www.nasmagic.com )
here is my plan firstly
i am using Platypus (http://sveinbjorn.org/platypus) a program for OSX that lets you create little "droplets" that basically wrap a bash script in a fancy drag and drop GUI.
now im using this as a nice easy way of making myself a drag and drop Nasm assembler.
i have made a few of these "droplets" with simple bash scripts inside them, one example would be the folowing:-
#!/bin/bash
chmod u+x $1
this as you can see makes my scripts executable,... very handy.
The way they work is they take one variable only and that is the path to the file eg.
/Users/MiRAGE/Desktop/example.sh
now here is my conundrum for the day
i need to do the following command:-
/usr/local/bin/nasm -f macho example.asm && ld -macosx_version_min 10.7.0 -o example example.o
this is one command that works fine with the non variable filenames. however in the droplet context it has a problem.
it can execute each of these commands in two seperate droplets but without changing the command it will not find the outputed file of the first command as it is not 'cd'd into the directory where the file is outputed so it doesent know what to do.
now as i say at the moment i have successfully got it to compile with one droplet with this command:-
/usr/local/bin/nasm -f macho $1
which i drag the file into first and it spits out a ".o" file
then i drop that ".o" file into droplet2 which has this command inside:-
ld -macosx_version_min 10.7.0 -lSystem -o $1.5y $1
this command is much less elegant than the first.
the only way i could get it to compile the file is to append my made up extention otherwise it would just not work. the problem with this method is that while it does output the compiled binary it ends up looking like this "exampleFilename.o.5y".
now i could go in and delete that and i would, i guess be where i need to be. but its very messy. 2 droplets, renaming files..... not what i want i want a drag and drop hassle free assembler.
so heres the code i have in mind except this is not real and clearly doesn't work.
if [filename $1 == ".asm"] # if the file extension is ".asm"
then # then do next step
/usr/local/bin/nasm -f macho $1 # make mach-o file
fi # end
else if [filename $1 == ".o"] # else if the file extension is ".o"
then # then do this step
ld -macosx_version_min 10.7.0 -lSystem -o $1 $1.o # take the file ".o" and outfile with no extention but the same name.
fi #end
this way i can drag it in once, it will spit out the ".o" file, then i drag that in, and it then converts it to the executable binary. PERFECT
but i cant for the life of me find out how to write this properly if anyone can help i would be very very appreciative
many thanks in advance
MiRAGE
If my understanding is correct, you want to be able to drag the .asm file to the droplet, then drag the resulting .o file to the droplet to produce an executable, in two steps.
In which case, after downloading platypus and recent nasm, I find this script works for me:
#!/bin/bash
# Get the path to the input file, and enter that directory
pathname=$(dirname "$1")
cd $pathname
# attempt strip the .asm extension off the input filename (if there is one)
filestem=$(basename -s .asm "$1")
# If the input file was a .asm file, then assemble it:
if [ "${pathname}/${filestem}.asm" == "$1" ]; then
/usr/local/bin/nasm -f macho "$1"
fi
# attempt strip the .o extension off the input filename (if there is one)
filestem=$(basename -s .o "$1")
# If the input file was a .o file, then link it:
if [ "${pathname}/${filestem}.o" == "$1" ]; then
ld -macosx_version_min 10.7.0 -o "$filestem" "$1"
fi
This script takes care to make sure output files are placed into the same directory as the input files.
Conversely, if you want a script to assemble and link in one shot, this works for me:
#!/bin/bash
# Get the path to the input file, and enter that directory
pathname=$(dirname "$1")
cd $pathname
# attempt strip the .asm extension off the input filename (if there is one)
filestem=$(basename -s .asm "$1")
# If the input file was an .asm file, then assemble and link it:
if [ "${pathname}/${filestem}.asm" == "$1" ]; then
/usr/local/bin/nasm -f macho "$1" && ld -macosx_version_min 10.7.0 -o "${pathname}/$filestem" "${pathname}/${filestem}.o"
fi
Note that what you have here basically replicates a lot of make or similar build systems do for you.
Note also that third-party software like playtpus is not strictly needed for the drag-n-drop part. You can use the built-in automator application to create similar applications. e.g I created one that you just drag example.asm onto the automator application icon and it runs the same shell script for you.
Also, you can test the shell script at the command line, simply by calling:
./myscript.sh example.asm
or
./myscript.sh example.o
Try this...
#!/bin/bash
ext=${1/*./}
test $ext == "asm" && /usr/local/bin/nasm -f macho "$1"
output=${1/*\//}; output=${output/.*/}
test $ext == "o" && ld -macosx_version_min 10.7.0 -lSystem -o $output $1
If you want to try make to do this, the following will do it:
#!/bin/bash
target=$(dirname "$1")/$(basename -s ".${1/*./}" "$1")
export CC=ld
export LDFLAGS="-macosx_version_min 10.7.0 -lSystem"
make -f - "$target" <<EOF
%.o: %.asm
/usr/local/bin/nasm -f macho $<
.PRECIOUS: %.o
EOF
(Note there is a TAB character at the start of the nasm line.)
The beauty of using make is that targets are only rebuilt if they don't exist, or they are older than their prerequisites. Also make has built-in implicit rules for many things, including linking a .o to create an executable, which we make use of.
This script will accept a .asm file or .o file as input, from which it will derive a make target (the name of the expected executable) by stripping off the extension.
explicitly set the linker to be "ld" instead of the default of "cc"
set necessary linker flags in the LDFLAGS variable
call make with the derived target. Normally make will parse a Makefile for its rules, but in this case, we redirect a makefile using a bash here-document.
The redirected makefile has one implicit rule, which says to assemble x.asm into x.o
The redirected makefile also has a special .PRECIOUS rule, which prevents deletion of intermediate files (.o files in this case)
My open source project distributes a Makefile. "make" by itself works fine as long as the user has Boost and OpenSSL installed. If not, he gets a compilation error.
I would like to show the user an error message with instructions on how to fix rather than have him discern the issue from the compiler output.
I've put together a little script to embed inside a Makefile that will do a quick and dirty compilation to validate if a prerequisite header file exists before allowing the core code to build. It shows an error message and aborts the compile if the code won't compile. It seems to work good.
# BOOST_INCLUDE := -I/home/jselbie/boost_1_51_0
all: myapp
testforboost.o:
#echo "Testing for the presence of Boost header files..."
#rm -f testforboost.o
#echo "#include <boost/shared_ptr.hpp> " | $(CXX) $(BOOST_INCLUDE) -x c++ -c - -o testforboost.o 2>testerr; true
#rm -f testerr
#if [ -e testforboost.o ];\
then \
echo "Validated Boost header files are available";\
else \
echo "* ********************************************";\
echo "* Error: Boost header files are not avaialble";\
echo "* Consult the README file on how to fix";\
echo "* ********************************************";\
exit 1;\
fi
myapp: testforboost.o
$(CXX) $(BOOST_INCLUDE) myapp.cpp -o myapp
Is my script a good way to do this? I'm under the assumption that it's portable beyond Linux (Solaris, BSD, MacOS). Or are there other standard practices for doing this? I know that Autotools can do similar things, but I'm not too excited about learning all of Autotools and re-writing my Makefiles.
In principle it's possible like that. But since you're only preprocessing, and given that you can use any command as a condition, it can be simplified to:
.PHONY: testforboost
testforboost:
#echo "Testing for the presence of Boost header files..."
#if echo "#include <boost/shared_ptr.hpp> " | $(CXX) -x c++ -E - >/dev/null 2>&1;\
then \
echo "Validated Boost header files are available";\
else \
echo "* ********************************************";\
echo "* Error: Boost header files are not avaialble";\
echo "* Consult the README file on how to fix";\
echo "* ********************************************";\
exit 1;\
fi
OTOH, since you have the boost include path in a variable, why not just look for the file directly? That would need some string manipulation. Probably hard in make, but with makepp it would be $(map $(BOOST_INCLUDE),s/^-I//)
Is there a way to make gcc use the absolute path when printing errors found in files compiled in the current directory?
For instance the following does what I want when print errors:
g++ -I. -I../../.. /home/some/path/somefile.cpp
but I want to achieve the same with something like:
g++ -I. -I../../.. somefile.cpp
I want warnings and errors to be formatting something like:
/home/some/path/somefile.cpp:299:52: warning: some warning
There is no way to do this with gcc itself, but it's trivial with a wrapper script, installed as "gcc", "g++", etc in a directory before /usr/bin in your PATH:
#! /bin/sh
sourcefile="$1"; shift
case "$sourcefile" in
/*) ;;
*) sourcefile="$PWD/$sourcefile" ;;
esac
exec "/usr/bin/${0##*/}" "$sourcefile" "$#"
... provided that you always put the source file first in your compiler invocation (you'll have to tweak your Makefiles).
I have seen this done in various installers, although unfortunately I cannot actually find much on how to do this or remember which programs had this feature so I can learn it from them.
I essentially am compiling a shared library (not with autoconf/libtool yet, just a standard makefile) and wish for make test to simply compile all files in the examples directory, linked to the library that was just built (i.e. ../bin/libfoo.so.1.0.1)
What does the makefile format provide to do this wildcard compile?
I know you're asking for a mechanism to automatically build all source files in a directory, but I dislike that approach; at some point in the future, an individual test or two will need to be disabled, and you're left doing something a little weird, like re-naming a file just so it won't automatically be compiled.
I much prefer listing every program that you intend on building in the Makefile, but it need not be horrible. With some suffix rules you can easily build all your .c files into .o files or directly into executables.
Here's a snippet from the AppArmor regression test suite, which is typical of what this sort of thing looks like. (I hope I copied out all the relevant bits, it has been a few years.)
SRC=access.c \
changeprofile.c \
changehat.c \
changehat_fork.c \
changehat_misc.c \
....
unlink.c \
xattrs.c
...
#only do the ioperm/iopl tests for x86 derived architectures
ifneq (,$(findstring $(shell uname -i),i386 i486 i586 i686 x86 x86_64))
SRC+=syscall_ioperm.c syscall_iopl.c
endif
...
LIBIMMUNIX:=$(shell if [ -f /usr/lib/libapparmor.so -o -f /usr/lib64/libapparmor.so ] ; then \
echo -lapparmor ; \
elif [ -f /lib/libimmunix.so.1 -o -f /lib64/libimmunix.so ] ; then \
echo -limmunix ; \
fi )
CFLAGS+=$(CHANGEHAT_FLAGS) -Wall -Wstrict-prototypes
LDLIBS+=$(LIBIMMUNIX)
EXEC=$(SRC:%.c=%)
...
all: $(EXEC) changehat.h
It isn't as easy as just dropping a new file into the directory; you do need to add it to the Makefile. But you only need to add the name, once, to one line, and it is there for good. If you want to disable it, then comment out the offending line. It's almost as easy and significantly more control over your build process.