• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 *
do_deflate_cleanup(void * result,z_stream * z,void * out_buf,Elf_Data * cdatap)46 do_deflate_cleanup (void *result, z_stream *z, void *out_buf,
47                     Elf_Data *cdatap)
48 {
49   deflateEnd (z);
50   free (out_buf);
51   if (cdatap != NULL)
52     free (cdatap->d_buf);
53   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
__libelf_compress(Elf_Scn * scn,size_t hsize,int ei_data,size_t * orig_size,size_t * orig_addralign,size_t * new_size,bool force)68 __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   Elf_Data *data = elf_getdata (scn, NULL);
79   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   Elf_Data *next_data = elf_getdata (scn, data);
87   if (next_data == NULL && !force
88       && data->d_size <= hsize + 5 + 6)
89     return (void *) -1;
90 
91   *orig_addralign = data->d_align;
92   *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   size_t block = (data->d_size / 8) + hsize;
98   size_t out_size = 2 * block;
99   void *out_buf = malloc (out_size);
100   if (out_buf == NULL)
101     {
102       __libelf_seterrno (ELF_E_NOMEM);
103       return NULL;
104     }
105 
106   /* Caller gets to fill in the header at the start.  Just skip it here.  */
107   size_t used = hsize;
108 
109   z_stream z;
110   z.zalloc = Z_NULL;
111   z.zfree = Z_NULL;
112   z.opaque = Z_NULL;
113   int zrc = deflateInit (&z, Z_BEST_COMPRESSION);
114   if (zrc != Z_OK)
115     {
116       free (out_buf);
117       __libelf_seterrno (ELF_E_COMPRESS_ERROR);
118       return NULL;
119     }
120 
121   Elf_Data cdata;
122   cdata.d_buf = NULL;
123 
124   /* Loop over data buffers.  */
125   int flush = Z_NO_FLUSH;
126   do
127     {
128       /* Convert to raw if different endianess.  */
129       cdata = *data;
130       bool convert = ei_data != MY_ELFDATA && data->d_size > 0;
131       if (convert)
132 	{
133 	  /* Don't do this conversion in place, we might want to keep
134 	     the original data around, caller decides.  */
135 	  cdata.d_buf = malloc (data->d_size);
136 	  if (cdata.d_buf == NULL)
137 	    {
138 	      __libelf_seterrno (ELF_E_NOMEM);
139 	      return deflate_cleanup (NULL, NULL);
140 	    }
141 	  if (gelf_xlatetof (scn->elf, &cdata, data, ei_data) == NULL)
142 	    return deflate_cleanup (NULL, &cdata);
143 	}
144 
145       z.avail_in = cdata.d_size;
146       z.next_in = cdata.d_buf;
147 
148       /* Get next buffer to see if this is the last one.  */
149       data = next_data;
150       if (data != NULL)
151 	{
152 	  *orig_addralign = MAX (*orig_addralign, data->d_align);
153 	  *orig_size += data->d_size;
154 	  next_data = elf_getdata (scn, data);
155 	}
156       else
157 	flush = Z_FINISH;
158 
159       /* Flush one data buffer.  */
160       do
161 	{
162 	  z.avail_out = out_size - used;
163 	  z.next_out = out_buf + used;
164 	  zrc = deflate (&z, flush);
165 	  if (zrc == Z_STREAM_ERROR)
166 	    {
167 	      __libelf_seterrno (ELF_E_COMPRESS_ERROR);
168 	      return deflate_cleanup (NULL, convert ? &cdata : NULL);
169 	    }
170 	  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 	  if (!force && flush == Z_FINISH && used >= *orig_size)
176 	    return deflate_cleanup ((void *) -1, convert ? &cdata : NULL);
177 
178 	  if (z.avail_out == 0)
179 	    {
180 	      void *bigger = realloc (out_buf, out_size + block);
181 	      if (bigger == NULL)
182 		{
183 		  __libelf_seterrno (ELF_E_NOMEM);
184 		  return deflate_cleanup (NULL, convert ? &cdata : NULL);
185 		}
186 	      out_buf = bigger;
187 	      out_size += block;
188 	    }
189 	}
190       while (z.avail_out == 0); /* Need more output buffer.  */
191 
192       if (convert)
193 	{
194 	  free (cdata.d_buf);
195 	  cdata.d_buf = NULL;
196 	}
197     }
198   while (flush != Z_FINISH); /* More data blocks.  */
199 
200   zrc = deflateEnd (&z);
201   if (zrc != Z_OK)
202     {
203       __libelf_seterrno (ELF_E_COMPRESS_ERROR);
204       return deflate_cleanup (NULL, NULL);
205     }
206 
207   *new_size = used;
208   return out_buf;
209 }
210 
211 void *
212 internal_function
__libelf_decompress(void * buf_in,size_t size_in,size_t size_out)213 __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   if (unlikely (size_out / 1032 > size_in))
219     {
220       __libelf_seterrno (ELF_E_INVALID_DATA);
221       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   void *buf_out = malloc (size_out ?: 1);
229   if (unlikely (buf_out == NULL))
230     {
231       __libelf_seterrno (ELF_E_NOMEM);
232       return NULL;
233     }
234 
235   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   int zrc = inflateInit (&z);
243   while (z.avail_in > 0 && likely (zrc == Z_OK))
244     {
245       z.next_out = buf_out + (size_out - z.avail_out);
246       zrc = inflate (&z, Z_FINISH);
247       if (unlikely (zrc != Z_STREAM_END))
248 	{
249 	  zrc = Z_DATA_ERROR;
250 	  break;
251 	}
252       zrc = inflateReset (&z);
253     }
254   if (likely (zrc == Z_OK))
255     zrc = inflateEnd (&z);
256 
257   if (unlikely (zrc != Z_OK) || unlikely (z.avail_out != 0))
258     {
259       free (buf_out);
260       __libelf_seterrno (ELF_E_DECOMPRESS_ERROR);
261       return NULL;
262     }
263 
264   return buf_out;
265 }
266 
267 void *
268 internal_function
__libelf_decompress_elf(Elf_Scn * scn,size_t * size_out,size_t * addralign)269 __libelf_decompress_elf (Elf_Scn *scn, size_t *size_out, size_t *addralign)
270 {
271   GElf_Chdr chdr;
272   if (gelf_getchdr (scn, &chdr) == NULL)
273     return NULL;
274 
275   if (chdr.ch_type != ELFCOMPRESS_ZLIB)
276     {
277       __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
278       return NULL;
279     }
280 
281   if (! powerof2 (chdr.ch_addralign))
282     {
283       __libelf_seterrno (ELF_E_INVALID_ALIGN);
284       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   Elf_Data *data = elf_getdata (scn, NULL);
294   if (data == NULL)
295     return NULL;
296 
297   int elfclass = scn->elf->class;
298   size_t hsize = (elfclass == ELFCLASS32
299 		  ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
300   size_t size_in = data->d_size - hsize;
301   void *buf_in = data->d_buf + hsize;
302   void *buf_out = __libelf_decompress (buf_in, size_in, chdr.ch_size);
303   *size_out = chdr.ch_size;
304   *addralign = chdr.ch_addralign;
305   return buf_out;
306 }
307 
308 /* Assumes buf is a malloced buffer.  */
309 void
310 internal_function
__libelf_reset_rawdata(Elf_Scn * scn,void * buf,size_t size,size_t align,Elf_Type type)311 __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   scn->rawdata.d.d_off = 0;
316   scn->rawdata.d.d_version = EV_CURRENT;
317   scn->rawdata.d.d_buf = buf;
318   scn->rawdata.d.d_size = size;
319   scn->rawdata.d.d_align = align;
320   scn->rawdata.d.d_type = type;
321 
322   /* Existing existing data is no longer valid.  */
323   scn->data_list_rear = NULL;
324   if (scn->data_base != scn->rawdata_base)
325     free (scn->data_base);
326   scn->data_base = NULL;
327   if (scn->elf->map_address == NULL
328       || scn->rawdata_base == scn->zdata_base
329       || (scn->flags & ELF_F_MALLOCED) != 0)
330     free (scn->rawdata_base);
331 
332   scn->rawdata_base = buf;
333   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   scn->data_read = 1;
338   scn->flags |= ELF_F_FILEDATA;
339   __libelf_set_data_list_rdlock (scn, 1);
340 }
341 
342 int
elf_compress(Elf_Scn * scn,int type,unsigned int flags)343 elf_compress (Elf_Scn *scn, int type, unsigned int flags)
344 {
345   if (scn == NULL)
346     return -1;
347 
348   if ((flags & ~ELF_CHF_FORCE) != 0)
349     {
350       __libelf_seterrno (ELF_E_INVALID_OPERAND);
351       return -1;
352     }
353 
354   bool force = (flags & ELF_CHF_FORCE) != 0;
355 
356   Elf *elf = scn->elf;
357   GElf_Ehdr ehdr;
358   if (gelf_getehdr (elf, &ehdr) == NULL)
359     return -1;
360 
361   int elfclass = elf->class;
362   int elfdata = ehdr.e_ident[EI_DATA];
363 
364   Elf64_Xword sh_flags;
365   Elf64_Word sh_type;
366   Elf64_Xword sh_addralign;
367   if (elfclass == ELFCLASS32)
368     {
369       Elf32_Shdr *shdr = elf32_getshdr (scn);
370       if (shdr == NULL)
371 	return -1;
372 
373       sh_flags = shdr->sh_flags;
374       sh_type = shdr->sh_type;
375       sh_addralign = shdr->sh_addralign;
376     }
377   else
378     {
379       Elf64_Shdr *shdr = elf64_getshdr (scn);
380       if (shdr == NULL)
381 	return -1;
382 
383       sh_flags = shdr->sh_flags;
384       sh_type = shdr->sh_type;
385       sh_addralign = shdr->sh_addralign;
386     }
387 
388   if ((sh_flags & SHF_ALLOC) != 0)
389     {
390       __libelf_seterrno (ELF_E_INVALID_SECTION_FLAGS);
391       return -1;
392     }
393 
394   if (sh_type == SHT_NULL || sh_type == SHT_NOBITS)
395     {
396       __libelf_seterrno (ELF_E_INVALID_SECTION_TYPE);
397       return -1;
398     }
399 
400   int compressed = (sh_flags & SHF_COMPRESSED);
401   if (type == ELFCOMPRESS_ZLIB)
402     {
403       /* Compress/Deflate.  */
404       if (compressed == 1)
405 	{
406 	  __libelf_seterrno (ELF_E_ALREADY_COMPRESSED);
407 	  return -1;
408 	}
409 
410       size_t hsize = (elfclass == ELFCLASS32
411 		      ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
412       size_t orig_size, orig_addralign, new_size;
413       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       if (out_buf == (void *) -1)
419 	return 0;
420 
421       /* Compression failed, return error.  */
422       if (out_buf == NULL)
423 	return -1;
424 
425       /* Put the header in front of the data.  */
426       if (elfclass == ELFCLASS32)
427 	{
428 	  Elf32_Chdr chdr;
429 	  chdr.ch_type = ELFCOMPRESS_ZLIB;
430 	  chdr.ch_size = orig_size;
431 	  chdr.ch_addralign = orig_addralign;
432 	  if (elfdata != MY_ELFDATA)
433 	    {
434 	      CONVERT (chdr.ch_type);
435 	      CONVERT (chdr.ch_size);
436 	      CONVERT (chdr.ch_addralign);
437 	    }
438 	  memcpy (out_buf, &chdr, sizeof (Elf32_Chdr));
439 	}
440       else
441 	{
442 	  Elf64_Chdr chdr;
443 	  chdr.ch_type = ELFCOMPRESS_ZLIB;
444 	  chdr.ch_reserved = 0;
445 	  chdr.ch_size = orig_size;
446 	  chdr.ch_addralign = sh_addralign;
447 	  if (elfdata != MY_ELFDATA)
448 	    {
449 	      CONVERT (chdr.ch_type);
450 	      CONVERT (chdr.ch_reserved);
451 	      CONVERT (chdr.ch_size);
452 	      CONVERT (chdr.ch_addralign);
453 	    }
454 	  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       if (elfclass == ELFCLASS32)
460 	{
461 	  Elf32_Shdr *shdr = elf32_getshdr (scn);
462 	  shdr->sh_size = new_size;
463 	  shdr->sh_addralign = __libelf_type_align (ELFCLASS32, ELF_T_CHDR);
464 	  shdr->sh_flags |= SHF_COMPRESSED;
465 	}
466       else
467 	{
468 	  Elf64_Shdr *shdr = elf64_getshdr (scn);
469 	  shdr->sh_size = new_size;
470 	  shdr->sh_addralign = __libelf_type_align (ELFCLASS64, ELF_T_CHDR);
471 	  shdr->sh_flags |= SHF_COMPRESSED;
472 	}
473 
474       __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       scn->zdata_base = NULL;
481 
482       return 1;
483     }
484   else if (type == 0)
485     {
486       /* Decompress/Inflate.  */
487       if (compressed == 0)
488 	{
489 	  __libelf_seterrno (ELF_E_NOT_COMPRESSED);
490 	  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       if (scn->zdata_base == NULL)
497 	{
498 	  size_t size_out, addralign;
499 	  void *buf_out = __libelf_decompress_elf (scn, &size_out, &addralign);
500 	  if (buf_out == NULL)
501 	    return -1;
502 
503 	  scn->zdata_base = buf_out;
504 	  scn->zdata_size = size_out;
505 	  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       if (elfclass == ELFCLASS32)
511 	{
512 	  Elf32_Shdr *shdr = elf32_getshdr (scn);
513 	  shdr->sh_size = scn->zdata_size;
514 	  shdr->sh_addralign = scn->zdata_align;
515 	  shdr->sh_flags &= ~SHF_COMPRESSED;
516 	}
517       else
518 	{
519 	  Elf64_Shdr *shdr = elf64_getshdr (scn);
520 	  shdr->sh_size = scn->zdata_size;
521 	  shdr->sh_addralign = scn->zdata_align;
522 	  shdr->sh_flags &= ~SHF_COMPRESSED;
523 	}
524 
525       __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       return 1;
531     }
532   else
533     {
534       __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
535       return -1;
536     }
537 }
538