]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
Grok Linux kernel zImage format.
authorRoland McGrath <roland@redhat.com>
Wed, 14 Jan 2009 11:01:49 +0000 (03:01 -0800)
committerRoland McGrath <roland@redhat.com>
Wed, 14 Jan 2009 11:01:49 +0000 (03:01 -0800)
libdwfl/gzip.c

index ed602a6669535e21b2068226474ccf3fcc0a8c63..6644525416afb8fca0f4c112662c97050e999771 100644 (file)
@@ -53,6 +53,7 @@
 
 #ifdef BZLIB
 # define inflate_groks_header  true
+# define mapped_zImage(...)    false
 # include <bzlib.h>
 # define unzip         __libdw_bunzip2
 # define DWFL_E_ZLIB   DWFL_E_BZLIB
 # define gzdopen       BZ2_bzdopen
 # define gzread                BZ2_bzread
 # define gzclose       BZ2_bzclose
+# define gzerror       BZ2_bzerror
 #else
 # define inflate_groks_header  false
 # include <zlib.h>
 # define unzip         __libdw_gunzip
 # define MAGIC         "\037\213"
 # define Z(what)       Z_##what
+
+/* We can also handle Linux kernel zImage format in a very hackish way.
+   If it looks like one, we actually just scan the image for the gzip
+   magic bytes to figure out where the gzip image starts.  */
+
+# define LINUX_MAGIC_OFFSET    514
+# define LINUX_MAGIC           "HdrS"
+
+static bool
+mapped_zImage (off64_t *start_offset, void **mapped, size_t *mapped_size)
+{
+  const size_t pos = LINUX_MAGIC_OFFSET + sizeof LINUX_MAGIC;
+  if (*mapped_size > pos
+      && !memcmp (*mapped + LINUX_MAGIC_OFFSET,
+                 LINUX_MAGIC, sizeof LINUX_MAGIC - 1))
+    {
+      void *p = memmem (*mapped + pos, *mapped_size - pos,
+                       MAGIC, sizeof MAGIC - 1);
+      if (p != NULL)
+       {
+         *start_offset += p - *mapped;
+         *mapped_size = *mapped + *mapped_size - p,
+         *mapped = p;
+         return true;
+       }
+    }
+  return false;
+}
 #endif
 
 /* If this is not a compressed image, return DWFL_E_BADELF.
@@ -120,7 +150,8 @@ unzip (int fd, off64_t start_offset,
   /* If the file is already mapped in, look at the header.  */
   if (mapped != NULL
       && (mapped_size <= sizeof MAGIC
-         || memcmp (mapped, MAGIC, sizeof MAGIC - 1)))
+         || memcmp (mapped, MAGIC, sizeof MAGIC - 1))
+      && !mapped_zImage (&start_offset, &mapped, &mapped_size))
     /* Not a compressed file.  */
     return DWFL_E_BADELF;
 
@@ -165,35 +196,69 @@ unzip (int fd, off64_t start_offset,
     {
       /* Let the decompression library read the file directly.  */
 
-      int d = dup (fd);
-      if (unlikely (d < 0))
-       return DWFL_E_BADELF;
-      if (start_offset != 0)
-       {
-         off64_t off = lseek (d, start_offset, SEEK_SET);
-         if (off != start_offset)
-           {
-             close (d);
-             return DWFL_E_BADELF;
-           }
-       }
-      gzFile zf = gzdopen (d, "r");
-      if (unlikely (zf == NULL))
-       {
-         close (d);
-         return zlib_fail (Z (MEM_ERROR));
-       }
+      gzFile zf;
+      Dwfl_Error open_stream (void)
+      {
+       int d = dup (fd);
+       if (unlikely (d < 0))
+         return DWFL_E_BADELF;
+       if (start_offset != 0)
+         {
+           off64_t off = lseek (d, start_offset, SEEK_SET);
+           if (off != start_offset)
+             {
+               close (d);
+               return DWFL_E_BADELF;
+             }
+         }
+       zf = gzdopen (d, "r");
+       if (unlikely (zf == NULL))
+         {
+           close (d);
+           return zlib_fail (Z (MEM_ERROR));
+         }
+
+       /* From here on, zlib will close D.  */
+
+       return DWFL_E_NOERROR;
+      }
 
-      /* From here on, zlib will close D.  */
+      Dwfl_Error result = open_stream ();
 
 #ifndef BZLIB
-      if (gzdirect (zf))
+      if (result == DWFL_E_NOERROR && gzdirect (zf))
        {
+         bool found = false;
+         char buf[sizeof LINUX_MAGIC - 1];
+         gzseek (zf, start_offset + LINUX_MAGIC_OFFSET, SEEK_SET);
+         int n = gzread (zf, buf, sizeof buf);
+         if (n == sizeof buf
+             && !memcmp (buf, LINUX_MAGIC, sizeof LINUX_MAGIC - 1))
+           while (gzread (zf, buf, sizeof MAGIC - 1) == sizeof MAGIC - 1)
+             if (!memcmp (buf, MAGIC, sizeof MAGIC - 1))
+               {
+                 start_offset = gztell (zf) - 2;
+                 found = true;
+                 break;
+               }
          gzclose (zf);
-         return DWFL_E_BADELF;
+         if (found)
+           {
+             result = open_stream ();
+             if (result == DWFL_E_NOERROR && unlikely (gzdirect (zf)))
+               {
+                 gzclose (zf);
+                 result = DWFL_E_BADELF;
+               }
+           }
+         else
+           result = DWFL_E_BADELF;
        }
 #endif
 
+      if (result != DWFL_E_NOERROR)
+       return result;
+
       ptrdiff_t pos = 0;
       while (1)
        {
@@ -205,9 +270,9 @@ unzip (int fd, off64_t start_offset,
          int n = gzread (zf, buffer + pos, size - pos);
          if (n < 0)
            {
-#ifdef BZLIB
              int code;
-             BZ2_bzerror (zf, &code);
+             gzerror (zf, &code);
+#ifdef BZLIB
              if (code == BZ_DATA_ERROR_MAGIC)
                {
                  gzclose (zf);
@@ -216,7 +281,7 @@ unzip (int fd, off64_t start_offset,
                }
 #endif
              gzclose (zf);
-             return zlib_fail (Z (DATA_ERROR));
+             return zlib_fail (code);
            }
          if (n == 0)
            break;