Home | History | Annotate | Download | only in libelf
      1 /* Compress or decompress a section.
      2    Copyright (C) 2015 Red Hat, Inc.
      3    This file is part of elfutils.
      4 
      5    This file is free software; you can redistribute it and/or modify
      6    it under the terms of either
      7 
      8      * the GNU Lesser General Public License as published by the Free
      9        Software Foundation; either version 3 of the License, or (at
     10        your option) any later version
     11 
     12    or
     13 
     14      * the GNU General Public License as published by the Free
     15        Software Foundation; either version 2 of the License, or (at
     16        your option) any later version
     17 
     18    or both in parallel, as here.
     19 
     20    elfutils is distributed in the hope that it will be useful, but
     21    WITHOUT ANY WARRANTY; without even the implied warranty of
     22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     23    General Public License for more details.
     24 
     25    You should have received copies of the GNU General Public License and
     26    the GNU Lesser General Public License along with this program.  If
     27    not, see <http://www.gnu.org/licenses/>.  */
     28 
     29 #ifdef HAVE_CONFIG_H
     30 # include <config.h>
     31 #endif
     32 
     33 #include <libelf.h>
     34 #include "libelfP.h"
     35 #include "common.h"
     36 
     37 #include <stddef.h>
     38 #include <stdlib.h>
     39 #include <string.h>
     40 #include <sys/param.h>
     41 #include <unistd.h>
     42 #include <zlib.h>
     43 
     44 #ifndef MAX
     45 # define MAX(a, b) ((a) > (b) ? (a) : (b))
     46 #endif
     47 
     48 /* Cleanup and return result.  Don't leak memory.  */
     49 static void *
     50 do_deflate_cleanup (void *result, z_stream *z, void *out_buf,
     51                     int ei_data, Elf_Data *cdatap)
     52 {
     53   deflateEnd (z);
     54   free (out_buf);
     55   if (ei_data != MY_ELFDATA)
     56     free (cdatap->d_buf);
     57   return result;
     58 }
     59 
     60 #define deflate_cleanup(result) \
     61     do_deflate_cleanup(result, &z, out_buf, ei_data, &cdata)
     62 
     63 /* Given a section, uses the (in-memory) Elf_Data to extract the
     64    original data size (including the given header size) and data
     65    alignment.  Returns a buffer that has at least hsize bytes (for the
     66    caller to fill in with a header) plus zlib compressed date.  Also
     67    returns the new buffer size in new_size (hsize + compressed data
     68    size).  Returns (void *) -1 when FORCE is false and the compressed
     69    data would be bigger than the original data.  */
     70 void *
     71 internal_function
     72 __libelf_compress (Elf_Scn *scn, size_t hsize, int ei_data,
     73 		   size_t *orig_size, size_t *orig_addralign,
     74 		   size_t *new_size, bool force)
     75 {
     76   /* The compressed data is the on-disk data.  We simplify the
     77      implementation a bit by asking for the (converted) in-memory
     78      data (which might be all there is if the user created it with
     79      elf_newdata) and then convert back to raw if needed before
     80      compressing.  Should be made a bit more clever to directly
     81      use raw if that is directly available.  */
     82   Elf_Data *data = elf_getdata (scn, NULL);
     83   if (data == NULL)
     84     return NULL;
     85 
     86   /* When not forced and we immediately know we would use more data by
     87      compressing, because of the header plus zlib overhead (five bytes
     88      per 16 KB block, plus a one-time overhead of six bytes for the
     89      entire stream), don't do anything.  */
     90   Elf_Data *next_data = elf_getdata (scn, data);
     91   if (next_data == NULL && !force
     92       && data->d_size <= hsize + 5 + 6)
     93     return (void *) -1;
     94 
     95   *orig_addralign = data->d_align;
     96   *orig_size = data->d_size;
     97 
     98   /* Guess an output block size. 1/8th of the original Elf_Data plus
     99      hsize.  Make the first chunk twice that size (25%), then increase
    100      by a block (12.5%) when necessary.  */
    101   size_t block = (data->d_size / 8) + hsize;
    102   size_t out_size = 2 * block;
    103   void *out_buf = malloc (out_size);
    104   if (out_buf == NULL)
    105     {
    106       __libelf_seterrno (ELF_E_NOMEM);
    107       return NULL;
    108     }
    109 
    110   /* Caller gets to fill in the header at the start.  Just skip it here.  */
    111   size_t used = hsize;
    112 
    113   z_stream z;
    114   z.zalloc = Z_NULL;
    115   z.zfree = Z_NULL;
    116   z.opaque = Z_NULL;
    117   int zrc = deflateInit (&z, Z_BEST_COMPRESSION);
    118   if (zrc != Z_OK)
    119     {
    120       __libelf_seterrno (ELF_E_COMPRESS_ERROR);
    121       return NULL;
    122     }
    123 
    124   Elf_Data cdata;
    125   cdata.d_buf = NULL;
    126 
    127   /* Loop over data buffers.  */
    128   int flush = Z_NO_FLUSH;
    129   do
    130     {
    131       /* Convert to raw if different endianess.  */
    132       cdata = *data;
    133       if (ei_data != MY_ELFDATA)
    134 	{
    135 	  /* Don't do this conversion in place, we might want to keep
    136 	     the original data around, caller decides.  */
    137 	  cdata.d_buf = malloc (data->d_size);
    138 	  if (cdata.d_buf == NULL)
    139 	    {
    140 	      __libelf_seterrno (ELF_E_NOMEM);
    141 	      return deflate_cleanup (NULL);
    142 	    }
    143 	  if (gelf_xlatetof (scn->elf, &cdata, data, ei_data) == NULL)
    144 	    return deflate_cleanup (NULL);
    145 	}
    146 
    147       z.avail_in = cdata.d_size;
    148       z.next_in = cdata.d_buf;
    149 
    150       /* Get next buffer to see if this is the last one.  */
    151       data = next_data;
    152       if (data != NULL)
    153 	{
    154 	  *orig_addralign = MAX (*orig_addralign, data->d_align);
    155 	  *orig_size += data->d_size;
    156 	  next_data = elf_getdata (scn, data);
    157 	}
    158       else
    159 	flush = Z_FINISH;
    160 
    161       /* Flush one data buffer.  */
    162       do
    163 	{
    164 	  z.avail_out = out_size - used;
    165 	  z.next_out = out_buf + used;
    166 	  zrc = deflate (&z, flush);
    167 	  if (zrc == Z_STREAM_ERROR)
    168 	    {
    169 	      __libelf_seterrno (ELF_E_COMPRESS_ERROR);
    170 	      return deflate_cleanup (NULL);
    171 	    }
    172 	  used += (out_size - used) - z.avail_out;
    173 
    174 	  /* Bail out if we are sure the user doesn't want the
    175 	     compression forced and we are using more compressed data
    176 	     than original data.  */
    177 	  if (!force && flush == Z_FINISH && used >= *orig_size)
    178 	    return deflate_cleanup ((void *) -1);
    179 
    180 	  if (z.avail_out == 0)
    181 	    {
    182 	      void *bigger = realloc (out_buf, out_size + block);
    183 	      if (bigger == NULL)
    184 		{
    185 		  __libelf_seterrno (ELF_E_NOMEM);
    186 		  return deflate_cleanup (NULL);
    187 		}
    188 	      out_buf = bigger;
    189 	      out_size += block;
    190 	    }
    191 	}
    192       while (z.avail_out == 0); /* Need more output buffer.  */
    193 
    194       if (ei_data != MY_ELFDATA)
    195 	{
    196 	  free (cdata.d_buf);
    197 	  cdata.d_buf = NULL;
    198 	}
    199     }
    200   while (flush != Z_FINISH); /* More data blocks.  */
    201 
    202   zrc = deflateEnd (&z);
    203   if (zrc != Z_OK)
    204     {
    205       __libelf_seterrno (ELF_E_COMPRESS_ERROR);
    206       return deflate_cleanup (NULL);
    207     }
    208 
    209   *new_size = used;
    210   return out_buf;
    211 }
    212 
    213 void *
    214 internal_function
    215 __libelf_decompress (void *buf_in, size_t size_in, size_t size_out)
    216 {
    217   void *buf_out = malloc (size_out);
    218   if (unlikely (buf_out == NULL))
    219     {
    220       __libelf_seterrno (ELF_E_NOMEM);
    221       return NULL;
    222     }
    223 
    224   z_stream z =
    225     {
    226       .next_in = buf_in,
    227       .avail_in = size_in,
    228       .next_out = buf_out,
    229       .avail_out = size_out
    230     };
    231   int zrc = inflateInit (&z);
    232   while (z.avail_in > 0 && likely (zrc == Z_OK))
    233     {
    234       z.next_out = buf_out + (size_out - z.avail_out);
    235       zrc = inflate (&z, Z_FINISH);
    236       if (unlikely (zrc != Z_STREAM_END))
    237 	{
    238 	  zrc = Z_DATA_ERROR;
    239 	  break;
    240 	}
    241       zrc = inflateReset (&z);
    242     }
    243   if (likely (zrc == Z_OK))
    244     zrc = inflateEnd (&z);
    245 
    246   if (unlikely (zrc != Z_OK) || unlikely (z.avail_out != 0))
    247     {
    248       free (buf_out);
    249       __libelf_seterrno (ELF_E_DECOMPRESS_ERROR);
    250       return NULL;
    251     }
    252 
    253   return buf_out;
    254 }
    255 
    256 void *
    257 internal_function
    258 __libelf_decompress_elf (Elf_Scn *scn, size_t *size_out, size_t *addralign)
    259 {
    260   GElf_Chdr chdr;
    261   if (gelf_getchdr (scn, &chdr) == NULL)
    262     return NULL;
    263 
    264   if (chdr.ch_type != ELFCOMPRESS_ZLIB)
    265     {
    266       __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
    267       return NULL;
    268     }
    269 
    270   if (! powerof2 (chdr.ch_addralign))
    271     {
    272       __libelf_seterrno (ELF_E_INVALID_ALIGN);
    273       return NULL;
    274     }
    275 
    276   /* Take the in-memory representation, so we can even handle a
    277      section that has just been constructed (maybe it was copied
    278      over from some other ELF file first with elf_newdata).  This
    279      is slightly inefficient when the raw data needs to be
    280      converted since then we'll be converting the whole buffer and
    281      not just Chdr.  */
    282   Elf_Data *data = elf_getdata (scn, NULL);
    283   if (data == NULL)
    284     return NULL;
    285 
    286   int elfclass = scn->elf->class;
    287   size_t hsize = (elfclass == ELFCLASS32
    288 		  ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
    289   size_t size_in = data->d_size - hsize;
    290   void *buf_in = data->d_buf + hsize;
    291   void *buf_out = __libelf_decompress (buf_in, size_in, chdr.ch_size);
    292   *size_out = chdr.ch_size;
    293   *addralign = chdr.ch_addralign;
    294   return buf_out;
    295 }
    296 
    297 void
    298 internal_function
    299 __libelf_reset_rawdata (Elf_Scn *scn, void *buf, size_t size, size_t align,
    300 			Elf_Type type)
    301 {
    302   /* This is the new raw data, replace and possibly free old data.  */
    303   scn->rawdata.d.d_off = 0;
    304   scn->rawdata.d.d_version = __libelf_version;
    305   scn->rawdata.d.d_buf = buf;
    306   scn->rawdata.d.d_size = size;
    307   scn->rawdata.d.d_align = align;
    308   scn->rawdata.d.d_type = type;
    309 
    310   /* Existing existing data is no longer valid.  */
    311   scn->data_list_rear = NULL;
    312   if (scn->data_base != scn->rawdata_base)
    313     free (scn->data_base);
    314   scn->data_base = NULL;
    315   if (scn->elf->map_address == NULL
    316       || scn->rawdata_base == scn->zdata_base)
    317     free (scn->rawdata_base);
    318 
    319   scn->rawdata_base = buf;
    320 }
    321 
    322 int
    323 elf_compress (Elf_Scn *scn, int type, unsigned int flags)
    324 {
    325   if (scn == NULL)
    326     return -1;
    327 
    328   if ((flags & ~ELF_CHF_FORCE) != 0)
    329     {
    330       __libelf_seterrno (ELF_E_INVALID_OPERAND);
    331       return -1;
    332     }
    333 
    334   bool force = (flags & ELF_CHF_FORCE) != 0;
    335 
    336   Elf *elf = scn->elf;
    337   GElf_Ehdr ehdr;
    338   if (gelf_getehdr (elf, &ehdr) == NULL)
    339     return -1;
    340 
    341   int elfclass = elf->class;
    342   int elfdata = ehdr.e_ident[EI_DATA];
    343 
    344   Elf64_Xword sh_flags;
    345   Elf64_Word sh_type;
    346   Elf64_Xword sh_addralign;
    347   if (elfclass == ELFCLASS32)
    348     {
    349       Elf32_Shdr *shdr = elf32_getshdr (scn);
    350       if (shdr == NULL)
    351 	return -1;
    352 
    353       sh_flags = shdr->sh_flags;
    354       sh_type = shdr->sh_type;
    355       sh_addralign = shdr->sh_addralign;
    356     }
    357   else
    358     {
    359       Elf64_Shdr *shdr = elf64_getshdr (scn);
    360       if (shdr == NULL)
    361 	return -1;
    362 
    363       sh_flags = shdr->sh_flags;
    364       sh_type = shdr->sh_type;
    365       sh_addralign = shdr->sh_addralign;
    366     }
    367 
    368   if ((sh_flags & SHF_ALLOC) != 0)
    369     {
    370       __libelf_seterrno (ELF_E_INVALID_SECTION_FLAGS);
    371       return -1;
    372     }
    373 
    374   if (sh_type == SHT_NULL || sh_type == SHT_NOBITS)
    375     {
    376       __libelf_seterrno (ELF_E_INVALID_SECTION_TYPE);
    377       return -1;
    378     }
    379 
    380   int compressed = (sh_flags & SHF_COMPRESSED);
    381   if (type == ELFCOMPRESS_ZLIB)
    382     {
    383       /* Compress/Deflate.  */
    384       if (compressed == 1)
    385 	{
    386 	  __libelf_seterrno (ELF_E_ALREADY_COMPRESSED);
    387 	  return -1;
    388 	}
    389 
    390       size_t hsize = (elfclass == ELFCLASS32
    391 		      ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
    392       size_t orig_size, orig_addralign, new_size;
    393       void *out_buf = __libelf_compress (scn, hsize, elfdata,
    394 					 &orig_size, &orig_addralign,
    395 					 &new_size, force);
    396 
    397       /* Compression would make section larger, don't change anything.  */
    398       if (out_buf == (void *) -1)
    399 	return 0;
    400 
    401       /* Compression failed, return error.  */
    402       if (out_buf == NULL)
    403 	return -1;
    404 
    405       /* Put the header in front of the data.  */
    406       if (elfclass == ELFCLASS32)
    407 	{
    408 	  Elf32_Chdr chdr;
    409 	  chdr.ch_type = ELFCOMPRESS_ZLIB;
    410 	  chdr.ch_size = orig_size;
    411 	  chdr.ch_addralign = orig_addralign;
    412 	  if (elfdata != MY_ELFDATA)
    413 	    {
    414 	      CONVERT (chdr.ch_type);
    415 	      CONVERT (chdr.ch_size);
    416 	      CONVERT (chdr.ch_addralign);
    417 	    }
    418 	  memcpy (out_buf, &chdr, sizeof (Elf32_Chdr));
    419 	}
    420       else
    421 	{
    422 	  Elf64_Chdr chdr;
    423 	  chdr.ch_type = ELFCOMPRESS_ZLIB;
    424 	  chdr.ch_reserved = 0;
    425 	  chdr.ch_size = orig_size;
    426 	  chdr.ch_addralign = sh_addralign;
    427 	  if (elfdata != MY_ELFDATA)
    428 	    {
    429 	      CONVERT (chdr.ch_type);
    430 	      CONVERT (chdr.ch_reserved);
    431 	      CONVERT (chdr.ch_size);
    432 	      CONVERT (chdr.ch_addralign);
    433 	    }
    434 	  memcpy (out_buf, &chdr, sizeof (Elf64_Chdr));
    435 	}
    436 
    437       /* Note we keep the sh_entsize as is, we assume it is setup
    438 	 correctly and ignored when SHF_COMPRESSED is set.  */
    439       if (elfclass == ELFCLASS32)
    440 	{
    441 	  Elf32_Shdr *shdr = elf32_getshdr (scn);
    442 	  shdr->sh_size = new_size;
    443 	  shdr->sh_addralign = 1;
    444 	  shdr->sh_flags |= SHF_COMPRESSED;
    445 	}
    446       else
    447 	{
    448 	  Elf64_Shdr *shdr = elf64_getshdr (scn);
    449 	  shdr->sh_size = new_size;
    450 	  shdr->sh_addralign = 1;
    451 	  shdr->sh_flags |= SHF_COMPRESSED;
    452 	}
    453 
    454       __libelf_reset_rawdata (scn, out_buf, new_size, 1, ELF_T_CHDR);
    455 
    456       /* The section is now compressed, we could keep the uncompressed
    457 	 data around, but since that might have been multiple Elf_Data
    458 	 buffers let the user uncompress it explicitly again if they
    459 	 want it to simplify bookkeeping.  */
    460       scn->zdata_base = NULL;
    461 
    462       return 1;
    463     }
    464   else if (type == 0)
    465     {
    466       /* Decompress/Inflate.  */
    467       if (compressed == 0)
    468 	{
    469 	  __libelf_seterrno (ELF_E_NOT_COMPRESSED);
    470 	  return -1;
    471 	}
    472 
    473       /* If the data is already decompressed (by elf_strptr), then we
    474 	 only need to setup the rawdata and section header. XXX what
    475 	 about elf_newdata?  */
    476       if (scn->zdata_base == NULL)
    477 	{
    478 	  size_t size_out, addralign;
    479 	  void *buf_out = __libelf_decompress_elf (scn, &size_out, &addralign);
    480 	  if (buf_out == NULL)
    481 	    return -1;
    482 
    483 	  scn->zdata_base = buf_out;
    484 	  scn->zdata_size = size_out;
    485 	  scn->zdata_align = addralign;
    486 	}
    487 
    488       /* Note we keep the sh_entsize as is, we assume it is setup
    489 	 correctly and ignored when SHF_COMPRESSED is set.  */
    490       if (elfclass == ELFCLASS32)
    491 	{
    492 	  Elf32_Shdr *shdr = elf32_getshdr (scn);
    493 	  shdr->sh_size = scn->zdata_size;
    494 	  shdr->sh_addralign = scn->zdata_align;
    495 	  shdr->sh_flags &= ~SHF_COMPRESSED;
    496 	}
    497       else
    498 	{
    499 	  Elf64_Shdr *shdr = elf64_getshdr (scn);
    500 	  shdr->sh_size = scn->zdata_size;
    501 	  shdr->sh_addralign = scn->zdata_align;
    502 	  shdr->sh_flags &= ~SHF_COMPRESSED;
    503 	}
    504 
    505       __libelf_reset_rawdata (scn, scn->zdata_base,
    506 			      scn->zdata_size, scn->zdata_align,
    507 			      __libelf_data_type (elf, sh_type));
    508 
    509       return 1;
    510     }
    511   else
    512     {
    513       __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
    514       return -1;
    515     }
    516 }
    517