libdwfl automagic decompression support
authorRoland McGrath <roland@redhat.com>
Tue, 6 Jan 2009 07:59:32 +0000 (23:59 -0800)
committerRoland McGrath <roland@redhat.com>
Tue, 6 Jan 2009 07:59:32 +0000 (23:59 -0800)
17 files changed:
ChangeLog
configure.ac
libdw/ChangeLog
libdw/Makefile.am
libdwfl/ChangeLog
libdwfl/Makefile.am
libdwfl/argp-std.c
libdwfl/bzip2.c [new file with mode: 0644]
libdwfl/dwfl_build_id_find_debuginfo.c
libdwfl/dwfl_build_id_find_elf.c
libdwfl/dwfl_module_getdwarf.c
libdwfl/dwfl_report_elf.c
libdwfl/find-debuginfo.c
libdwfl/gzip.c [new file with mode: 0644]
libdwfl/libdwflP.h
libdwfl/offline.c
libdwfl/open.c [new file with mode: 0644]

index 9a90de7..a03fc75 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2009-01-05  Roland McGrath  <roland@redhat.com>
+
+       * configure.ac (eu_ZIPLIB): New macro.
+       Use it to test for -lz, -lbz2, set .am ZLIB, BZLIB, zip_LIBS.
+
 2008-12-30  Ulrich Drepper  <drepper@redhat.com>
 
        * configure.ac: We need automake 1.8 now.
index 25804a8..dac3392 100644 (file)
@@ -1,7 +1,7 @@
 dnl Process this file with autoconf to produce a configure script.
 dnl Configure input file for elfutils.                     -*-autoconf-*-
 dnl
-dnl Copyright (C) 1996-2002, 2003, 2004, 2005, 2006, 2007, 2008 Red Hat, Inc.
+dnl Copyright (C) 1996-2009 Red Hat, Inc.
 dnl
 dnl This program is free software; you can redistribute it and/or modify
 dnl it under the terms of the GNU General Public License as published by
@@ -182,6 +182,29 @@ AC_SUBST([LIBEBL_SUBDIR])
 AC_DEFINE_UNQUOTED(LIBEBL_SUBDIR, "$LIBEBL_SUBDIR")
 AH_TEMPLATE([LIBEBL_SUBDIR], [$libdir subdirectory containing libebl modules.])
 
+m4_defun([eu_ZIPLIB], [with_[$1]lib=default
+AC_ARG_WITH([[$1]lib],
+AC_HELP_STRING([--with-[$1]lib], [support g[$1]ip compression in libdwfl]))
+if test $with_[$1]lib != no; then
+  AC_SEARCH_LIBS([$4], [$3], [with_[$1]lib=yes],
+                [test $with_[$1]lib = default ||
+                 AC_MSG_ERROR([missing -l[$3] for --with-[$1]lib])])
+fi
+AM_CONDITIONAL([$2]LIB, test $with_[$1]lib = yes)
+if test $with_[$1]lib = yes; then
+  AC_DEFINE(USE_[$2]LIB)
+fi
+AH_TEMPLATE(USE_[$2]LIB, [Support $5 decompression via -l$3.])
+])
+
+save_LIBS="$LIBS"
+LIBS=
+eu_ZIPLIB(z,Z,z,gzdopen,gzip)
+eu_ZIPLIB(bz,BZ,bz2,BZ2_bzdopen,bzip2)
+zip_LIBS="$LIBS"
+LIBS="$save_LIBS"
+AC_SUBST([zip_LIBS])
+
 dnl The directories with content.
 
 dnl Documentation.
index 235fac0..eda753d 100644 (file)
@@ -1,3 +1,7 @@
+2009-01-05  Roland McGrath  <roland@redhat.com>
+
+       * Makefile.am (libdw.so): Link in $(zip_LIBS).
+
 2008-08-15  Roland McGrath  <roland@redhat.com>
 
        * libdw.map (ELFUTILS_0.136): New version set, inherits from
index 69ce526..8649395 100644 (file)
@@ -1,6 +1,6 @@
 ## Process this file with automake to create Makefile.in
 ##
-## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 Red Hat, Inc.
+## Copyright (C) 2002-2009 Red Hat, Inc.
 ## This file is part of Red Hat elfutils.
 ##
 ## Red Hat elfutils is free software; you can redistribute it and/or modify
@@ -92,7 +92,7 @@ am_libdw_pic_a_OBJECTS = $(libdw_a_SOURCES:.c=.os)
 libdw_so_SOURCES =
 libdw.so: $(srcdir)/libdw.map libdw_pic.a \
          ../libdwfl/libdwfl_pic.a ../libebl/libebl.a \
-         ../libelf/libelf.so
+         ../libelf/libelf.so $(zip_LIBS)
 # The rpath is necessary for libebl because its $ORIGIN use will
 # not fly in a setuid executable that links in libdw.
        $(LINK) -shared -o $@ -Wl,--soname,$@.$(VERSION),-z,defs \
index 11aeafd..d925077 100644 (file)
@@ -1,3 +1,23 @@
+2009-01-05  Roland McGrath  <roland@redhat.com>
+
+       * argp-std.c (parse_opt): Use __libdw_open_file for core file.
+       * dwfl_build_id_find_debuginfo.c: Use it to open the file.
+       * dwfl_build_id_find_elf.c: Likewise.
+       * dwfl_module_getdwarf.c (open_elf): Likewise.
+       * dwfl_report_elf.c: Likewise.
+       * find-debuginfo.c (validate): Likewise.
+       * offline.c (__libdwfl_report_offline): Likewise.
+
+       * libdwflP.h: Declare __libdw_open_file.
+       * open.c: New file.
+       * Makefile.am (libdwfl_a_SOURCES): Add it.
+
+       * gzip.c: New file.
+       * Makefile.am [ZLIB] (libdwfl_a_SOURCES): Add it.
+       * bzip2.c: New file.
+       * Makefile.am [BZLIB] (libdwfl_a_SOURCES): Add it.
+       * libdwflP.h: Declare __libdw_gunzip, __libdw_bunzip2.
+
 2008-12-16  Roland McGrath  <roland@redhat.com>
 
        * dwfl_module_build_id.c (dwfl_module_build_id): Define with alias and
index db14db2..605f924 100644 (file)
@@ -2,7 +2,7 @@
 ##
 ## Process this file with automake to create Makefile.in
 ##
-## Copyright (C) 2005, 2006, 2007, 2008 Red Hat, Inc.
+## Copyright (C) 2005, 2006, 2007, 2008, 2009 Red Hat, Inc.
 ## This file is part of Red Hat elfutils.
 ##
 ## Red Hat elfutils is free software; you can redistribute it and/or modify
@@ -73,8 +73,14 @@ libdwfl_a_SOURCES = dwfl_begin.c dwfl_end.c dwfl_error.c dwfl_version.c \
                    dwfl_module_return_value_location.c \
                    dwfl_module_register_names.c \
                    dwfl_segment_report_module.c \
-                   link_map.c core-file.c
+                   link_map.c core-file.c open.c
 
+if ZLIB
+libdwfl_a_SOURCES += gzip.c
+endif
+if BZLIB
+libdwfl_a_SOURCES += bzip2.c
+endif
 
 if MUDFLAP
 libdwfl = libdwfl.a $(libdw) $(libebl) $(libelf) $(libeu)
