Blame libelf/elf_compress.c

Packit Service 97d2fb
/* Compress or decompress a section.
Packit Service 97d2fb
   Copyright (C) 2015, 2016 Red Hat, Inc.
Packit Service 97d2fb
   This file is part of elfutils.
Packit Service 97d2fb
Packit Service 97d2fb
   This file is free software; you can redistribute it and/or modify
Packit Service 97d2fb
   it under the terms of either
Packit Service 97d2fb
Packit Service 97d2fb
     * the GNU Lesser General Public License as published by the Free
Packit Service 97d2fb
       Software Foundation; either version 3 of the License, or (at
Packit Service 97d2fb
       your option) any later version
Packit Service 97d2fb
Packit Service 97d2fb
   or
Packit Service 97d2fb
Packit Service 97d2fb
     * the GNU General Public License as published by the Free
Packit Service 97d2fb
       Software Foundation; either version 2 of the License, or (at
Packit Service 97d2fb
       your option) any later version
Packit Service 97d2fb
Packit Service 97d2fb
   or both in parallel, as here.
Packit Service 97d2fb
Packit Service 97d2fb
   elfutils is distributed in the hope that it will be useful, but
Packit Service 97d2fb
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 97d2fb
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 97d2fb
   General Public License for more details.
Packit Service 97d2fb
Packit Service 97d2fb
   You should have received copies of the GNU General Public License and
Packit Service 97d2fb
   the GNU Lesser General Public License along with this program.  If
Packit Service 97d2fb
   not, see <http://www.gnu.org/licenses/>.  */
Packit Service 97d2fb
Packit Service 97d2fb
#ifdef HAVE_CONFIG_H
Packit Service 97d2fb
# include <config.h>
Packit Service 97d2fb
#endif
Packit Service 97d2fb
Packit Service 97d2fb
#include <libelf.h>
Packit Service 97d2fb
#include <system.h>
Packit Service 97d2fb
#include "libelfP.h"
Packit Service 97d2fb
#include "common.h"
Packit Service 97d2fb
Packit Service 97d2fb
#include <stddef.h>
Packit Service 97d2fb
#include <stdlib.h>
Packit Service 97d2fb
#include <string.h>
Packit Service 97d2fb
#include <unistd.h>
Packit Service 97d2fb
#include <zlib.h>
Packit Service 97d2fb
Packit Service 97d2fb
/* Cleanup and return result.  Don't leak memory.  */
Packit Service 97d2fb
static void *
Packit Service 97d2fb
do_deflate_cleanup (void *result, z_stream *z, void *out_buf,
Packit Service 97d2fb
                    Elf_Data *cdatap)
