Squashed 'third_party/elfutils/' content from commit 555e15e

Change-Id: I61cde98949e47e5c8c09c33260de17f30921be79
git-subtree-dir: third_party/elfutils
git-subtree-split: 555e15ebe8bf1eb33d00747173cfc80cc65648a4
diff --git a/libdwfl/gzip.c b/libdwfl/gzip.c
new file mode 100644
index 0000000..c2c13ba
--- /dev/null
+++ b/libdwfl/gzip.c
@@ -0,0 +1,328 @@
+/* Decompression support for libdwfl: zlib (gzip) and/or bzlib (bzip2).
+   Copyright (C) 2009 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   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 copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+#include "system.h"
+
+#include <unistd.h>
+
+#ifdef LZMA
+# define USE_INFLATE	1
+# include <lzma.h>
+# define unzip		__libdw_unlzma
+# define DWFL_E_ZLIB	DWFL_E_LZMA
+# define MAGIC		"\xFD" "7zXZ\0" /* XZ file format.  */
+# define MAGIC2		"\x5d\0"	/* Raw LZMA format.  */
+# define Z(what)	LZMA_##what
+# define LZMA_ERRNO	LZMA_PROG_ERROR
+# define z_stream	lzma_stream
+# define inflateInit(z)	lzma_auto_decoder (z, 1 << 30, 0)
+# define do_inflate(z)	lzma_code (z, LZMA_RUN)
+# define inflateEnd(z)	lzma_end (z)
+#elif defined BZLIB
+# define USE_INFLATE	1
+# include <bzlib.h>
+# define unzip		__libdw_bunzip2
+# define DWFL_E_ZLIB	DWFL_E_BZLIB
+# define MAGIC		"BZh"
+# define Z(what)	BZ_##what
+# define BZ_ERRNO	BZ_IO_ERROR
+# define z_stream	bz_stream
+# define inflateInit(z)	BZ2_bzDecompressInit (z, 0, 0)
+# define do_inflate(z)	BZ2_bzDecompress (z)
+# define inflateEnd(z)	BZ2_bzDecompressEnd (z)
+#else
+# define USE_INFLATE	0
+# define crc32		loser_crc32
+# include <zlib.h>
+# define unzip		__libdw_gunzip
+# define MAGIC		"\037\213"
+# define Z(what)	Z_##what
+#endif
+
+#define READ_SIZE		(1 << 20)
+
+struct unzip_state {
+#if !USE_INFLATE
+  gzFile zf;
+#endif
+  size_t mapped_size;
+  void **whole;
+  void *buffer;
+  size_t size;
+  void *input_buffer;
+  off_t input_pos;
+};
+
+static inline bool
+bigger_buffer (struct unzip_state *state, size_t start)
+{
+  size_t more = state->size ? state->size * 2 : start;
+  char *b = realloc (state->buffer, more);
+  while (unlikely (b == NULL) && more >= state->size + 1024)
+    b = realloc (state->buffer, more -= 1024);
+  if (unlikely (b == NULL))
+    return false;
+  state->buffer = b;
+  state->size = more;
+  return true;
+}
+
+static inline void
+smaller_buffer (struct unzip_state *state, size_t end)
+{
+  state->buffer =
+      realloc (state->buffer, end) ?: end == 0 ? NULL : state->buffer;
+  state->size = end;
+}
+
+static inline Dwfl_Error
+fail (struct unzip_state *state, Dwfl_Error failure)
+{
+  if (state->input_pos == (off_t) state->mapped_size)
+    *state->whole = state->input_buffer;
+  else
+    {
+      free (state->input_buffer);
+      *state->whole = NULL;
+    }
+  free (state->buffer);
+  return failure;
+}
+
+static inline Dwfl_Error
+zlib_fail (struct unzip_state *state, int result)
+{
+  switch (result)
+    {
+    case Z (MEM_ERROR):
+      return fail (state, DWFL_E_NOMEM);
+    case Z (ERRNO):
+      return fail (state, DWFL_E_ERRNO);
+    default:
+      return fail (state, DWFL_E_ZLIB);
+    }
+}
+
+#if !USE_INFLATE
+static Dwfl_Error
+open_stream (int fd, off_t start_offset, struct unzip_state *state)
+{
+    int d = dup (fd);
+    if (unlikely (d < 0))
+      return DWFL_E_BADELF;
+    if (start_offset != 0)
+      {
+	off_t off = lseek (d, start_offset, SEEK_SET);
+	if (off != start_offset)
+	  {
+	    close (d);
+	    return DWFL_E_BADELF;
+	  }
+      }
+    state->zf = gzdopen (d, "r");
+    if (unlikely (state->zf == NULL))
+      {
+	close (d);
+	return zlib_fail (state, Z (MEM_ERROR));
+      }
+
+    /* From here on, zlib will close D.  */
+
+    return DWFL_E_NOERROR;
+}
+#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.
+   If we return an error after reading the first part of the file,
+   leave that portion malloc'd in *WHOLE, *WHOLE_SIZE.  If *WHOLE
+   is not null on entry, we'll use it in lieu of repeating a read.  */
+
+Dwfl_Error internal_function
+unzip (int fd, off_t start_offset,
+       void *mapped, size_t _mapped_size,
+       void **_whole, size_t *whole_size)
+{
+  struct unzip_state state =
+    {
+#if !USE_INFLATE
+      .zf = NULL,
+#endif
+      .mapped_size = _mapped_size,
+      .whole = _whole,
+      .buffer = NULL,
+      .size = 0,
+      .input_buffer = NULL,
+      .input_pos = 0
+    };
+
+  if (mapped == NULL)
+    {
+      if (*state.whole == NULL)
+	{
+	  state.input_buffer = malloc (READ_SIZE);
+	  if (unlikely (state.input_buffer == NULL))
+	    return DWFL_E_NOMEM;
+
+	  ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE, start_offset);
+	  if (unlikely (n < 0))
+	    return zlib_fail (&state, Z (ERRNO));
+
+	  state.input_pos = n;
+	  mapped = state.input_buffer;
+	  state.mapped_size = n;
+	}
+      else
+	{
+	  state.input_buffer = *state.whole;
+	  state.input_pos = state.mapped_size = *whole_size;
+	}
+    }
+
+#define NOMAGIC(magic) \
+  (state.mapped_size <= sizeof magic || \
+   memcmp (mapped, magic, sizeof magic - 1))
+
+  /* First, look at the header.  */
+  if (NOMAGIC (MAGIC)
+#ifdef MAGIC2
+      && NOMAGIC (MAGIC2)
+#endif
+      )
+    /* Not a compressed file.  */
+    return DWFL_E_BADELF;
+
+#if USE_INFLATE
+
+  /* This style actually only works with bzlib and liblzma.
+     The stupid zlib interface has nothing to grok the
+     gzip file headers except the slow gzFile interface.  */
+
+  z_stream z = { .next_in = mapped, .avail_in = state.mapped_size };
+  int result = inflateInit (&z);
+  if (result != Z (OK))
+    {
+      inflateEnd (&z);
+      return zlib_fail (&state, result);
+    }
+
+  do
+    {
+      if (z.avail_in == 0 && state.input_buffer != NULL)
+	{
+	  ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE,
+				   start_offset + state.input_pos);
+	  if (unlikely (n < 0))
+	    {
+	      inflateEnd (&z);
+	      return zlib_fail (&state, Z (ERRNO));
+	    }
+	  z.next_in = state.input_buffer;
+	  z.avail_in = n;
+	  state.input_pos += n;
+	}
+      if (z.avail_out == 0)
+	{
+	  ptrdiff_t pos = (void *) z.next_out - state.buffer;
+	  if (!bigger_buffer (&state, z.avail_in))
+	    {
+	      result = Z (MEM_ERROR);
+	      break;
+	    }
+	  z.next_out = state.buffer + pos;
+	  z.avail_out = state.size - pos;
+	}
+    }
+  while ((result = do_inflate (&z)) == Z (OK));
+
+#ifdef BZLIB
+  uint64_t total_out = (((uint64_t) z.total_out_hi32 << 32)
+			| z.total_out_lo32);
+  smaller_buffer (&state, total_out);
+#else
+  smaller_buffer (&state, z.total_out);
+#endif
+
+  inflateEnd (&z);
+
+  if (result != Z (STREAM_END))
+    return zlib_fail (&state, result);
+
+#else  /* gzip only.  */
+
+  /* Let the decompression library read the file directly.  */
+
+  Dwfl_Error result = open_stream (fd, start_offset, &state);
+
+  if (result == DWFL_E_NOERROR && gzdirect (state.zf))
+    {
+      gzclose (state.zf);
+      return fail (&state, DWFL_E_BADELF);
+    }
+
+  if (result != DWFL_E_NOERROR)
+    return fail (&state, result);
+
+  ptrdiff_t pos = 0;
+  while (1)
+    {
+      if (!bigger_buffer (&state, 1024))
+	{
+	  gzclose (state.zf);
+	  return zlib_fail (&state, Z (MEM_ERROR));
+	}
+      int n = gzread (state.zf, state.buffer + pos, state.size - pos);
+      if (n < 0)
+	{
+	  int code;
+	  gzerror (state.zf, &code);
+	  gzclose (state.zf);
+	  return zlib_fail (&state, code);
+	}
+      if (n == 0)
+	break;
+      pos += n;
+    }
+
+  gzclose (state.zf);
+  smaller_buffer (&state, pos);
+#endif
+
+  free (state.input_buffer);
+
+  *state.whole = state.buffer;
+  *whole_size = state.size;
+
+  return DWFL_E_NOERROR;
+}