index 0a0f7eb..ce2e20a 100644 (file)
@@ -1,5 +1,5 @@
 /* Standard argp argument parsers for tools using libdwfl.
-   Copyright (C) 2005, 2007, 2008 Red Hat, Inc.
+   Copyright (C) 2005, 2007, 2008, 2009 Red Hat, Inc.
    This file is part of Red Hat elfutils.
 
    Red Hat elfutils is free software; you can redistribute it and/or modify
@@ -215,14 +215,14 @@ parse_opt (int key, char *arg, struct argp_state *state)
        if (fd < 0)
          goto nofile;
 
-       Elf *core = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL);
-       if (core == NULL)
+       Elf *core;
+       Dwfl_Error error = __libdw_open_file (&fd, &core, true, false);
+       if (error != DWFL_E_NOERROR)
          {
-           close (fd);
            argp_failure (state, EXIT_FAILURE, 0,
                          _("cannot read ELF core file: %s"),
-                         elf_errmsg (-1));
-           return EIO;
+                         INTUSE(dwfl_errmsg) (error));
+           return error == DWFL_E_ERRNO ? errno : EIO;
          }
 
        GElf_Ehdr ehdr;
diff --git a/libdwfl/bzip2.c b/libdwfl/bzip2.c
new file mode 100644 (file)
index 0000000..8ad4ee5
--- /dev/null
@@ -0,0 +1,4 @@
+/* bzlib is almost just like zlib.  */
+
+#define BZLIB
+#include "gzip.c"
index 97def07..e51b65b 100644 (file)
@@ -1,5 +1,5 @@
 /* Find the debuginfo file for a module from its build ID.
-   Copyright (C) 2007 Red Hat, Inc.
+   Copyright (C) 2007, 2009 Red Hat, Inc.
    This file is part of Red Hat elfutils.
 
    Red Hat elfutils is free software; you can redistribute it and/or modify
@@ -71,10 +71,16 @@ dwfl_build_id_find_debuginfo (Dwfl_Module *mod,
       /* We need to open an Elf handle on the file so we can check its
         build ID note for validation.  Backdoor the handle into the
         module data structure since we had to open it early anyway.  */
-      mod->debug.elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL);
-      if (likely (__libdwfl_find_build_id (mod, false, mod->debug.elf) == 2))
-       /* Also backdoor the gratuitous flag.  */
-       mod->debug.valid = true;
+      Dwfl_Error error = __libdw_open_file (&fd, &mod->debug.elf, true, false);
+      if (error != DWFL_E_NOERROR)
+       __libdwfl_seterrno (error);
+      else if (likely (__libdwfl_find_build_id (mod, false,
+                                               mod->debug.elf) == 2))
+       {
+         /* Also backdoor the gratuitous flag.  */
+         mod->debug.valid = true;
+         return fd;
+       }
       else
        {
          /* A mismatch!  */
@@ -82,10 +88,10 @@ dwfl_build_id_find_debuginfo (Dwfl_Module *mod,
          mod->debug.elf = NULL;
          close (fd);
          fd = -1;
-         free (*debuginfo_file_name);
-         *debuginfo_file_name = NULL;
-         errno = 0;
        }
+      free (*debuginfo_file_name);
+      *debuginfo_file_name = NULL;
+      errno = 0;
     }
   return fd;
 }
index 1a226df..fcc6f1e 100644 (file)
@@ -1,5 +1,5 @@
 /* Find an ELF file for a module from its build ID.
-   Copyright (C) 2007, 2008 Red Hat, Inc.
+   Copyright (C) 2007, 2008, 2009 Red Hat, Inc.
    This file is part of Red Hat elfutils.
 
    Red Hat elfutils is free software; you can redistribute it and/or modify
@@ -140,10 +140,15 @@ dwfl_build_id_find_elf (Dwfl_Module *mod,
   int fd = __libdwfl_open_by_build_id (mod, false, file_name);
   if (fd >= 0)
     {
-      *elfp = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL);
-      if (__libdwfl_find_build_id (mod, false, *elfp) == 2)
-       /* This is a backdoor signal to short-circuit the ID refresh.  */
-       mod->main.valid = true;
+      Dwfl_Error error = __libdw_open_file (&fd, elfp, true, false);
+      if (error != DWFL_E_NOERROR)
+       __libdwfl_seterrno (error);
+      else if (__libdwfl_find_build_id (mod, false, *elfp) == 2)
+       {
+         /* This is a backdoor signal to short-circuit the ID refresh.  */
+         mod->main.valid = true;
+         return fd;
+       }
       else
        {
          /* This file does not contain the ID it should!  */
@@ -151,9 +156,9 @@ dwfl_build_id_find_elf (Dwfl_Module *mod,
          *elfp = NULL;
          close (fd);
          fd = -1;
-         free (*file_name);
-         *file_name = NULL;
        }
+      free (*file_name);
+      *file_name = NULL;
     }
   return fd;
 }