Packit Service 97d2fb
{
Packit Service 97d2fb
  deflateEnd (z);
Packit Service 97d2fb
  free (out_buf);
Packit Service 97d2fb
  if (cdatap != NULL)
Packit Service 97d2fb
    free (cdatap->d_buf);
Packit Service 97d2fb
  return result;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
#define deflate_cleanup(result, cdata) \
Packit Service 97d2fb
    do_deflate_cleanup(result, &z, out_buf, cdata)
Packit Service 97d2fb
Packit Service 97d2fb
/* Given a section, uses the (in-memory) Elf_Data to extract the
Packit Service 97d2fb
   original data size (including the given header size) and data
Packit Service 97d2fb
   alignment.  Returns a buffer that has at least hsize bytes (for the
Packit Service 97d2fb
   caller to fill in with a header) plus zlib compressed date.  Also
Packit Service 97d2fb
   returns the new buffer size in new_size (hsize + compressed data
Packit Service 97d2fb
   size).  Returns (void *) -1 when FORCE is false and the compressed
Packit Service 97d2fb
   data would be bigger than the original data.  */
Packit Service 97d2fb
void *
Packit Service 97d2fb
internal_function
Packit Service 97d2fb
__libelf_compress (Elf_Scn *scn, size_t hsize, int ei_data,
Packit Service 97d2fb
		   size_t *orig_size, size_t *orig_addralign,
Packit Service 97d2fb
		   size_t *new_size, bool force)
Packit Service 97d2fb
{
Packit Service 97d2fb
  /* The compressed data is the on-disk data.  We simplify the
Packit Service 97d2fb
     implementation a bit by asking for the (converted) in-memory
Packit Service 97d2fb
     data (which might be all there is if the user created it with
Packit Service 97d2fb
     elf_newdata) and then convert back to raw if needed before
Packit Service 97d2fb
     compressing.  Should be made a bit more clever to directly
Packit Service 97d2fb
     use raw if that is directly available.  */
Packit Service 97d2fb
  Elf_Data *data = elf_getdata (scn, NULL);
Packit Service 97d2fb
  if (data == NULL)
Packit Service 97d2fb
    return NULL;
Packit Service 97d2fb
Packit Service 97d2fb
  /* When not forced and we immediately know we would use more data by
Packit Service 97d2fb
     compressing, because of the header plus zlib overhead (five bytes
Packit Service 97d2fb
     per 16 KB block, plus a one-time overhead of six bytes for the
Packit Service 97d2fb
     entire stream), don't do anything.  */
Packit Service 97d2fb
  Elf_Data *next_data = elf_getdata (scn, data);
Packit Service 97d2fb
  if (next_data == NULL && !force
Packit Service 97d2fb
      && data->d_size <= hsize + 5 + 6)
Packit Service 97d2fb
    return (void *) -1;
Packit Service 97d2fb
Packit Service 97d2fb
  *orig_addralign = data->d_align;
Packit Service 97d2fb
  *orig_size = data->d_size;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Guess an output block size. 1/8th of the original Elf_Data plus
Packit Service 97d2fb
     hsize.  Make the first chunk twice that size (25%), then increase
Packit Service 97d2fb
     by a block (12.5%) when necessary.  */
Packit Service 97d2fb
  size_t block = (data->d_size / 8) + hsize;
Packit Service 97d2fb
  size_t out_size = 2 * block;
Packit Service 97d2fb
  void *out_buf = malloc (out_size);
Packit Service 97d2fb
  if (out_buf == NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libelf_seterrno (ELF_E_NOMEM);
Packit Service 97d2fb
      return NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* Caller gets to fill in the header at the start.  Just skip it here.  */
Packit Service 97d2fb
  size_t used = hsize;
Packit Service 97d2fb
Packit Service 97d2fb
  z_stream z;
Packit Service 97d2fb
  z.zalloc = Z_NULL;
Packit Service 97d2fb
  z.zfree = Z_NULL;
Packit Service 97d2fb
  z.opaque = Z_NULL;
Packit Service 97d2fb
  int zrc = deflateInit (&z, Z_BEST_COMPRESSION);
Packit Service 97d2fb
  if (zrc != Z_OK)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libelf_seterrno (ELF_E_COMPRESS_ERROR);
Packit Service 97d2fb
      return deflate_cleanup(NULL, NULL);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  Elf_Data cdata;
Packit Service 97d2fb
  cdata.d_buf = NULL;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Loop over data buffers.  */
Packit Service 97d2fb
  int flush = Z_NO_FLUSH;
Packit Service 97d2fb
  do
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* Convert to raw if different endianess.  */
Packit Service 97d2fb
      cdata = *data;
Packit Service 97d2fb
      bool convert = ei_data != MY_ELFDATA && data->d_size > 0;
Packit Service 97d2fb
      if (convert)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* Don't do this conversion in place, we might want to keep
Packit Service 97d2fb
	     the original data around, caller decides.  */
Packit Service 97d2fb
	  cdata.d_buf = malloc (data->d_size);
Packit Service 97d2fb
	  if (cdata.d_buf == NULL)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      __libelf_seterrno (ELF_E_NOMEM);
Packit Service 97d2fb
	      return deflate_cleanup (NULL, NULL);
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  if (gelf_xlatetof (scn->elf, &cdata, data, ei_data) == NULL)
Packit Service 97d2fb
	    return deflate_cleanup (NULL, &cdata);
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      z.avail_in = cdata.d_size;
Packit Service 97d2fb
      z.next_in = cdata.d_buf;
Packit Service 97d2fb
Packit Service 97d2fb
      /* Get next buffer to see if this is the last one.  */
Packit Service 97d2fb
      data = next_data;
Packit Service 97d2fb
      if (data != NULL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  *orig_addralign = MAX (*orig_addralign, data->d_align);
Packit Service 97d2fb
	  *orig_size += data->d_size;
Packit Service 97d2fb
	  next_data = elf_getdata (scn, data);
Packit Service 97d2fb
	}
Packit Service 97d2fb
      else
Packit Service 97d2fb
	flush = Z_FINISH;
Packit Service 97d2fb
Packit Service 97d2fb
      /* Flush one data buffer.  */
Packit Service 97d2fb
      do
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  z.avail_out = out_size - used;
Packit Service 97d2fb
	  z.next_out = out_buf + used;
Packit Service 97d2fb
	  zrc = deflate (&z, flush);
Packit Service 97d2fb
	  if (zrc == Z_STREAM_ERROR)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      __libelf_seterrno (ELF_E_COMPRESS_ERROR);
Packit Service 97d2fb
	      return deflate_cleanup (NULL, convert ? &cdata : NULL);
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  used += (out_size - used) - z.avail_out;
Packit Service 97d2fb
Packit Service 97d2fb
	  /* Bail out if we are sure the user doesn't want the
Packit Service 97d2fb
	     compression forced and we are using more compressed data
Packit Service 97d2fb
	     than original data.  */
Packit Service 97d2fb
	  if (!force && flush == Z_FINISH && used >= *orig_size)
Packit Service 97d2fb
	    return deflate_cleanup ((void *) -1, convert ? &cdata : NULL);
Packit Service 97d2fb
Packit Service 97d2fb
	  if (z.avail_out == 0)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      void *bigger = realloc (out_buf, out_size + block);
Packit Service 97d2fb
	      if (bigger == NULL)
Packit Service 97d2fb
		{
Packit Service 97d2fb
		  __libelf_seterrno (ELF_E_NOMEM);
Packit Service 97d2fb
		  return deflate_cleanup (NULL, convert ? &cdata : NULL);
Packit Service 97d2fb
		}
Packit Service 97d2fb
	      out_buf = bigger;
Packit Service 97d2fb
	      out_size += block;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	}
Packit Service 97d2fb
      while (z.avail_out == 0); /* Need more output buffer.  */
Packit Service 97d2fb
Packit Service 97d2fb
      if (convert)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  free (cdata.d_buf);
Packit Service 97d2fb
	  cdata.d_buf = NULL;
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
  while (flush != Z_FINISH); /* More data blocks.  */
Packit Service 97d2fb
Packit Service 97d2fb
  if (zrc != Z_STREAM_END)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libelf_seterrno (ELF_E_COMPRESS_ERROR);
Packit Service 97d2fb
      return deflate_cleanup (NULL, NULL);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  deflateEnd (&z);
Packit Service 97d2fb
  *new_size = used;
Packit Service 97d2fb
  return out_buf;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
void *
Packit Service 97d2fb
internal_function
Packit Service 97d2fb
__libelf_decompress (void *buf_in, size_t size_in, size_t size_out)
Packit Service 97d2fb
{
Packit Service 97d2fb
  /* Catch highly unlikely compression ratios so we don't allocate
Packit Service 97d2fb
     some giant amount of memory for nothing. The max compression
Packit Service 97d2fb
     factor 1032:1 comes from http://www.zlib.net/zlib_tech.html  */
Packit Service 97d2fb
  if (unlikely (size_out / 1032 > size_in))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libelf_seterrno (ELF_E_INVALID_DATA);
Packit Service 97d2fb
      return NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* Malloc might return NULL when requestion zero size.  This is highly
Packit Service 97d2fb
     unlikely, it would only happen when the compression was forced.
Packit Service 97d2fb
     But we do need a non-NULL buffer to return and set as result.
Packit Service 97d2fb
     Just make sure to always allocate at least 1 byte.  */
Packit Service 97d2fb
  void *buf_out = malloc (size_out ?: 1);
Packit Service 97d2fb
  if (unlikely (buf_out == NULL))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libelf_seterrno (ELF_E_NOMEM);
Packit Service 97d2fb
      return NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  z_stream z =
Packit Service 97d2fb
    {
Packit Service 97d2fb
      .next_in = buf_in,
Packit Service 97d2fb
      .avail_in = size_in,
Packit Service 97d2fb
      .next_out = buf_out,
Packit Service 97d2fb
      .avail_out = size_out
Packit Service 97d2fb
    };
Packit Service 97d2fb
  int zrc = inflateInit (&z);
Packit Service 97d2fb
  while (z.avail_in > 0 && likely (zrc == Z_OK))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      z.next_out = buf_out + (size_out - z.avail_out);
Packit Service 97d2fb
      zrc = inflate (&z, Z_FINISH);
Packit Service 97d2fb
      if (unlikely (zrc != Z_STREAM_END))
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  zrc = Z_DATA_ERROR;
Packit Service 97d2fb
	  break;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      zrc = inflateReset (&z);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (unlikely (zrc != Z_OK) || unlikely (z.avail_out != 0))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      free (buf_out);
Packit Service 97d2fb
      buf_out = NULL;
Packit Service 97d2fb
      __libelf_seterrno (ELF_E_DECOMPRESS_ERROR);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  inflateEnd(&z);
Packit Service 97d2fb
  return buf_out;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
void *
Packit Service 97d2fb
internal_function
Packit Service 97d2fb
__libelf_decompress_elf (Elf_Scn *scn, size_t *size_out, size_t *addralign)
Packit Service 97d2fb
{
Packit Service 97d2fb
  GElf_Chdr chdr;
Packit Service 97d2fb
  if (gelf_getchdr (scn, &chdr) == NULL)
Packit Service 97d2fb
    return NULL;
Packit Service 97d2fb
Packit Service 97d2fb
  if (chdr.ch_type != ELFCOMPRESS_ZLIB)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
Packit Service 97d2fb
      return NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (! powerof2 (chdr.ch_addralign))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libelf_seterrno (ELF_E_INVALID_ALIGN);
Packit Service 97d2fb
      return NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* Take the in-memory representation, so we can even handle a
Packit Service 97d2fb
     section that has just been constructed (maybe it was copied
Packit Service 97d2fb
     over from some other ELF file first with elf_newdata).  This
Packit Service 97d2fb
     is slightly inefficient when the raw data needs to be
Packit Service 97d2fb
     converted since then we'll be converting the whole buffer and
Packit Service 97d2fb
     not just Chdr.  */
Packit Service 97d2fb
  Elf_Data *data = elf_getdata (scn, NULL);
Packit Service 97d2fb
  if (data == NULL)
Packit Service 97d2fb
    return NULL;
Packit Service 97d2fb
Packit Service 97d2fb
  int elfclass = scn->elf->class;
Packit Service 97d2fb
  size_t hsize = (elfclass == ELFCLASS32
Packit Service 97d2fb
		  ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
Packit Service 97d2fb
  size_t size_in = data->d_size - hsize;
Packit Service 97d2fb
  void *buf_in = data->d_buf + hsize;
Packit Service 97d2fb
  void *buf_out = __libelf_decompress (buf_in, size_in, chdr.ch_size);
Packit Service 97d2fb
  *size_out = chdr.ch_size;
Packit Service 97d2fb
  *addralign = chdr.ch_addralign;
Packit Service 97d2fb
  return buf_out;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
/* Assumes buf is a malloced buffer.  */
Packit Service 97d2fb
void
Packit Service 97d2fb
internal_function
Packit Service 97d2fb
__libelf_reset_rawdata (Elf_Scn *scn, void *buf, size_t size, size_t align,
Packit Service 97d2fb
			Elf_Type type)
Packit Service 97d2fb
{
Packit Service 97d2fb
  /* This is the new raw data, replace and possibly free old data.  */
Packit Service 97d2fb
  scn->rawdata.d.d_off = 0;
Packit Service 97d2fb
  scn->rawdata.d.d_version = EV_CURRENT;
Packit Service 97d2fb
  scn->rawdata.d.d_buf = buf;
Packit Service 97d2fb
  scn->rawdata.d.d_size = size;
Packit Service 97d2fb
  scn->rawdata.d.d_align = align;
Packit Service 97d2fb
  scn->rawdata.d.d_type = type;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Existing existing data is no longer valid.  */
Packit Service 97d2fb
  scn->data_list_rear = NULL;
Packit Service 97d2fb
  if (scn->data_base != scn->rawdata_base)
Packit Service 97d2fb
    free (scn->data_base);
Packit Service 97d2fb
  scn->data_base = NULL;
Packit Service 97d2fb
  if (scn->elf->map_address == NULL
Packit Service 97d2fb
      || scn->rawdata_base == scn->zdata_base
Packit Service 97d2fb
      || (scn->flags & ELF_F_MALLOCED) != 0)
Packit Service 97d2fb
    free (scn->rawdata_base);
Packit Service 97d2fb
Packit Service 97d2fb
  scn->rawdata_base = buf;
Packit Service 97d2fb
  scn->flags |= ELF_F_MALLOCED;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Pretend we (tried to) read the data from the file and setup the
Packit Service 97d2fb
     data (might have to convert the Chdr to native format).  */
Packit Service 97d2fb
  scn->data_read = 1;
Packit Service 97d2fb
  scn->flags |= ELF_F_FILEDATA;
Packit Service 97d2fb
  __libelf_set_data_list_rdlock (scn, 1);
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
int
Packit Service 97d2fb
elf_compress (Elf_Scn *scn, int type, unsigned int flags)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (scn == NULL)
Packit Service 97d2fb
    return -1;
Packit Service 97d2fb
Packit Service 97d2fb
  if ((flags & ~ELF_CHF_FORCE) != 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libelf_seterrno (ELF_E_INVALID_OPERAND);
Packit Service 97d2fb
      return -1;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  bool force = (flags & ELF_CHF_FORCE) != 0;
Packit Service 97d2fb
Packit Service 97d2fb
  Elf *elf = scn->elf;
Packit Service 97d2fb
  GElf_Ehdr ehdr;
Packit Service 97d2fb
  if (gelf_getehdr (elf, &ehdr) == NULL)
Packit Service 97d2fb
    return -1;
Packit Service 97d2fb
Packit Service 97d2fb
  int elfclass = elf->class;
Packit Service 97d2fb
  int elfdata = ehdr.e_ident[EI_DATA];
Packit Service 97d2fb
Packit Service 97d2fb
  Elf64_Xword sh_flags;
Packit Service 97d2fb
  Elf64_Word sh_type;
Packit Service 97d2fb
  Elf64_Xword sh_addralign;
Packit Service 97d2fb
  if (elfclass == ELFCLASS32)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      Elf32_Shdr *shdr = elf32_getshdr (scn);
Packit Service 97d2fb
      if (shdr == NULL)
Packit Service 97d2fb
	return -1;
Packit Service 97d2fb
Packit Service 97d2fb
      sh_flags = shdr->sh_flags;
Packit Service 97d2fb
      sh_type = shdr->sh_type;
Packit Service 97d2fb
      sh_addralign = shdr->sh_addralign;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  else
Packit Service 97d2fb
    {
Packit Service 97d2fb
      Elf64_Shdr *shdr = elf64_getshdr (scn);
Packit Service 97d2fb
      if (shdr == NULL)
Packit Service 97d2fb
	return -1;
Packit Service 97d2fb
Packit Service 97d2fb
      sh_flags = shdr->sh_flags;
Packit Service 97d2fb
      sh_type = shdr->sh_type;
Packit Service 97d2fb
      sh_addralign = shdr->sh_addralign;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if ((sh_flags & SHF_ALLOC) != 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libelf_seterrno (ELF_E_INVALID_SECTION_FLAGS);
Packit Service 97d2fb
      return -1;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (sh_type == SHT_NULL || sh_type == SHT_NOBITS)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libelf_seterrno (ELF_E_INVALID_SECTION_TYPE);
Packit Service 97d2fb
      return -1;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  int compressed = (sh_flags & SHF_COMPRESSED);
Packit Service 97d2fb
  if (type == ELFCOMPRESS_ZLIB)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* Compress/Deflate.  */
Packit Service 97d2fb
      if (compressed == 1)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  __libelf_seterrno (ELF_E_ALREADY_COMPRESSED);
Packit Service 97d2fb
	  return -1;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      size_t hsize = (elfclass == ELFCLASS32
Packit Service 97d2fb
		      ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
Packit Service 97d2fb
      size_t orig_size, orig_addralign, new_size;
Packit Service 97d2fb
      void *out_buf = __libelf_compress (scn, hsize, elfdata,
Packit Service 97d2fb
					 &orig_size, &orig_addralign,
Packit Service 97d2fb
					 &new_size, force);
Packit Service 97d2fb
Packit Service 97d2fb
      /* Compression would make section larger, don't change anything.  */
Packit Service 97d2fb
      if (out_buf == (void *) -1)
Packit Service 97d2fb
	return 0;
Packit Service 97d2fb
Packit Service 97d2fb
      /* Compression failed, return error.  */
Packit Service 97d2fb
      if (out_buf == NULL)
Packit Service 97d2fb
	return -1;
Packit Service 97d2fb
Packit Service 97d2fb
      /* Put the header in front of the data.  */
Packit Service 97d2fb
      if (elfclass == ELFCLASS32)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  Elf32_Chdr chdr;
Packit Service 97d2fb
	  chdr.ch_type = ELFCOMPRESS_ZLIB;
Packit Service 97d2fb
	  chdr.ch_size = orig_size;
Packit Service 97d2fb
	  chdr.ch_addralign = orig_addralign;
Packit Service 97d2fb
	  if (elfdata != MY_ELFDATA)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      CONVERT (chdr.ch_type);
Packit Service 97d2fb
	      CONVERT (chdr.ch_size);
Packit Service 97d2fb
	      CONVERT (chdr.ch_addralign);
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  memcpy (out_buf, &chdr, sizeof (Elf32_Chdr));
Packit Service 97d2fb
	}
Packit Service 97d2fb
      else
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  Elf64_Chdr chdr;
Packit Service 97d2fb
	  chdr.ch_type = ELFCOMPRESS_ZLIB;
Packit Service 97d2fb
	  chdr.ch_reserved = 0;
Packit Service 97d2fb
	  chdr.ch_size = orig_size;
Packit Service 97d2fb
	  chdr.ch_addralign = sh_addralign;
Packit Service 97d2fb
	  if (elfdata != MY_ELFDATA)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      CONVERT (chdr.ch_type);
Packit Service 97d2fb
	      CONVERT (chdr.ch_reserved);
Packit Service 97d2fb
	      CONVERT (chdr.ch_size);
Packit Service 97d2fb
	      CONVERT (chdr.ch_addralign);
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  memcpy (out_buf, &chdr, sizeof (Elf64_Chdr));
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      /* Note we keep the sh_entsize as is, we assume it is setup
Packit Service 97d2fb
	 correctly and ignored when SHF_COMPRESSED is set.  */
Packit Service 97d2fb
      if (elfclass == ELFCLASS32)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  Elf32_Shdr *shdr = elf32_getshdr (scn);
Packit Service 97d2fb
	  shdr->sh_size = new_size;
Packit Service 97d2fb
	  shdr->sh_addralign = __libelf_type_align (ELFCLASS32, ELF_T_CHDR);
Packit Service 97d2fb
	  shdr->sh_flags |= SHF_COMPRESSED;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      else
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  Elf64_Shdr *shdr = elf64_getshdr (scn);
Packit Service 97d2fb
	  shdr->sh_size = new_size;
Packit Service 97d2fb
	  shdr->sh_addralign = __libelf_type_align (ELFCLASS64, ELF_T_CHDR);
Packit Service 97d2fb
	  shdr->sh_flags |= SHF_COMPRESSED;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      __libelf_reset_rawdata (scn, out_buf, new_size, 1, ELF_T_CHDR);
Packit Service 97d2fb
Packit Service 97d2fb
      /* The section is now compressed, we could keep the uncompressed
Packit Service 97d2fb
	 data around, but since that might have been multiple Elf_Data
Packit Service 97d2fb
	 buffers let the user uncompress it explicitly again if they
Packit Service 97d2fb
	 want it to simplify bookkeeping.  */
Packit Service 97d2fb
      scn->zdata_base = NULL;
Packit Service 97d2fb
Packit Service 97d2fb
      return 1;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  else if (type == 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* Decompress/Inflate.  */
Packit Service 97d2fb
      if (compressed == 0)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  __libelf_seterrno (ELF_E_NOT_COMPRESSED);
Packit Service 97d2fb
	  return -1;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      /* If the data is already decompressed (by elf_strptr), then we
Packit Service 97d2fb
	 only need to setup the rawdata and section header. XXX what
Packit Service 97d2fb
	 about elf_newdata?  */
Packit Service 97d2fb
      if (scn->zdata_base == NULL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  size_t size_out, addralign;
Packit Service 97d2fb
	  void *buf_out = __libelf_decompress_elf (scn, &size_out, &addralign);
Packit Service 97d2fb
	  if (buf_out == NULL)
Packit Service 97d2fb
	    return -1;
Packit Service 97d2fb
Packit Service 97d2fb
	  scn->zdata_base = buf_out;
Packit Service 97d2fb
	  scn->zdata_size = size_out;
Packit Service 97d2fb
	  scn->zdata_align = addralign;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      /* Note we keep the sh_entsize as is, we assume it is setup
Packit Service 97d2fb
	 correctly and ignored when SHF_COMPRESSED is set.  */
Packit Service 97d2fb
      if (elfclass == ELFCLASS32)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  Elf32_Shdr *shdr = elf32_getshdr (scn);
Packit Service 97d2fb
	  shdr->sh_size = scn->zdata_size;
Packit Service 97d2fb
	  shdr->sh_addralign = scn->zdata_align;
Packit Service 97d2fb
	  shdr->sh_flags &= ~SHF_COMPRESSED;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      else
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  Elf64_Shdr *shdr = elf64_getshdr (scn);
Packit Service 97d2fb
	  shdr->sh_size = scn->zdata_size;
Packit Service 97d2fb
	  shdr->sh_addralign = scn->zdata_align;
Packit Service 97d2fb
	  shdr->sh_flags &= ~SHF_COMPRESSED;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      __libelf_reset_rawdata (scn, scn->zdata_base,
Packit Service 97d2fb
			      scn->zdata_size, scn->zdata_align,
Packit Service 97d2fb
			      __libelf_data_type (elf, sh_type,
Packit Service 97d2fb
						  scn->zdata_align));
Packit Service 97d2fb
Packit Service 97d2fb
      return 1;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  else
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
Packit Service 97d2fb
      return -1;
Packit Service 97d2fb
    }
Packit Service 97d2fb
}