http://fedoraproject.org/wiki/Features/MiniDebugInfo https://bugzilla.redhat.com/show_bug.cgi?id=834068 Patch by Alexander Larsson. Review/modifications and testfile by Jan Kratochvil. Index: gdb-7.4.50.20120703/gdb/Makefile.in =================================================================== --- gdb-7.4.50.20120703.orig/gdb/Makefile.in 2012-07-06 21:44:57.243444217 +0200 +++ gdb-7.4.50.20120703/gdb/Makefile.in 2012-07-06 21:44:57.768444937 +0200 @@ -151,6 +151,9 @@ READLINE_CFLAGS = @READLINE_CFLAGS@ # Where is expat? This will be empty if expat was not available. LIBEXPAT = @LIBEXPAT@ +# Where is lzma? This will be empty if lzma was not available. +LIBLZMA = @LIBLZMA@ + WARN_CFLAGS = @WARN_CFLAGS@ WERROR_CFLAGS = @WERROR_CFLAGS@ GDB_WARN_CFLAGS = $(WARN_CFLAGS) @@ -467,7 +470,7 @@ INTERNAL_LDFLAGS = $(CFLAGS) $(GLOBAL_CF # LIBIBERTY appears twice on purpose. CLIBS = $(SIM) $(READLINE) $(OPCODES) $(BFD) $(INTL) $(LIBIBERTY) $(LIBDECNUMBER) \ $(XM_CLIBS) $(NAT_CLIBS) $(GDBTKLIBS) @LIBS@ @PYTHON_LIBS@ \ - $(LIBEXPAT) \ + $(LIBEXPAT) $(LIBLZMA) \ $(LIBIBERTY) $(WIN32LIBS) $(LIBGNU) CDEPS = $(XM_CDEPS) $(NAT_CDEPS) $(SIM) $(BFD) $(READLINE_DEPS) \ $(OPCODES) $(INTL_DEPS) $(LIBIBERTY) $(CONFIG_DEPS) $(LIBGNU) Index: gdb-7.4.50.20120703/gdb/config.in =================================================================== --- gdb-7.4.50.20120703.orig/gdb/config.in 2012-07-06 21:44:57.599444705 +0200 +++ gdb-7.4.50.20120703/gdb/config.in 2012-07-06 21:44:57.769444938 +0200 @@ -198,6 +198,9 @@ /* Define to 1 if you have the `libiconvlist' function. */ #undef HAVE_LIBICONVLIST +/* Define if you have the lzma library. */ +#undef HAVE_LIBLZMA + /* Define to 1 if you have the `m' library (-lm). */ #undef HAVE_LIBM Index: gdb-7.4.50.20120703/gdb/configure =================================================================== --- gdb-7.4.50.20120703.orig/gdb/configure 2012-07-06 21:44:57.696444838 +0200 +++ gdb-7.4.50.20120703/gdb/configure 2012-07-06 21:53:47.667170352 +0200 @@ -641,6 +641,9 @@ TCL_VERSION WIN32LDAPP GUI_CFLAGS_X LIBGUI +LTLIBLZMA +LIBLZMA +HAVE_LIBLZMA WIN32LIBS SER_HARDWIRE WERROR_CFLAGS @@ -813,6 +816,8 @@ with_system_gdbinit enable_werror enable_build_warnings enable_gdb_build_warnings +with_lzma +with_liblzma_prefix with_tcl with_tk with_x @@ -1532,6 +1537,9 @@ Optional Packages: --with-sysroot[=DIR] search for usr/lib et al within DIR --with-system-gdbinit=PATH automatically load a system-wide gdbinit file + --with-lzma support lzma compression (auto/yes/no) + --with-liblzma-prefix[=DIR] search for liblzma in DIR/include and DIR/lib + --without-liblzma-prefix don't search for liblzma in includedir and libdir --with-tcl directory containing tcl configuration (tclConfig.sh) --with-tk directory containing tk configuration (tkConfig.sh) --with-x use the X Window System @@ -13151,6 +13159,494 @@ LIBS=$OLD_LIBS # Add any host-specific objects to GDB. CONFIG_OBS="${CONFIG_OBS} ${gdb_host_obs}" +# If building on ELF, look for lzma support for embedded compressed debug info. +if test $gdb_cv_var_elf = yes; then + +# Check whether --with-lzma was given. +if test "${with_lzma+set}" = set; then : + withval=$with_lzma; +else + with_lzma=auto +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use lzma" >&5 +$as_echo_n "checking whether to use lzma... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_lzma" >&5 +$as_echo "$with_lzma" >&6; } + + if test "${with_lzma}" != no; then + + + + + + + + + use_additional=yes + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + +# Check whether --with-liblzma-prefix was given. +if test "${with_liblzma_prefix+set}" = set; then : + withval=$with_liblzma_prefix; + if test "X$withval" = "Xno"; then + use_additional=no + else + if test "X$withval" = "X"; then + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + else + additional_includedir="$withval/include" + additional_libdir="$withval/lib" + fi + fi + +fi + + LIBLZMA= + LTLIBLZMA= + INCLZMA= + rpathdirs= + ltrpathdirs= + names_already_handled= + names_next_round='lzma ' + while test -n "$names_next_round"; do + names_this_round="$names_next_round" + names_next_round= + for name in $names_this_round; do + already_handled= + for n in $names_already_handled; do + if test "$n" = "$name"; then + already_handled=yes + break + fi + done + if test -z "$already_handled"; then + names_already_handled="$names_already_handled $name" + uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'` + eval value=\"\$HAVE_LIB$uppername\" + if test -n "$value"; then + if test "$value" = yes; then + eval value=\"\$LIB$uppername\" + test -z "$value" || LIBLZMA="${LIBLZMA}${LIBLZMA:+ }$value" + eval value=\"\$LTLIB$uppername\" + test -z "$value" || LTLIBLZMA="${LTLIBLZMA}${LTLIBLZMA:+ }$value" + else + : + fi + else + found_dir= + found_la= + found_so= + found_a= + if test $use_additional = yes; then + if test -n "$shlibext" && test -f "$additional_libdir/lib$name.$shlibext"; then + found_dir="$additional_libdir" + found_so="$additional_libdir/lib$name.$shlibext" + if test -f "$additional_libdir/lib$name.la"; then + found_la="$additional_libdir/lib$name.la" + fi + else + if test -f "$additional_libdir/lib$name.$libext"; then + found_dir="$additional_libdir" + found_a="$additional_libdir/lib$name.$libext" + if test -f "$additional_libdir/lib$name.la"; then + found_la="$additional_libdir/lib$name.la" + fi + fi + fi + fi + if test "X$found_dir" = "X"; then + for x in $LDFLAGS $LTLIBLZMA; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + case "$x" in + -L*) + dir=`echo "X$x" | sed -e 's/^X-L//'` + if test -n "$shlibext" && test -f "$dir/lib$name.$shlibext"; then + found_dir="$dir" + found_so="$dir/lib$name.$shlibext" + if test -f "$dir/lib$name.la"; then + found_la="$dir/lib$name.la" + fi + else + if test -f "$dir/lib$name.$libext"; then + found_dir="$dir" + found_a="$dir/lib$name.$libext" + if test -f "$dir/lib$name.la"; then + found_la="$dir/lib$name.la" + fi + fi + fi + ;; + esac + if test "X$found_dir" != "X"; then + break + fi + done + fi + if test "X$found_dir" != "X"; then + LTLIBLZMA="${LTLIBLZMA}${LTLIBLZMA:+ }-L$found_dir -l$name" + if test "X$found_so" != "X"; then + if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/lib"; then + LIBLZMA="${LIBLZMA}${LIBLZMA:+ }$found_so" + else + haveit= + for x in $ltrpathdirs; do + if test "X$x" = "X$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + ltrpathdirs="$ltrpathdirs $found_dir" + fi + if test "$hardcode_direct" = yes; then + LIBLZMA="${LIBLZMA}${LIBLZMA:+ }$found_so" + else + if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then + LIBLZMA="${LIBLZMA}${LIBLZMA:+ }$found_so" + haveit= + for x in $rpathdirs; do + if test "X$x" = "X$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + rpathdirs="$rpathdirs $found_dir" + fi + else + haveit= + for x in $LDFLAGS $LIBLZMA; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X-L$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + LIBLZMA="${LIBLZMA}${LIBLZMA:+ }-L$found_dir" + fi + if test "$hardcode_minus_L" != no; then + LIBLZMA="${LIBLZMA}${LIBLZMA:+ }$found_so" + else + LIBLZMA="${LIBLZMA}${LIBLZMA:+ }-l$name" + fi + fi + fi + fi + else + if test "X$found_a" != "X"; then + LIBLZMA="${LIBLZMA}${LIBLZMA:+ }$found_a" + else + LIBLZMA="${LIBLZMA}${LIBLZMA:+ }-L$found_dir -l$name" + fi + fi + additional_includedir= + case "$found_dir" in + */lib | */lib/) + basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e 's,/lib/*$,,'` + additional_includedir="$basedir/include" + ;; + esac + if test "X$additional_includedir" != "X"; then + if test "X$additional_includedir" != "X/usr/include"; then + haveit= + if test "X$additional_includedir" = "X/usr/local/include"; then + if test -n "$GCC"; then + case $host_os in + linux*) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + for x in $CPPFLAGS $INCLZMA; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X-I$additional_includedir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_includedir"; then + INCLZMA="${INCLZMA}${INCLZMA:+ }-I$additional_includedir" + fi + fi + fi + fi + fi + if test -n "$found_la"; then + save_libdir="$libdir" + case "$found_la" in + */* | *\\*) . "$found_la" ;; + *) . "./$found_la" ;; + esac + libdir="$save_libdir" + for dep in $dependency_libs; do + case "$dep" in + -L*) + additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'` + if test "X$additional_libdir" != "X/usr/lib"; then + haveit= + if test "X$additional_libdir" = "X/usr/local/lib"; then + if test -n "$GCC"; then + case $host_os in + linux*) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + haveit= + for x in $LDFLAGS $LIBLZMA; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + LIBLZMA="${LIBLZMA}${LIBLZMA:+ }-L$additional_libdir" + fi + fi + haveit= + for x in $LDFLAGS $LTLIBLZMA; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + LTLIBLZMA="${LTLIBLZMA}${LTLIBLZMA:+ }-L$additional_libdir" + fi + fi + fi + fi + ;; + -R*) + dir=`echo "X$dep" | sed -e 's/^X-R//'` + if test "$enable_rpath" != no; then + haveit= + for x in $rpathdirs; do + if test "X$x" = "X$dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + rpathdirs="$rpathdirs $dir" + fi + haveit= + for x in $ltrpathdirs; do + if test "X$x" = "X$dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + ltrpathdirs="$ltrpathdirs $dir" + fi + fi + ;; + -l*) + names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'` + ;; + *.la) + names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'` + ;; + *) + LIBLZMA="${LIBLZMA}${LIBLZMA:+ }$dep" + LTLIBLZMA="${LTLIBLZMA}${LTLIBLZMA:+ }$dep" + ;; + esac + done + fi + else + LIBLZMA="${LIBLZMA}${LIBLZMA:+ }-l$name" + LTLIBLZMA="${LTLIBLZMA}${LTLIBLZMA:+ }-l$name" + fi + fi + fi + done + done + if test "X$rpathdirs" != "X"; then + if test -n "$hardcode_libdir_separator"; then + alldirs= + for found_dir in $rpathdirs; do + alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir" + done + acl_save_libdir="$libdir" + libdir="$alldirs" + eval flag=\"$hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + LIBLZMA="${LIBLZMA}${LIBLZMA:+ }$flag" + else + for found_dir in $rpathdirs; do + acl_save_libdir="$libdir" + libdir="$found_dir" + eval flag=\"$hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + LIBLZMA="${LIBLZMA}${LIBLZMA:+ }$flag" + done + fi + fi + if test "X$ltrpathdirs" != "X"; then + for found_dir in $ltrpathdirs; do + LTLIBLZMA="${LTLIBLZMA}${LTLIBLZMA:+ }-R$found_dir" + done + fi + + + ac_save_CPPFLAGS="$CPPFLAGS" + + for element in $INCLZMA; do + haveit= + for x in $CPPFLAGS; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X$element"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element" + fi + done + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for liblzma" >&5 +$as_echo_n "checking for liblzma... " >&6; } +if test "${ac_cv_liblzma+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + + ac_save_LIBS="$LIBS" + LIBS="$LIBS $LIBLZMA" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include "lzma.h" +int +main () +{ +lzma_index_iter iter; + lzma_index_iter_init (&iter, 0); + lzma_mf_is_supported (LZMA_MF_HC3); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_liblzma=yes +else + ac_cv_liblzma=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LIBS="$ac_save_LIBS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_liblzma" >&5 +$as_echo "$ac_cv_liblzma" >&6; } + if test "$ac_cv_liblzma" = yes; then + HAVE_LIBLZMA=yes + +$as_echo "#define HAVE_LIBLZMA 1" >>confdefs.h + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with liblzma" >&5 +$as_echo_n "checking how to link with liblzma... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBLZMA" >&5 +$as_echo "$LIBLZMA" >&6; } + else + HAVE_LIBLZMA=no + CPPFLAGS="$ac_save_CPPFLAGS" + LIBLZMA= + LTLIBLZMA= + fi + + + + + + + if test "$HAVE_LIBLZMA" != yes; then + if test "$with_lzma" = yes; then + as_fn_error "missing liblzma for --with-lzma" "$LINENO" 5 + fi + fi + fi +fi + LIBGUI="../libgui/src/libgui.a" GUI_CFLAGS_X="-I${srcdir}/../libgui/src" Index: gdb-7.4.50.20120703/gdb/configure.ac =================================================================== --- gdb-7.4.50.20120703.orig/gdb/configure.ac 2012-07-06 21:44:57.699444841 +0200 +++ gdb-7.4.50.20120703/gdb/configure.ac 2012-07-06 21:53:29.875146225 +0200 @@ -2196,6 +2196,27 @@ LIBS=$OLD_LIBS # Add any host-specific objects to GDB. CONFIG_OBS="${CONFIG_OBS} ${gdb_host_obs}" +# If building on ELF, look for lzma support for embedded compressed debug info. +if test $gdb_cv_var_elf = yes; then + AC_ARG_WITH(lzma, + AS_HELP_STRING([--with-lzma], [support lzma compression (auto/yes/no)]), + [], [with_lzma=auto]) + AC_MSG_CHECKING([whether to use lzma]) + AC_MSG_RESULT([$with_lzma]) + + if test "${with_lzma}" != no; then + AC_LIB_HAVE_LINKFLAGS([lzma], [], [#include "lzma.h"], + [lzma_index_iter iter; + lzma_index_iter_init (&iter, 0); + lzma_mf_is_supported (LZMA_MF_HC3);]) + if test "$HAVE_LIBLZMA" != yes; then + if test "$with_lzma" = yes; then + AC_MSG_ERROR([missing liblzma for --with-lzma]) + fi + fi + fi +fi + LIBGUI="../libgui/src/libgui.a" GUI_CFLAGS_X="-I${srcdir}/../libgui/src" AC_SUBST(LIBGUI) Index: gdb-7.4.50.20120703/gdb/elfread.c =================================================================== --- gdb-7.4.50.20120703.orig/gdb/elfread.c 2012-07-06 21:44:57.211444174 +0200 +++ gdb-7.4.50.20120703/gdb/elfread.c 2012-07-06 21:44:57.800444979 +0200 @@ -51,6 +51,10 @@ #include "observer.h" #include "elf/external.h" #include +#include "gdbcore.h" +#ifdef HAVE_LIBLZMA +# include +#endif extern void _initialize_elfread (void); @@ -2210,6 +2214,262 @@ find_separate_debug_file_by_buildid (str return NULL; } +#ifdef HAVE_LIBLZMA + +/* Custom lzma_allocator.alloc so they use the gdb ones. */ + +static void * +alloc_lzma (void *opaque, size_t nmemb, size_t size) +{ + return xmalloc (nmemb * size); +} + +/* Custom lzma_allocator.free so they use the gdb ones. */ + +static void +free_lzma (void *opaque, void *ptr) +{ + xfree (ptr); +} + +/* It cannot be const due to the lzma library function prototypes. */ + +static lzma_allocator gdb_lzma_allocator = { alloc_lzma, free_lzma, NULL}; + +/* Custom bfd_openr_iovec implementation to read compressed data from a + section. This keeps only the last decompressed block in memory to + allow larger data without using to much memory. */ + +struct lzma_stream +{ + /* Section of input BFD we are decoding data from. */ + asection *section; + + /* lzma library decompression state. */ + lzma_index *index; + + /* Currently decoded block. */ + bfd_size_type data_start; + bfd_size_type data_end; + gdb_byte *data; +}; + +/* bfd_openr_iovec OPEN_P implementation for + find_separate_debug_file_in_section. OPEN_CLOSURE is 'asection *' of the + section to decompress. + + Return 'struct lzma_stream *' must be freed by caller by xfree, together + with its INDEX lzma data. */ + +static void * +lzma_open (struct bfd *nbfd, void *open_closure) +{ + asection *section = open_closure; + bfd_size_type size, offset; + lzma_stream_flags options; + gdb_byte footer[LZMA_STREAM_HEADER_SIZE]; + gdb_byte *indexdata; + lzma_index *index; + int ret; + uint64_t memlimit = UINT64_MAX; + struct lzma_stream *lstream; + size_t pos; + + size = bfd_get_section_size (section); + offset = section->filepos + size - LZMA_STREAM_HEADER_SIZE; + if (size < LZMA_STREAM_HEADER_SIZE + || bfd_seek (section->owner, offset, SEEK_SET) != 0 + || bfd_bread (footer, LZMA_STREAM_HEADER_SIZE, section->owner) + != LZMA_STREAM_HEADER_SIZE + || lzma_stream_footer_decode (&options, footer) != LZMA_OK + || offset < options.backward_size) + { + bfd_set_error (bfd_error_wrong_format); + return NULL; + } + + offset -= options.backward_size; + indexdata = xmalloc (options.backward_size); + index = NULL; + pos = 0; + if (bfd_seek (section->owner, offset, SEEK_SET) != 0 + || bfd_bread (indexdata, options.backward_size, section->owner) + != options.backward_size + || lzma_index_buffer_decode (&index, &memlimit, &gdb_lzma_allocator, + indexdata, &pos, options.backward_size) + != LZMA_OK + || lzma_index_size (index) != options.backward_size) + { + xfree (indexdata); + bfd_set_error (bfd_error_wrong_format); + return NULL; + } + xfree (indexdata); + + lstream = xzalloc (sizeof (struct lzma_stream)); + lstream->section = section; + lstream->index = index; + + return lstream; +} + +/* bfd_openr_iovec PREAD_P implementation for + find_separate_debug_file_in_section. Passed STREAM + is 'struct lzma_stream *'. */ + +static file_ptr +lzma_pread (struct bfd *nbfd, void *stream, void *buf, file_ptr nbytes, + file_ptr offset) +{ + struct lzma_stream *lstream = stream; + bfd_size_type chunk_size; + lzma_index_iter iter; + gdb_byte *compressed, *uncompressed; + file_ptr block_offset; + lzma_filter filters[LZMA_FILTERS_MAX + 1]; + lzma_block block; + size_t compressed_pos, uncompressed_pos; + file_ptr res; + + res = 0; + while (nbytes > 0) + { + if (lstream->data == NULL + || lstream->data_start > offset || offset >= lstream->data_end) + { + asection *section = lstream->section; + + lzma_index_iter_init (&iter, lstream->index); + if (lzma_index_iter_locate (&iter, offset)) + break; + + compressed = xmalloc (iter.block.total_size); + block_offset = section->filepos + iter.block.compressed_file_offset; + if (bfd_seek (section->owner, block_offset, SEEK_SET) != 0 + || bfd_bread (compressed, iter.block.total_size, section->owner) + != iter.block.total_size) + { + xfree (compressed); + break; + } + + uncompressed = xmalloc (iter.block.uncompressed_size); + + memset (&block, 0, sizeof (block)); + block.filters = filters; + block.header_size = lzma_block_header_size_decode (compressed[0]); + if (lzma_block_header_decode (&block, &gdb_lzma_allocator, compressed) + != LZMA_OK) + { + xfree (compressed); + xfree (uncompressed); + break; + } + + compressed_pos = block.header_size; + uncompressed_pos = 0; + if (lzma_block_buffer_decode (&block, &gdb_lzma_allocator, + compressed, &compressed_pos, + iter.block.total_size, + uncompressed, &uncompressed_pos, + iter.block.uncompressed_size) + != LZMA_OK) + { + xfree (compressed); + xfree (uncompressed); + break; + } + + xfree (compressed); + + xfree (lstream->data); + lstream->data = uncompressed; + lstream->data_start = iter.block.uncompressed_file_offset; + lstream->data_end = (iter.block.uncompressed_file_offset + + iter.block.uncompressed_size); + } + + chunk_size = min (nbytes, lstream->data_end - offset); + memcpy (buf, lstream->data + offset - lstream->data_start, chunk_size); + buf = (gdb_byte *) buf + chunk_size; + offset += chunk_size; + nbytes -= chunk_size; + res += chunk_size; + } + + return res; +} + +/* bfd_openr_iovec CLOSE_P implementation for + find_separate_debug_file_in_section. Passed STREAM + is 'struct lzma_stream *'. */ + +static int +lzma_close (struct bfd *nbfd, + void *stream) +{ + struct lzma_stream *lstream = stream; + + lzma_index_end (lstream->index, &gdb_lzma_allocator); + xfree (lstream->data); + xfree (lstream); + return 0; +} + +/* bfd_openr_iovec STAT_P implementation for + find_separate_debug_file_in_section. Passed STREAM + is 'struct lzma_stream *'. */ + +static int +lzma_stat (struct bfd *abfd, + void *stream, + struct stat *sb) +{ + struct lzma_stream *lstream = stream; + + sb->st_size = lzma_index_uncompressed_size (lstream->index); + return 0; +} + +/* This looks for a xz compressed separate debug info object file embedded + in a section called .gnu_debugdata. If we find one we create a iovec + based bfd that decompresses the object data on demand. */ + +static bfd * +find_separate_debug_file_in_section (struct objfile *objfile) +{ + asection *section; + bfd *abfd; + + section = bfd_get_section_by_name (objfile->obfd, ".gnu_debugdata"); + if (section == NULL) + return NULL; + + /* objfile->NAME lifetime is longer than the ABFD's lifetime. */ + abfd = bfd_openr_iovec (objfile->name, gnutarget, lzma_open, section, + lzma_pread, lzma_close, lzma_stat); + if (abfd == NULL) + return NULL; + + if (!bfd_check_format (abfd, bfd_object)) + { + bfd_close (abfd); + return NULL; + } + + return abfd; +} + +#else /* !HAVE_LIBLZMA */ + +static bfd * +find_separate_debug_file_in_section (struct objfile *objfile) +{ + return NULL; +} + +#endif /* !HAVE_LIBLZMA */ + /* Scan and build partial symbols for a symbol file. We have been initialized by a call to elf_symfile_init, which currently does nothing. @@ -2433,6 +2693,7 @@ elf_symfile_read (struct objfile *objfil else if (!objfile_has_partial_symbols (objfile)) { char *debugfile, *build_id_filename; + bfd *abfd = NULL; debugfile = find_separate_debug_file_by_buildid (objfile, &build_id_filename); @@ -2442,9 +2703,7 @@ elf_symfile_read (struct objfile *objfil if (debugfile) { - bfd *abfd = symfile_bfd_open (debugfile); - - symbol_file_add_separate (abfd, symfile_flags, objfile); + abfd = symfile_bfd_open (debugfile); xfree (debugfile); } /* Check if any separate debug info has been extracted out. */ @@ -2453,6 +2712,12 @@ elf_symfile_read (struct objfile *objfil debug_print_missing (objfile->name, build_id_filename); xfree (build_id_filename); + + if (abfd == NULL) + abfd = find_separate_debug_file_in_section (objfile); + + if (abfd != NULL) + symbol_file_add_separate (abfd, symfile_flags, objfile); } if (symtab_create_debug) Index: gdb-7.4.50.20120703/gdb/testsuite/gdb.dwarf2/dw2-gnu-debugdata.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb-7.4.50.20120703/gdb/testsuite/gdb.dwarf2/dw2-gnu-debugdata.c 2012-07-06 21:44:57.802444983 +0200 @@ -0,0 +1,30 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2012 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include + +static int +debugdata_function (void) +{ + return raise (SIGSEGV) + 1; +} + +int +main (void) +{ + return debugdata_function () + 1; +} Index: gdb-7.4.50.20120703/gdb/testsuite/gdb.dwarf2/dw2-gnu-debugdata.exp =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb-7.4.50.20120703/gdb/testsuite/gdb.dwarf2/dw2-gnu-debugdata.exp 2012-07-06 21:44:57.809444991 +0200 @@ -0,0 +1,91 @@ +# Copyright 2012 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +standard_testfile + +load_lib dwarf.exp +if ![dwarf2_support] { + return 0 +} + +if [build_executable ${testfile}.exp $testfile] { + return -1 +} + +proc run { test cmdline } { + verbose "cmdline is $cmdline" + set result [catch "exec $cmdline" output] + verbose "result is $result" + verbose "output is $output" + if {$result == 0} { + pass $test + return 0 + } else { + fail $test + return -1 + } +} + +set strip_program [transform strip] +set nm_program [transform nm] + +# Extract the dynamic symbols from the main binary, there is no need to also have these +# in the normal symbol table +file delete -- ${binfile}.dynsyms +if [run "nm -D" "[transform nm] -D ${binfile} --format=posix --defined-only | awk \\{print\\ \\\$1\\} | sort > ${binfile}.dynsyms"] { + return -1 +} + +# Extract all the text (i.e. function) symbols from the debuginfo +file delete -- ${binfile}.funcsyms +if [run "nm" "[transform nm] ${binfile} --format=posix --defined-only | awk \\{if(\\\$2==\"T\"||\\\$2==\"t\")print\\ \\\$1\\} | sort > ${binfile}.funcsyms"] { + return -1 +} + +# Keep all the function symbols not already in the dynamic symbol table +file delete -- ${binfile}.keep_symbols +if [run "comm" "comm -13 ${binfile}.dynsyms ${binfile}.funcsyms > ${binfile}.keep_symbols"] { + return -1 +} + +# Copy the full debuginfo, keeping only a minumal set of symbols and removing some unnecessary sections +file delete -- ${binfile}.mini_debuginfo +if [run "objcopy 1" "[transform objcopy] -S --remove-section .gdb_index --remove-section .comment --keep-symbols=${binfile}.keep_symbols ${binfile} ${binfile}.mini_debuginfo"] { + return -1 +} + +# GDB specific - we do not have split executable in advance. +file delete -- ${binfile}.strip +if [run "strip" "[transform strip] --strip-all -o ${binfile}.strip ${binfile}"] { + return -1 +} + +# Inject the compressed data into the .gnu_debugdata section of the original binary +file delete -- ${binfile}.mini_debuginfo.xz +if [run "xz" "xz ${binfile}.mini_debuginfo"] { + return -1 +} +file delete -- ${binfile}.test +if [run "objcopy 2" "[transform objcopy] --add-section .gnu_debugdata=${binfile}.mini_debuginfo.xz ${binfile}.strip ${binfile}.test"] { + return -1 +} + +clean_restart "$testfile.strip" + +gdb_test "p debugdata_function" {No symbol table is loaded\. Use the "file" command\.} "no symtab" + +clean_restart "$testfile.test" + +gdb_test "p debugdata_function" { = {} 0x[0-9a-f]+ } "have symtab"