index 652383b..e99aa1f 100644 (file)
@@ -1,5 +1,5 @@
 /* Find debugging and symbol information for a module in libdwfl.
-   Copyright (C) 2005, 2006, 2007, 2008 Red Hat, Inc.
+   Copyright (C) 2005, 2006, 2007, 2008, 2009 Red Hat, Inc.
    This file is part of Red Hat elfutils.
 
    Red Hat elfutils is free software; you can redistribute it and/or modify
@@ -69,10 +69,11 @@ open_elf (Dwfl_Module *mod, struct dwfl_file *file)
       if (file->fd < 0)
        return CBFAIL;
 
-      file->elf = elf_begin (file->fd, ELF_C_READ_MMAP_PRIVATE, NULL);
+      Dwfl_Error error = __libdw_open_file (&file->fd, &file->elf, true, false);
+      if (error != DWFL_E_NOERROR)
+       return error;
     }
-
-  if (unlikely (elf_kind (file->elf) != ELF_K_ELF))
+  else if (unlikely (elf_kind (file->elf) != ELF_K_ELF))
     {
       close (file->fd);
       file->fd = -1;
index 0e5d09b..3482a22 100644 (file)
@@ -1,5 +1,5 @@
 /* Report a module to libdwfl based on ELF program headers.
-   Copyright (C) 2005, 2007 Red Hat, Inc.
+   Copyright (C) 2005, 2007, 2009 Red Hat, Inc.
    This file is part of Red Hat elfutils.
 
    Red Hat elfutils is free software; you can redistribute it and/or modify
@@ -274,7 +274,14 @@ dwfl_report_elf (Dwfl *dwfl, const char *name,
        }
     }
 
-  Elf *elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL);
+  Elf *elf;
+  Dwfl_Error error = __libdw_open_file (&fd, &elf, closefd, false);
+  if (error != DWFL_E_NOERROR)
+    {
+      __libdwfl_seterrno (error);
+      return NULL;
+    }
+
   Dwfl_Module *mod = __libdwfl_report_elf (dwfl, name, file_name,
                                           fd, elf, base);
   if (mod == NULL)
index a01293e..9e81739 100644 (file)
@@ -1,5 +1,5 @@
 /* Standard find_debuginfo callback for libdwfl.
-   Copyright (C) 2005, 2006, 2007, 2008 Red Hat, Inc.
+   Copyright (C) 2005, 2006, 2007, 2008, 2009 Red Hat, Inc.
    This file is part of Red Hat elfutils.
 
    Red Hat elfutils is free software; you can redistribute it and/or modify
@@ -99,16 +99,22 @@ validate (Dwfl_Module *mod, int fd, bool check, GElf_Word debuglink_crc)
       /* We need to open an Elf handle on the file so we can check its
         build ID note for validation.  Backdoor the handle into the
         module data structure since we had to open it early anyway.  */
-      mod->debug.elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL);
-      if (likely (__libdwfl_find_build_id (mod, false, mod->debug.elf) == 2))
-       /* Also backdoor the gratuitous flag.  */
-       mod->debug.valid = true;
+
+      mod->debug.valid = false;
+      Dwfl_Error error = __libdw_open_file (&fd, &mod->debug.elf, false, false);
+      if (error != DWFL_E_NOERROR)
+       __libdwfl_seterrno (error);
+      else if (likely (__libdwfl_find_build_id (mod, false,
+                                               mod->debug.elf) == 2))
+         /* Also backdoor the gratuitous flag.  */
+         mod->debug.valid = true;
       else
        {
          /* A mismatch!  */
          elf_end (mod->debug.elf);
          mod->debug.elf = NULL;
-         mod->debug.valid = false;
+         close (fd);
+         fd = -1;
        }
 
       return mod->debug.valid;
