From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 23793 invoked by alias); 26 Mar 2008 17:40:34 -0000 Received: (qmail 23780 invoked by uid 22791); 26 Mar 2008 17:40:31 -0000 X-Spam-Check-By: sourceware.org Received: from smtp-out.google.com (HELO smtp-out.google.com) (216.239.33.17) by sourceware.org (qpsmtpd/0.31) with ESMTP; Wed, 26 Mar 2008 17:39:58 +0000 Received: from zps75.corp.google.com (zps75.corp.google.com [172.25.146.75]) by smtp-out.google.com with ESMTP id m2QHdb6p029535; Wed, 26 Mar 2008 17:39:38 GMT Received: from localhost (meta.corp.google.com [172.22.108.53]) by zps75.corp.google.com with ESMTP id m2QHdILe008060; Wed, 26 Mar 2008 10:39:19 -0700 Received: by localhost (Postfix, from userid 500) id E6D063F25E8; Wed, 26 Mar 2008 10:39:18 -0700 (PDT) To: bauerman@br.ibm.com CC: gdb-patches@sourceware.org In-reply-to: <1206547779.29533.43.camel@localhost.localdomain> (message from Thiago Jung Bauermann on Wed, 26 Mar 2008 13:09:39 -0300) Subject: Re: Patch to handle compressed sections References: <20080325230440.BF0623F25D6@localhost> <1206547779.29533.43.camel@localhost.localdomain> Message-Id: <20080326173918.E6D063F25E8@localhost> Date: Wed, 26 Mar 2008 17:40:00 -0000 From: csilvers@google.com (Craig Silverstein) Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2008-03/txt/msg00404.txt.bz2 } Neat. Out of curiosity, does this approach work well in practice? Yes, it seems to work quite well. } If you need to compress debug info, it means you are dealing with a } very large amount of it. This is true. An example binary we deal with could be about 2G with debug information, and about 1.3G with the debug information compressed. However, we tend to have machines with quite fast processors, and decompressing the debug information doesn't take very long; at least, it's on the order of existing gdb startup time (which includes reading all that data from disk in the first place). It may even be faster to read less data from disk and uncompress it, than to just read all the data from disk in the first place. I wrote the patch to be extensible to other compression formats in the future. One possibility would be to support a compression format that does not have great compression, but is very fast to decompress. That could be used in environments where decompress time is more of a concern. } It's a trade-off where you ease the burden on the computer where the } program runs, and permits you to have debuginfo around where you } probably couldn't before, but on the other hand you need a lot of } resources to actually be able to work with that debuginfo in a debug } environment, right? I see the trade-off as more of a disk/cpu tradeoff. For us, the machines are powerful enough to deal with the uncompressed binaries, but they take up an awful lot of disk space. Compressing allows the binaries to take up less disk, without really affecting run-time behavior noticeably. } Encoding this information in the section name looks strange to } me. Isn't it possible to define a new flag in the section's sh_flags } to signal that the section is compressed and then use the first few } bytes of the section contents for the information above? This was the first thing I tried, but it had -- as you pointed out -- compatibility issues. In particular, various tools would try to read a .debug_* section, but not know about the new flag (or the new .note section, if we implemented it that way), and would complain about a malformed section. objdump, if I remember correctly, would complain about 1000 times per malformed section(!). I've found these tools deal much better if the section is just missing entirely (because instead we have a section named .debug_*.zlib.nnnn instead). We could compromise and still give the section a different name, like .debug_info.zlib, and then store the 'nnnn' part in a .note section or somewhere else, but I didn't see much benefit to that. } I think it's better for this error message to say something along } the lines of "Support for compressed DWARF data is disabled in this } copy of GDB". Good idea. I attach a new patch below that's identical except for the error mesage text. craig 2008-03-24 Craig Silverstein * configure.ac (AC_SEARCH_LIBS): Add check for zlib. * config.in, configure: Regenerate. * dwarf2read.c: Include zlib.h if present. (uncompressed_section_size): New. (section_is_p): New. (dwarf2_locate_sections): Use section_is_p instead of strcmp to determine whether a given section has a given name. (dwarf2_read_section): Read the compressed section if present in the binary. --- gdb/config.in 2008-03-25 15:53:56.000000000 -0700 +++ gdb/config.in 2008-03-24 15:50:21.000000000 -0700 @@ -472,6 +472,9 @@ /* Define to 1 if you have the `XML_StopParser' function. */ #undef HAVE_XML_STOPPARSER +/* Define to 1 if you have the header file. */ +#undef HAVE_ZLIB_H + /* Define to 1 if your system has the _etext variable. */ #undef HAVE__ETEXT --- gdb/configure 2008-03-25 15:53:56.000000000 -0700 +++ gdb/configure 2008-03-24 15:50:23.000000000 -0700 @@ -6003,6 +6003,283 @@ fi +# Link in zlib if we can. This allows us to read compressed debug sections. +echo "$as_me:$LINENO: checking for library containing zlibVersion" >&5 +echo $ECHO_N "checking for library containing zlibVersion... $ECHO_C" >&6 +if test "${ac_cv_search_zlibVersion+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_zlibVersion=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char zlibVersion (); +int +main () +{ +zlibVersion (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_zlibVersion="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_zlibVersion" = no; then + for ac_lib in z; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char zlibVersion (); +int +main () +{ +zlibVersion (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_zlibVersion="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_zlibVersion" >&5 +echo "${ECHO_T}$ac_cv_search_zlibVersion" >&6 +if test "$ac_cv_search_zlibVersion" != no; then + test "$ac_cv_search_zlibVersion" = "none required" || LIBS="$ac_cv_search_zlibVersion $LIBS" + +for ac_header in zlib.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------ ## +## Report this to the AC_PACKAGE_NAME lists. ## +## ------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +fi + + # For the TUI, we need enhanced curses functionality. # # FIXME: kettenis/20040905: We prefer ncurses over the vendor-supplied --- gdb/configure.ac 2008-03-25 15:53:56.000000000 -0700 +++ gdb/configure.ac 2008-03-24 15:50:25.000000000 -0700 @@ -394,6 +394,9 @@ # Some systems (e.g. Solaris) have `socketpair' in libsocket. AC_SEARCH_LIBS(socketpair, socket) +# Link in zlib if we can. This allows us to read compressed debug sections. +AC_SEARCH_LIBS(zlibVersion, z, [AC_CHECK_HEADERS(zlib.h)]) + # For the TUI, we need enhanced curses functionality. # # FIXME: kettenis/20040905: We prefer ncurses over the vendor-supplied --- gdb/dwarf2read.c 2008-03-25 15:53:56.000000000 -0700 +++ gdb/dwarf2read.c 2008-03-24 17:28:44.000000000 -0700 @@ -50,6 +50,9 @@ #include "gdb_string.h" #include "gdb_assert.h" #include +#ifdef HAVE_ZLIB_H +#include +#endif /* A note on memory usage for this file. @@ -195,6 +198,10 @@ /* names of the debugging sections */ +/* Note that if the debugging section has been compressed, it might + have a name like .debug_info.zlib.nnnn (where nnn is the + uncompressed size). */ + #define INFO_SECTION ".debug_info" #define ABBREV_SECTION ".debug_abbrev" #define LINE_SECTION ".debug_line" @@ -1109,6 +1116,40 @@ return (dwarf_info_section != NULL && dwarf_abbrev_section != NULL); } +/* Given a section name like .debug_str.zlib.nnnn, where nnnn is the + uncompressed section size, returns nnnn as an unsigned int. For + other sections, returns the size as per bfd_get_section_size. */ +static unsigned int +uncompressed_section_size (asection *sectp) +{ + unsigned int uncompressed_size = 0; + char *compression_type = strchr (sectp->name + 1, '.'); + if (compression_type != NULL) + { + char *size_string = strchr (compression_type + 1, '.'); + char *strtol_error; + if (size_string != NULL + && size_string[1] != '\0') + uncompressed_size = strtoul (size_string + 1, &strtol_error, 10); + if (uncompressed_size > 0 && *strtol_error == '\0') + return uncompressed_size; + } + return bfd_get_section_size (sectp); +} + +/* When loading sections, we can either look for the section name, + * or for section_name.zlib.nnnn, which indicates a compressed + * section. nnnn is the uncompressed section size. */ + +static int +section_is_p(asection *sectp, const char *name) +{ + int name_size = strlen (name); + return (strcmp (sectp->name, name) == 0 + || (strncmp (sectp->name, name, name_size) == 0 + && sectp->name[name_size] == '.')); +} + /* This function is mapped across the sections and remembers the offset and size of each of the debugging sections we are interested in. */ @@ -1116,63 +1157,63 @@ static void dwarf2_locate_sections (bfd *abfd, asection *sectp, void *ignore_ptr) { - if (strcmp (sectp->name, INFO_SECTION) == 0) + if (section_is_p (sectp, INFO_SECTION)) { - dwarf2_per_objfile->info_size = bfd_get_section_size (sectp); + dwarf2_per_objfile->info_size = uncompressed_section_size (sectp); dwarf_info_section = sectp; } - else if (strcmp (sectp->name, ABBREV_SECTION) == 0) + else if (section_is_p (sectp, ABBREV_SECTION)) { - dwarf2_per_objfile->abbrev_size = bfd_get_section_size (sectp); + dwarf2_per_objfile->abbrev_size = uncompressed_section_size (sectp); dwarf_abbrev_section = sectp; } - else if (strcmp (sectp->name, LINE_SECTION) == 0) + else if (section_is_p (sectp, LINE_SECTION)) { - dwarf2_per_objfile->line_size = bfd_get_section_size (sectp); + dwarf2_per_objfile->line_size = uncompressed_section_size (sectp); dwarf_line_section = sectp; } - else if (strcmp (sectp->name, PUBNAMES_SECTION) == 0) + else if (section_is_p (sectp, PUBNAMES_SECTION)) { - dwarf2_per_objfile->pubnames_size = bfd_get_section_size (sectp); + dwarf2_per_objfile->pubnames_size = uncompressed_section_size (sectp); dwarf_pubnames_section = sectp; } - else if (strcmp (sectp->name, ARANGES_SECTION) == 0) + else if (section_is_p (sectp, ARANGES_SECTION)) { - dwarf2_per_objfile->aranges_size = bfd_get_section_size (sectp); + dwarf2_per_objfile->aranges_size = uncompressed_section_size (sectp); dwarf_aranges_section = sectp; } - else if (strcmp (sectp->name, LOC_SECTION) == 0) + else if (section_is_p (sectp, LOC_SECTION)) { - dwarf2_per_objfile->loc_size = bfd_get_section_size (sectp); + dwarf2_per_objfile->loc_size = uncompressed_section_size (sectp); dwarf_loc_section = sectp; } - else if (strcmp (sectp->name, MACINFO_SECTION) == 0) + else if (section_is_p (sectp, MACINFO_SECTION)) { - dwarf2_per_objfile->macinfo_size = bfd_get_section_size (sectp); + dwarf2_per_objfile->macinfo_size = uncompressed_section_size (sectp); dwarf_macinfo_section = sectp; } - else if (strcmp (sectp->name, STR_SECTION) == 0) + else if (section_is_p (sectp, STR_SECTION)) { - dwarf2_per_objfile->str_size = bfd_get_section_size (sectp); + dwarf2_per_objfile->str_size = uncompressed_section_size (sectp); dwarf_str_section = sectp; } - else if (strcmp (sectp->name, FRAME_SECTION) == 0) + else if (section_is_p (sectp, FRAME_SECTION)) { - dwarf2_per_objfile->frame_size = bfd_get_section_size (sectp); + dwarf2_per_objfile->frame_size = uncompressed_section_size (sectp); dwarf_frame_section = sectp; } - else if (strcmp (sectp->name, EH_FRAME_SECTION) == 0) + else if (section_is_p (sectp, EH_FRAME_SECTION)) { flagword aflag = bfd_get_section_flags (ignore_abfd, sectp); if (aflag & SEC_HAS_CONTENTS) { - dwarf2_per_objfile->eh_frame_size = bfd_get_section_size (sectp); + dwarf2_per_objfile->eh_frame_size = uncompressed_section_size (sectp); dwarf_eh_frame_section = sectp; } } - else if (strcmp (sectp->name, RANGES_SECTION) == 0) + else if (section_is_p (sectp, RANGES_SECTION)) { - dwarf2_per_objfile->ranges_size = bfd_get_section_size (sectp); + dwarf2_per_objfile->ranges_size = uncompressed_section_size (sectp); dwarf_ranges_section = sectp; } @@ -5234,29 +5275,113 @@ } /* Read the contents of the section at OFFSET and of size SIZE from the - object file specified by OBJFILE into the objfile_obstack and return it. */ + object file specified by OBJFILE into the objfile_obstack and return it. + If the section is compressed, uncompress it before returning. */ gdb_byte * dwarf2_read_section (struct objfile *objfile, asection *sectp) { bfd *abfd = objfile->obfd; - gdb_byte *buf, *retbuf; bfd_size_type size = bfd_get_section_size (sectp); + char *compression_type = NULL; + unsigned long uncompressed_size = 0; if (size == 0) return NULL; - buf = obstack_alloc (&objfile->objfile_obstack, size); - retbuf = symfile_relocate_debug_section (abfd, sectp, buf); - if (retbuf != NULL) - return retbuf; + /* If the section is stored compressed, it will have the name + .debug_*.zlib.nnnn, where nnnn is the uncompressed size. */ + compression_type = strchr (sectp->name + 1, '.'); + if (compression_type != NULL) + { + char *size_string = strchr (compression_type + 1, '.'); + char *strtol_error; + if (size_string != NULL + && size_string[1] != '\0') + uncompressed_size = strtoul (size_string + 1, &strtol_error, 10); + if (uncompressed_size == 0 || *strtol_error != '\0') + error (_("Dwarf Error: Can't parse size at end of section name '%s'" + " in '%s'"), + sectp->name, bfd_get_filename (abfd)); + } + + /* Handle the case of a normal, not-compressed section. */ + if (compression_type == NULL) + { + gdb_byte *buf = obstack_alloc (&objfile->objfile_obstack, size); + /* When debugging .o files, we may need to apply relocations; see + http://www.cygwin.com/ml/gdb-patches/2002-04/msg00136.html . + We never compress sections in .o files, so we only need to + try this when the section is not compressed. */ + gdb_byte *retbuf = symfile_relocate_debug_section (abfd, sectp, buf); + if (retbuf != NULL) + return retbuf; + + if (bfd_seek (abfd, sectp->filepos, SEEK_SET) != 0 + || bfd_bread (buf, size, abfd) != size) + error (_("Dwarf Error: Can't read DWARF data from '%s'"), + bfd_get_filename (abfd)); + + return buf; + } + + /* Handle the case of a section compressed using zlib. */ + if (strncmp (compression_type, ".zlib.", sizeof(".zlib.")-1) == 0) + { +#ifndef HAVE_ZLIB_H + error (_("Support for compressed DWARF data (from '%s') is disabled " + "in this copy of GDB"), + bfd_get_filename (abfd)); +#else + int rc; + gdb_byte *compressed_buffer = xmalloc (size); + Bytef *uncompressed_buffer = obstack_alloc (&objfile->objfile_obstack, + uncompressed_size); + z_stream strm; + + if (bfd_seek (abfd, sectp->filepos, SEEK_SET) != 0 + || bfd_bread (compressed_buffer, size, abfd) != size) + error (_("Dwarf Error: Can't read DWARF data from '%s'"), + bfd_get_filename (abfd)); + + /* It is possible the section consists of several compressed + buffers concatenated together, so we uncompress in a loop. */ + strm.zalloc = NULL; + strm.zfree = NULL; + strm.opaque = NULL; + strm.next_in = (Bytef*)compressed_buffer; + strm.avail_in = size; + strm.avail_out = uncompressed_size; + rc = inflateInit (&strm); + while (strm.avail_in > 0) + { + if (rc != Z_OK) + error (_("Dwarf Error: setting up DWARF uncompression in '%s': %d"), + bfd_get_filename (abfd), rc); + strm.next_out = (uncompressed_buffer + + (uncompressed_size - strm.avail_out)); + rc = inflate (&strm, Z_FINISH); + if (rc != Z_STREAM_END) + error (_("Dwarf Error: zlib error uncompressing from '%s': %d"), + bfd_get_filename (abfd), rc); + rc = inflateReset (&strm); + } + rc = inflateEnd (&strm); + if (rc != Z_OK + || strm.avail_out != 0) + error (_("Dwarf Error: concluding DWARF uncompression in '%s': %d"), + bfd_get_filename (abfd), rc); - if (bfd_seek (abfd, sectp->filepos, SEEK_SET) != 0 - || bfd_bread (buf, size, abfd) != size) - error (_("Dwarf Error: Can't read DWARF data from '%s'"), - bfd_get_filename (abfd)); + xfree (compressed_buffer); + return uncompressed_buffer; +#endif + } - return buf; + /* If we get here, we have an unknown compression type. */ + error (_("Dwarf Error: Unknown compression type in section named '%s'" + " from '%s'"), + sectp->name, bfd_get_filename (abfd)); + return NULL; } /* In DWARF version 2, the description of the debugging information is --- /dev/null 1969-12-31 16:00:00.000000000 -0800 +++ gdb/testsuite/gdb.dwarf2/dw2-compressed.S 2008-03-24 15:50:39.000000000 -0700 @@ -0,0 +1,213 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2008 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 . */ + +/* This tests that gdb can read compressed sections. The contents + are the same as dw2-basic.S, but the .debug_abbrev section has been + comrpessed using zlib. */ + +/* Dummy function to provide debug information for. */ + + .text + .globl _start +_start: + .int 0 +.Lbegin_text1: + .globl func_cu1 + .type func_cu1, %function +func_cu1: +.Lbegin_func_cu1: + .int 0 +.Lend_func_cu1: + .size func_cu1, .-func_cu1 +.Lend_text1: + +/* Debug information */ + + .section .debug_info +.Lcu1_begin: + /* CU header */ + .4byte .Lcu1_end - .Lcu1_start /* Length of Compilation Unit */ +.Lcu1_start: + .2byte 2 /* DWARF Version */ + .4byte .Labbrev1_begin /* Offset into abbrev section */ + .byte 4 /* Pointer size */ + + /* CU die */ + .uleb128 1 /* Abbrev: DW_TAG_compile_unit */ + .4byte .Lline1_begin /* DW_AT_stmt_list */ + .4byte .Lend_text1 /* DW_AT_high_pc */ + .4byte .Lbegin_text1 /* DW_AT_low_pc */ + .ascii "file1.txt\0" /* DW_AT_name */ + .ascii "GNU C 3.3.3\0" /* DW_AT_producer */ + .byte 1 /* DW_AT_language (C) */ + + /* func_cu1 */ + .uleb128 2 /* Abbrev: DW_TAG_subprogram */ + .byte 1 /* DW_AT_external */ + .byte 1 /* DW_AT_decl_file */ + .byte 2 /* DW_AT_decl_line */ + .ascii "func_cu1\0" /* DW_AT_name */ + .4byte .Ltype_int-.Lcu1_begin /* DW_AT_type */ + .4byte .Lbegin_func_cu1 /* DW_AT_low_pc */ + .4byte .Lend_func_cu1 /* DW_AT_high_pc */ + .byte 1 /* DW_AT_frame_base: length */ + .byte 0x55 /* DW_AT_frame_base: DW_OP_reg5 */ + +.Ltype_int: + .uleb128 3 /* Abbrev: DW_TAG_base_type */ + .ascii "int\0" /* DW_AT_name */ + .byte 4 /* DW_AT_byte_size */ + .byte 5 /* DW_AT_encoding */ + + .byte 0 /* End of children of CU */ + +.Lcu1_end: + +/* Line table */ + .section .debug_line +.Lline1_begin: + .4byte .Lline1_end - .Lline1_start /* Initial length */ +.Lline1_start: + .2byte 2 /* Version */ + .4byte .Lline1_lines - .Lline1_hdr /* header_length */ +.Lline1_hdr: + .byte 1 /* Minimum insn length */ + .byte 1 /* default_is_stmt */ + .byte 1 /* line_base */ + .byte 1 /* line_range */ + .byte 0x10 /* opcode_base */ + + /* Standard lengths */ + .byte 0 + .byte 1 + .byte 1 + .byte 1 + .byte 1 + .byte 0 + .byte 0 + .byte 0 + .byte 1 + .byte 0 + .byte 0 + .byte 1 + .byte 0 + .byte 0 + .byte 0 + + /* Include directories */ + .byte 0 + + /* File names */ + .ascii "file1.txt\0" + .uleb128 0 + .uleb128 0 + .uleb128 0 + + .byte 0 + +.Lline1_lines: + .byte 0 /* DW_LNE_set_address */ + .uleb128 5 + .byte 2 + .4byte .Lbegin_func_cu1 + + .byte 3 /* DW_LNS_advance_line */ + .sleb128 3 /* ... to 4 */ + + .byte 1 /* DW_LNS_copy */ + + .byte 1 /* DW_LNS_copy (second time as an end-of-prologue marker) */ + + .byte 0 /* DW_LNE_set_address */ + .uleb128 5 + .byte 2 + .4byte .Lend_func_cu1 + + .byte 0 /* DW_LNE_end_of_sequence */ + .uleb128 1 + .byte 1 + +.Lline1_end: + +/* Abbrev table -- compressed */ + .section .debug_abbrev.zlib.51 +.Labbrev1_begin: + .byte 0x78 + .byte 0x5e + .byte 0x63 + .byte 0x14 + .byte 0x64 + .byte 0x14 + .byte 0x60 + .byte 0x13 + .byte 0x62 + .byte 0x14 + .byte 0x64 + .byte 0x64 + .byte 0xe6 + .byte 0x50 + .byte 0xe5 + .byte 0x10 + .byte 0xe6 + .byte 0x66 + .byte 0x60 + .byte 0x60 + .byte 0xd2 + .byte 0x63 + .byte 0xb0 + .byte 0xe7 + .byte 0xb1 + .byte 0xe2 + .byte 0xb6 + .byte 0xe6 + .byte 0x66 + .byte 0xe6 + .byte 0xf0 + .byte 0x14 + .byte 0x16 + .byte 0x64 + .byte 0x14 + .byte 0x62 + .byte 0x74 + .byte 0xe0 + .byte 0x02 + .byte 0x00 + .byte 0x25 + .byte 0x78 + .byte 0x02 + .byte 0x81 + .byte 0x78 + .byte 0x9c + .byte 0x63 + .byte 0x60 + .byte 0x60 + .byte 0x56 + .byte 0x61 + .byte 0x60 + .byte 0xe6 + .byte 0xe0 + .byte 0xe6 + .byte 0xb6 + .byte 0xe3 + .byte 0x66 + .byte 0x00 + .byte 0x02 + .byte 0x00 + .byte 0x04 + .byte 0x9c + .byte 0x00 + .byte 0x92 --- /dev/null 1969-12-31 16:00:00.000000000 -0800 +++ gdb/testsuite/gdb.dwarf2/dw2-compressed.exp 2008-03-24 15:50:39.000000000 -0700 @@ -0,0 +1,52 @@ +# Copyright 2008 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 . + +# Minimal DWARF-2 unit test + +# This test can only be run on targets which support DWARF-2 and use gas. +# For now pick a sampling of likely targets. +if {![istarget *-*-linux*] + && ![istarget *-*-gnu*] + && ![istarget *-*-elf*] + && ![istarget *-*-openbsd*] + && ![istarget arm-*-eabi*] + && ![istarget powerpc-*-eabi*]} { + return 0 +} + +set testfile "dw2-compressed" +set srcfile ${testfile}.S +set binfile ${objdir}/${subdir}/${testfile}.x + +if { [gdb_compile "${srcdir}/${subdir}/main.c" "main-ndebug.o" object -g0] != "" } { + return -1 +} + +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${testfile}.o" object {nodebug}] != "" } { + return -1 +} + +if { [gdb_compile "${testfile}.o main-ndebug.o -static -nostdlib" "${binfile}" executable {debug}] != "" } { + return -1 +} + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir +gdb_load ${binfile} + +gdb_test "set listsize 1" "" +gdb_test "list func_cu1" "4\[ \t\]+File 1 Line 4" +gdb_test "ptype func_cu1" "type = int \\(\\)"