LCOV - code coverage report
Current view: top level - libelf - elf_compress.c (source / functions) Hit Total Coverage
Test: elfutils-0.177 Lines: 202 235 86.0 %
Date: 2019-08-14 14:28:26 Functions: 5 5 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13