diff --git a/libdwfl/gzip.c b/libdwfl/gzip.c
new file mode 100644 (file)
index 0000000..effde5f
--- /dev/null
@@ -0,0 +1,223 @@
+/* Decompression support for libdwfl: zlib (gzip) and/or bzlib (bzip2).
+   Copyright (C) 2009 Red Hat, Inc.
+   This file is part of Red Hat elfutils.
+
+   Red Hat elfutils 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; version 2 of the License.
+
+   Red Hat elfutils 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+   In addition, as a special exception, Red Hat, Inc. gives You the
+   additional right to link the code of Red Hat elfutils with code licensed
+   under any Open Source Initiative certified open source license
+   (http://www.opensource.org/licenses/index.php) which requires the
+   distribution of source code with any binary distribution and to
+   distribute linked combinations of the two.  Non-GPL Code permitted under
+   this exception must only link to the code of Red Hat elfutils through
+   those well defined interfaces identified in the file named EXCEPTION
+   found in the source code files (the "Approved Interfaces").  The files
+   of Non-GPL Code may instantiate templates or use macros or inline
+   functions from the Approved Interfaces without causing the resulting
+   work to be covered by the GNU General Public License.  Only Red Hat,
+   Inc. may make changes or additions to the list of Approved Interfaces.
+   Red Hat's grant of this exception is conditioned upon your not adding
+   any new exceptions.  If you wish to add a new Approved Interface or
+   exception, please contact Red Hat.  You must obey the GNU General Public
+   License in all respects for all of the Red Hat elfutils code and other
+   code used in conjunction with Red Hat elfutils except the Non-GPL Code
+   covered by this exception.  If you modify this file, you may extend this
+   exception to your version of the file, but you are not obligated to do
+   so.  If you do not wish to provide this exception without modification,
+   you must delete this exception statement from your version and license
+   this file solely under the GPL without exception.
+
+   Red Hat elfutils is an included package of the Open Invention Network.
+   An included package of the Open Invention Network is a package for which
+   Open Invention Network licensees cross-license their patents.  No patent
+   license is granted, either expressly or impliedly, by designation as an
+   included package.  Should you wish to participate in the Open Invention
+   Network licensing program, please visit www.openinventionnetwork.com
+   <http://www.openinventionnetwork.com>.  */
+
+#include "libdwflP.h"
+
+#include <unistd.h>
+
+#ifdef BZLIB
+# include <bzlib.h>
+# define unzip         __libdw_bunzip2
+# define DWFL_E_ZLIB   DWFL_E_BZLIB
+# define MAGIC         "BZh"
+# define Z(what)       BZ_##what
+# define z_stream      bz_stream
+# define inflateInit(z)        BZ2_bzDecompressInit (z, 0, 0)
+# define inflate(z, f) BZ2_bzDecompress (z)
+# define inflateEnd(z) BZ2_bzDecompressEnd (z)
+# define gzFile                BZFILE *
+# define gzdopen       BZ2_bzdopen
+# define gzread                BZ2_bzread
+# define gzclose       BZ2_bzclose
+#else
+# include <zlib.h>
+# define unzip         __libdw_gunzip
+# define MAGIC         "\037\213"
+# define Z(what)       Z_##what
+#endif
+
+/* If this is not a compressed image, return DWFL_E_BADELF.
+   If we uncompressed it into *WHOLE, *WHOLE_SIZE, return DWFL_E_NOERROR.
+   Otherwise return an error for bad compressed data or I/O failure.  */
+
+Dwfl_Error internal_function
+unzip (int fd, off64_t start_offset,
+       void *mapped, size_t mapped_size,
+       void **whole, size_t *whole_size)
+{
+  void *buffer = NULL;
+  size_t size = 0;
+  inline bool bigger_buffer (size_t start)
+  {
+    size_t more = size ? size * 2 : start;
+    char *b = realloc (buffer, more);
+    while (unlikely (b == NULL) && more >= size + 1024)
+      b = realloc (buffer, more -= 1024);
+    if (unlikely (b == NULL))
+      return false;
+    buffer = b;
+    size = more;
+    return true;
+  }
+  inline void smaller_buffer (size_t end)
+  {
+    buffer = realloc (buffer, end) ?: buffer;
+    size = end;
+  }
+
+  inline Dwfl_Error zlib_fail (int result)
+  {
+    Dwfl_Error failure = DWFL_E_ZLIB;
+    switch (result)
+      {
+      case Z (MEM_ERROR):
+       failure = DWFL_E_NOMEM;
+       break;
+      }
+    free (buffer);
+    *whole = NULL;
+    return failure;
+  }
+
+  if (mapped != NULL)
+    {
+      /* The file is already mapped in.  Look at the header.  */
+      if (mapped_size <= sizeof MAGIC || memcmp (mapped, MAGIC, sizeof MAGIC))
+       /* Not a compressed file.  */
+       return DWFL_E_CB;
+
+      z_stream z = { .next_in = mapped, .avail_in = mapped_size };
+      int result = inflateInit (&z);
+      if (result != Z (OK))
+       return zlib_fail (result);
+
+      while ((result = inflate (&z, Z_SYNC_FLUSH)) == Z (OK))
+       {
+         ptrdiff_t pos = (void *) z.next_out - buffer;
+         if (!bigger_buffer (z.avail_in))
+           {
+             result = Z (MEM_ERROR);
+             break;
+           }
+         z.next_out = buffer + pos;
+         z.avail_out = size - pos;
+       }
+
+#ifdef BZLIB
+      uint64_t total_out = (((uint64_t) z.total_out_hi32 << 32)
+                           | z.total_out_lo32);
+      smaller_buffer (total_out);
+#else
+      smaller_buffer (z.total_out);
+#endif
+
+      inflateEnd (&z);
+
+      if (result != Z (STREAM_END))
+       return zlib_fail (result);
+    }
+  else
+    {
+      int d = dup (fd);
+      if (unlikely (d < 0))
+       return DWFL_E_CB;
+      if (start_offset != 0)
+       {
+         off64_t off = lseek (d, start_offset, SEEK_SET);
+         if (off != start_offset)
+           {
+             close (d);
+             return DWFL_E_CB;
+           }
+       }
+      gzFile zf = gzdopen (d, "r");
+      if (unlikely (zf == NULL))
+       {
+         close (d);
+         return zlib_fail (Z (MEM_ERROR));
+       }
+
+      /* From here on, zlib will close D.  */
+
+#ifndef BZLIB
+      if (gzdirect (zf))
+       {
+         gzclose (zf);
+         return DWFL_E_CB;
+       }
+#endif
+
+      ptrdiff_t pos = 0;
+      while (1)
+       {
+         if (!bigger_buffer (1024))
+           {
+             gzclose (zf);
+             return zlib_fail (Z (MEM_ERROR));
+           }
+         int n = gzread (zf, buffer + pos, size - pos);
+         if (n < 0)
+           {
+#ifdef BZLIB
+             int code;
+             BZ2_bzerror (zf, &code);
+             if (code == BZ_DATA_ERROR_MAGIC)
+               {
+                 gzclose (zf);
+                 free (buffer);
+                 return DWFL_E_CB;
+               }
+#endif
+             gzclose (zf);
+             return zlib_fail (Z (DATA_ERROR));
+           }
+         if (n == 0)
+           break;
+         pos += n;
+       }
+
+      gzclose (zf);
+      smaller_buffer (pos);
+    }
+
+  *whole = buffer;
+  *whole_size = size;
+
+return DWFL_E_NOERROR;
+}
index 6ba5c96..8551949 100644 (file)
@@ -1,5 +1,5 @@
 /* Internal definitions for libdwfl.
-   Copyright (C) 2005, 2006, 2007, 2008 Red Hat, Inc.
+   Copyright (C) 2005, 2006, 2007, 2008, 2009 Red Hat, Inc.
    This file is part of Red Hat elfutils.
 
    Red Hat elfutils is free software; you can redistribute it and/or modify
@@ -74,6 +74,8 @@
   DWFL_ERROR (LIBELF, N_("See elf_errno"))                                   \
   DWFL_ERROR (LIBDW, N_("See dwarf_errno"))                                  \
   DWFL_ERROR (LIBEBL, N_("See ebl_errno (XXX missing)"))                     \
+  DWFL_ERROR (ZLIB, N_("gzip decompression failed"))                         \
+  DWFL_ERROR (BZLIB, N_("bzip2 decompression failed"))                       \
   DWFL_ERROR (UNKNOWN_MACHINE, N_("no support library found for machine"))    \
   DWFL_ERROR (NOREL, N_("Callbacks missing for ET_REL file"))                \
   DWFL_ERROR (BADRELTYPE, N_("Unsupported relocation type"))                 \
@@ -315,6 +317,23 @@ extern Dwfl_Module *__libdwfl_report_offline (Dwfl *dwfl, const char *name,
                                                                const char *))
   internal_function;
 
+/* Decompression wrappers: decompress whole file into memory.  */
+extern Dwfl_Error __libdw_gunzip  (int fd, off64_t start_offset,
+                                  void *mapped, size_t mapped_size,
+                                  void **whole, size_t *whole_size)
+  internal_function;
+extern Dwfl_Error __libdw_bunzip2  (int fd, off64_t start_offset,
+                                   void *mapped, size_t mapped_size,
+                                   void **whole, size_t *whole_size)
+  internal_function;
+
+/* Open Elf handle on *FDP.  This handles decompression and checks
+   elf_kind.  Succeed only for ELF_K_ELF, or also ELF_K_AR if ARCHIVE_OK.
+   Returns DWFL_E_NOERROR and sets *ELFP on success, resets *FDP to -1 if
+   it's no longer used.  Resets *FDP on failure too iff CLOSE_ON_FAIL.  */
+extern Dwfl_Error __libdw_open_file (int *fdp, Elf **elfp,
+                                    bool close_on_fail, bool archive_ok)
+  internal_function;
 
 /* These are working nicely for --core, but are not ready to be
    exported interfaces quite yet.  */
@@ -363,6 +382,7 @@ extern int dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
 extern int dwfl_core_file_report (Dwfl *dwfl, Elf *elf, const GElf_Ehdr *ehdr);
 
 
+
 /* Avoid PLT entries.  */
 INTDECL (dwfl_begin)
 INTDECL (dwfl_errmsg)
index b3a95dd..a80e292 100644 (file)
@@ -1,5 +1,5 @@
 /* Recover relocatibility for addresses computed from debug information.
-   Copyright (C) 2005, 2006, 2007, 2008 Red Hat, Inc.
+   Copyright (C) 2005, 2006, 2007, 2008, 2009 Red Hat, Inc.
    This file is part of Red Hat elfutils.
 
    Red Hat elfutils is free software; you can redistribute it and/or modify
@@ -288,7 +288,13 @@ __libdwfl_report_offline (Dwfl *dwfl, const char *name,
                          int (*predicate) (const char *module,
                                            const char *file))
 {
-  Elf *elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL);
+  Elf *elf;
+  Dwfl_Error error = __libdw_open_file (&fd, &elf, closefd, true);
+  if (error != DWFL_E_NOERROR)
+    {
+      __libdwfl_seterrno (error);
+      return NULL;
+    }
   Dwfl_Module *mod = process_file (dwfl, name, file_name, fd, elf, predicate);
   if (mod == NULL)
     {
diff --git a/libdwfl/open.c b/libdwfl/open.c
new file mode 100644 (file)
index 0000000..ca6b862
--- /dev/null
@@ -0,0 +1,143 @@
+/* Decompression support for libdwfl: zlib (gzip) and/or bzlib (bzip2).
+   Copyright (C) 2009 Red Hat, Inc.
+   This file is part of Red Hat elfutils.
+
+   Red Hat elfutils 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; version 2 of the License.
+
+   Red Hat elfutils 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+   In addition, as a special exception, Red Hat, Inc. gives You the
+   additional right to link the code of Red Hat elfutils with code licensed
+   under any Open Source Initiative certified open source license
+   (http://www.opensource.org/licenses/index.php) which requires the
+   distribution of source code with any binary distribution and to
+   distribute linked combinations of the two.  Non-GPL Code permitted under
+   this exception must only link to the code of Red Hat elfutils through
+   those well defined interfaces identified in the file named EXCEPTION
+   found in the source code files (the "Approved Interfaces").  The files
+   of Non-GPL Code may instantiate templates or use macros or inline
+   functions from the Approved Interfaces without causing the resulting
+   work to be covered by the GNU General Public License.  Only Red Hat,
+   Inc. may make changes or additions to the list of Approved Interfaces.
+   Red Hat's grant of this exception is conditioned upon your not adding
+   any new exceptions.  If you wish to add a new Approved Interface or
+   exception, please contact Red Hat.  You must obey the GNU General Public
+   License in all respects for all of the Red Hat elfutils code and other
+   code used in conjunction with Red Hat elfutils except the Non-GPL Code
+   covered by this exception.  If you modify this file, you may extend this
+   exception to your version of the file, but you are not obligated to do
+   so.  If you do not wish to provide this exception without modification,
+   you must delete this exception statement from your version and license
+   this file solely under the GPL without exception.
+
+   Red Hat elfutils is an included package of the Open Invention Network.
+   An included package of the Open Invention Network is a package for which
+   Open Invention Network licensees cross-license their patents.  No patent
+   license is granted, either expressly or impliedly, by designation as an
+   included package.  Should you wish to participate in the Open Invention
+   Network licensing program, please visit www.openinventionnetwork.com
+   <http://www.openinventionnetwork.com>.  */
+
+#include "../libelf/libelfP.h"
+#undef _
+#include "libdwflP.h"
+
+#include <unistd.h>
+
+#if !USE_ZLIB
+# define __libdw_gunzip(...)   false
+#endif
+
+#if !USE_BZLIB
+# define __libdw_bunzip2(...)  false
+#endif
+
+/* Always consumes *ELF, never consumes FD.
+   Replaces *ELF on success.  */
+static Dwfl_Error
+decompress (int fd, Elf **elf)
+{
+  Dwfl_Error error = DWFL_E_BADELF;
+
+#if USE_ZLIB || USE_BZLIB
+  void *buffer;
+  size_t size;
+
+  const off64_t offset = (*elf)->start_offset;
+  void *const mapped = ((*elf)->map_address == NULL ? NULL
+                       : (*elf)->map_address + (*elf)->start_offset);
+  const size_t mapped_size = (*elf)->maximum_size;
+
+  error = __libdw_gunzip (fd, offset, mapped, mapped_size, &buffer, &size);
+  if (error == DWFL_E_BADELF)
+    error = __libdw_bunzip2 (fd, offset, mapped, mapped_size, &buffer, &size);
+#endif
+
+  elf_end (*elf);
+  *elf = NULL;
+
+  if (error == DWFL_E_NOERROR)
+    {
+      *elf = elf_memory (buffer, size);
+      if (*elf == NULL)
+       {
+         error = DWFL_E_LIBELF;
+         free (buffer);
+       }
+      else
+       (*elf)->flags |= ELF_F_MALLOCED;
+    }
+
+  return error;
+}
+
+Dwfl_Error internal_function
+__libdw_open_file (int *fdp, Elf **elfp, bool close_on_fail, bool archive_ok)
+{
+  bool close_fd = false;
+  Dwfl_Error error = DWFL_E_NOERROR;
+
+  Elf *elf = elf_begin (*fdp, ELF_C_READ_MMAP_PRIVATE, NULL);
+  Elf_Kind kind = elf_kind (elf);
+  if (unlikely (kind == ELF_K_NONE))
+    {
+      if (unlikely (elf == NULL))
+       error = DWFL_E_LIBELF;
+      else
+       {
+         error = decompress (*fdp, &elf);
+         if (error == DWFL_E_NOERROR)
+           {
+             close_fd = true;
+             kind = elf_kind (elf);
+           }
+       }
+    }
+
+  if (error == DWFL_E_NOERROR
+      && kind != ELF_K_ELF
+      && !(archive_ok && kind == ELF_K_AR))
+    {
+      elf_end (elf);
+      elf = NULL;
+      error = DWFL_E_BADELF;
+    }
+
+  if (error == DWFL_E_NOERROR ? close_fd : close_on_fail)
+    {
+      close (*fdp);
+      *fdp = -1;
+    }
+
+  *elfp = elf;
+  return error;
+}