• 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       __libelf_seterrno (ELF_E_COMPRESS_ERROR);
117       return deflate_cleanup(NULL, NULL);
118     }
119 
120   Elf_Data cdata;
121   cdata.d_buf = NULL;
122 
123   /* Loop over data buffers.  */
124   int flush = Z_NO_FLUSH;
125   do
126     {
127       /* Convert to raw if different endianness.  */
128       cdata = *data;
129       bool convert = ei_data != MY_ELFDATA && data->d_size > 0;
130       if (convert)
131 	{
132 	  /* Don't do this conversion in place, we might want to keep
133 	     the original data around, caller decides.  */
134 	  cdata.d_buf = malloc (data->d_size);
135 	  if (cdata.d_buf == NULL)
136 	    {
137 	      __libelf_seterrno (ELF_E_NOMEM);
138 	      return deflate_cleanup (NULL, NULL);
139 	    }
140 	  if (gelf_xlatetof (scn->elf, &cdata, data, ei_data) == NULL)
141 	    return deflate_cleanup (NULL, &cdata);
142 	}
143 
144       z.avail_in = cdata.d_size;
145       z.next_in = cdata.d_buf;
146 
147       /* Get next buffer to see if this is the last one.  */
148       data = next_data;
149       if (data != NULL)
150 	{
151 	  *orig_addralign = MAX (*orig_addralign, data->d_align);
152 	  *orig_size += data->d_size;
153 	  next_data = elf_getdata (scn, data);
154 	}
155       else
156 	flush = Z_FINISH;
157 
158       /* Flush one data buffer.  */
159       do
160 	{
161 	  z.avail_out = out_size - used;
162 	  z.next_out = out_buf + used;
163 	  zrc = deflate (&z, flush);
164 	  if (zrc == Z_STREAM_ERROR)
165 	    {
166 	      __libelf_seterrno (ELF_E_COMPRESS_ERROR);
167 	      return deflate_cleanup (NULL, convert ? &cdata : NULL);
168 	    }
169 	  used += (out_size - used) - z.avail_out;
170 
171 	  /* Bail out if we are sure the user doesn't want the
172 	     compression forced and we are using more compressed data
173 	     than original data.  */
174 	  if (!force && flush == Z_FINISH && used >= *orig_size)
175 	    return deflate_cleanup ((void *) -1, convert ? &cdata : NULL);
176 
177 	  if (z.avail_out == 0)
178 	    {
179 	      void *bigger = realloc (out_buf, out_size + block);
180 	      if (bigger == NULL)
181 		{
182 		  __libelf_seterrno (ELF_E_NOMEM);
183 		  return deflate_cleanup (NULL, convert ? &cdata : NULL);
184 		}
185 	      out_buf = bigger;
186 	      out_size += block;
187 	    }
188 	}
189       while (z.avail_out == 0); /* Need more output buffer.  */
190 
191       if (convert)
192 	{
193 	  free (cdata.d_buf);
194 	  cdata.d_buf = NULL;
195 	}
196     }
197   while (flush != Z_FINISH); /* More data blocks.  */
198 
199   if (zrc != Z_STREAM_END)
200     {
201       __libelf_seterrno (ELF_E_COMPRESS_ERROR);
202       return deflate_cleanup (NULL, NULL);
203     }
204 
205   deflateEnd (&z);
206   *new_size = used;
207   return out_buf;
208 }
209 
210 void *
211 internal_function
__libelf_decompress(void * buf_in,size_t size_in,size_t size_out)212 __libelf_decompress (void *buf_in, size_t size_in, size_t size_out)
213 {
214   /* Catch highly unlikely compression ratios so we don't allocate
215      some giant amount of memory for nothing. The max compression
216      factor 1032:1 comes from http://www.zlib.net/zlib_tech.html  */
217   if (unlikely (size_out / 1032 > size_in))
218     {
219       __libelf_seterrno (ELF_E_INVALID_DATA);
220       return NULL;
221     }
222 
223   /* Malloc might return NULL when requestion zero size.  This is highly
224      unlikely, it would only happen when the compression was forced.
225      But we do need a non-NULL buffer to return and set as result.
226      Just make sure to always allocate at least 1 byte.  */
227   void *buf_out = malloc (size_out ?: 1);
228   if (unlikely (buf_out == NULL))
229     {
230       __libelf_seterrno (ELF_E_NOMEM);
231       return NULL;
232     }
233 
234   z_stream z =
235     {
236       .next_in = buf_in,
237       .avail_in = size_in,
238       .next_out = buf_out,
239       .avail_out = size_out
240     };
241   int zrc = inflateInit (&z);
242   while (z.avail_in > 0 && likely (zrc == Z_OK))
243     {
244       z.next_out = buf_out + (size_out - z.avail_out);
245       zrc = inflate (&z, Z_FINISH);
246       if (unlikely (zrc != Z_STREAM_END))
247 	{
248 	  zrc = Z_DATA_ERROR;
249 	  break;
250 	}
251       zrc = inflateReset (&z);
252     }
253 
254   if (unlikely (zrc != Z_OK) || unlikely (z.avail_out != 0))
255     {
256       free (buf_out);
257       buf_out = NULL;
258       __libelf_seterrno (ELF_E_DECOMPRESS_ERROR);
259     }
260 
261   inflateEnd(&z);
262   return buf_out;
263 }
264 
265 void *
266 internal_function
__libelf_decompress_elf(Elf_Scn * scn,size_t * size_out,size_t * addralign)267 __libelf_decompress_elf (Elf_Scn *scn, size_t *size_out, size_t *addralign)
268 {
269   GElf_Chdr chdr;
270   if (gelf_getchdr (scn, &chdr) == NULL)
271     return NULL;
272 
273   if (chdr.ch_type != ELFCOMPRESS_ZLIB)
274     {
275       __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
276       return NULL;
277     }
278 
279   if (! powerof2 (chdr.ch_addralign))
280     {
281       __libelf_seterrno (ELF_E_INVALID_ALIGN);
282       return NULL;
283     }
284 
285   /* Take the in-memory representation, so we can even handle a
286      section that has just been constructed (maybe it was copied
287      over from some other ELF file first with elf_newdata).  This
288      is slightly inefficient when the raw data needs to be
289      converted since then we'll be converting the whole buffer and
290      not just Chdr.  */
291   Elf_Data *data = elf_getdata (scn, NULL);
292   if (data == NULL)
293     return NULL;
294 
295   int elfclass = scn->elf->class;
296   size_t hsize = (elfclass == ELFCLASS32
297 		  ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
298   size_t size_in = data->d_size - hsize;
299   void *buf_in = data->d_buf + hsize;
300   void *buf_out = __libelf_decompress (buf_in, size_in, chdr.ch_size);
301   *size_out = chdr.ch_size;
302   *addralign = chdr.ch_addralign;
303   return buf_out;
304 }
305 
306 /* Assumes buf is a malloced buffer.  */
307 void
308 internal_function
__libelf_reset_rawdata(Elf_Scn * scn,void * buf,size_t size,size_t align,Elf_Type type)309 __libelf_reset_rawdata (Elf_Scn *scn, void *buf, size_t size, size_t align,
310 			Elf_Type type)
311 {
312   /* This is the new raw data, replace and possibly free old data.  */
313   scn->rawdata.d.d_off = 0;
314   scn->rawdata.d.d_version = EV_CURRENT;
315   scn->rawdata.d.d_buf = buf;
316   scn->rawdata.d.d_size = size;
317   scn->rawdata.d.d_align = align;
318   scn->rawdata.d.d_type = type;
319 
320   /* Existing existing data is no longer valid.  */
321   scn->data_list_rear = NULL;
322   if (scn->data_base != scn->rawdata_base)
323     free (scn->data_base);
324   scn->data_base = NULL;
325   if (scn->elf->map_address == NULL
326       || scn->rawdata_base == scn->zdata_base
327       || (scn->flags & ELF_F_MALLOCED) != 0)
328     free (scn->rawdata_base);
329 
330   scn->rawdata_base = buf;
331   scn->flags |= ELF_F_MALLOCED;
332 
333   /* Pretend we (tried to) read the data from the file and setup the
334      data (might have to convert the Chdr to native format).  */
335   scn->data_read = 1;
336   scn->flags |= ELF_F_FILEDATA;
337   __libelf_set_data_list_rdlock (scn, 1);
338 }
339 
340 int
elf_compress(Elf_Scn * scn,int type,unsigned int flags)341 elf_compress (Elf_Scn *scn, int type, unsigned int flags)
342 {
343   if (scn == NULL)
344     return -1;
345 
346   if ((flags & ~ELF_CHF_FORCE) != 0)
347     {
348       __libelf_seterrno (ELF_E_INVALID_OPERAND);
349       return -1;
350     }
351 
352   bool force = (flags & ELF_CHF_FORCE) != 0;
353 
354   Elf *elf = scn->elf;
355   GElf_Ehdr ehdr;
356   if (gelf_getehdr (elf, &ehdr) == NULL)
357     return -1;
358 
359   int elfclass = elf->class;
360   int elfdata = ehdr.e_ident[EI_DATA];
361 
362   Elf64_Xword sh_flags;
363   Elf64_Word sh_type;
364   Elf64_Xword sh_addralign;
365   if (elfclass == ELFCLASS32)
366     {
367       Elf32_Shdr *shdr = elf32_getshdr (scn);
368       if (shdr == NULL)
369 	return -1;
370 
371       sh_flags = shdr->sh_flags;
372       sh_type = shdr->sh_type;
373       sh_addralign = shdr->sh_addralign;
374     }
375   else
376     {
377       Elf64_Shdr *shdr = elf64_getshdr (scn);
378       if (shdr == NULL)
379 	return -1;
380 
381       sh_flags = shdr->sh_flags;
382       sh_type = shdr->sh_type;
383       sh_addralign = shdr->sh_addralign;
384     }
385 
386   if ((sh_flags & SHF_ALLOC) != 0)
387     {
388       __libelf_seterrno (ELF_E_INVALID_SECTION_FLAGS);
389       return -1;
390     }
391 
392   if (sh_type == SHT_NULL || sh_type == SHT_NOBITS)
393     {
394       __libelf_seterrno (ELF_E_INVALID_SECTION_TYPE);
395       return -1;
396     }
397 
398   int compressed = (sh_flags & SHF_COMPRESSED);
399   if (type == ELFCOMPRESS_ZLIB)
400     {
401       /* Compress/Deflate.  */
402       if (compressed == 1)
403 	{
404 	  __libelf_seterrno (ELF_E_ALREADY_COMPRESSED);
405 	  return -1;
406 	}
407 
408       size_t hsize = (elfclass == ELFCLASS32
409 		      ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
410       size_t orig_size, orig_addralign, new_size;
411       void *out_buf = __libelf_compress (scn, hsize, elfdata,
412 					 &orig_size, &orig_addralign,
413 					 &new_size, force);
414 
415       /* Compression would make section larger, don't change anything.  */
416       if (out_buf == (void *) -1)
417 	return 0;
418 
419       /* Compression failed, return error.  */
420       if (out_buf == NULL)
421 	return -1;
422 
423       /* Put the header in front of the data.  */
424       if (elfclass == ELFCLASS32)
425 	{
426 	  Elf32_Chdr chdr;
427 	  chdr.ch_type = ELFCOMPRESS_ZLIB;
428 	  chdr.ch_size = orig_size;
429 	  chdr.ch_addralign = orig_addralign;
430 	  if (elfdata != MY_ELFDATA)
431 	    {
432 	      CONVERT (chdr.ch_type);
433 	      CONVERT (chdr.ch_size);
434 	      CONVERT (chdr.ch_addralign);
435 	    }
436 	  memcpy (out_buf, &chdr, sizeof (Elf32_Chdr));
437 	}
438       else
439 	{
440 	  Elf64_Chdr chdr;
441 	  chdr.ch_type = ELFCOMPRESS_ZLIB;
442 	  chdr.ch_reserved = 0;
443 	  chdr.ch_size = orig_size;
444 	  chdr.ch_addralign = sh_addralign;
445 	  if (elfdata != MY_ELFDATA)
446 	    {
447 	      CONVERT (chdr.ch_type);
448 	      CONVERT (chdr.ch_reserved);
449 	      CONVERT (chdr.ch_size);
450 	      CONVERT (chdr.ch_addralign);
451 	    }
452 	  memcpy (out_buf, &chdr, sizeof (Elf64_Chdr));
453 	}
454 
455       /* Note we keep the sh_entsize as is, we assume it is setup
456 	 correctly and ignored when SHF_COMPRESSED is set.  */
457       if (elfclass == ELFCLASS32)
458 	{
459 	  Elf32_Shdr *shdr = elf32_getshdr (scn);
460 	  shdr->sh_size = new_size;
461 	  shdr->sh_addralign = __libelf_type_align (ELFCLASS32, ELF_T_CHDR);
462 	  shdr->sh_flags |= SHF_COMPRESSED;
463 	}
464       else
465 	{
466 	  Elf64_Shdr *shdr = elf64_getshdr (scn);
467 	  shdr->sh_size = new_size;
468 	  shdr->sh_addralign = __libelf_type_align (ELFCLASS64, ELF_T_CHDR);
469 	  shdr->sh_flags |= SHF_COMPRESSED;
470 	}
471 
472       __libelf_reset_rawdata (scn, out_buf, new_size, 1, ELF_T_CHDR);
473 
474       /* The section is now compressed, we could keep the uncompressed
475 	 data around, but since that might have been multiple Elf_Data
476 	 buffers let the user uncompress it explicitly again if they
477 	 want it to simplify bookkeeping.  */
478       scn->zdata_base = NULL;
479 
480       return 1;
481     }
482   else if (type == 0)
483     {
484       /* Decompress/Inflate.  */
485       if (compressed == 0)
486 	{
487 	  __libelf_seterrno (ELF_E_NOT_COMPRESSED);
488 	  return -1;
489 	}
490 
491       /* If the data is already decompressed (by elf_strptr), then we
492 	 only need to setup the rawdata and section header. XXX what
493 	 about elf_newdata?  */
494       if (scn->zdata_base == NULL)
495 	{
496 	  size_t size_out, addralign;
497 	  void *buf_out = __libelf_decompress_elf (scn, &size_out, &addralign);
498 	  if (buf_out == NULL)
499 	    return -1;
500 
501 	  scn->zdata_base = buf_out;
502 	  scn->zdata_size = size_out;
503 	  scn->zdata_align = addralign;
504 	}
505 
506       /* Note we keep the sh_entsize as is, we assume it is setup
507 	 correctly and ignored when SHF_COMPRESSED is set.  */
508       if (elfclass == ELFCLASS32)
509 	{
510 	  Elf32_Shdr *shdr = elf32_getshdr (scn);
511 	  shdr->sh_size = scn->zdata_size;
512 	  shdr->sh_addralign = scn->zdata_align;
513 	  shdr->sh_flags &= ~SHF_COMPRESSED;
514 	}
515       else
516 	{
517 	  Elf64_Shdr *shdr = elf64_getshdr (scn);
518 	  shdr->sh_size = scn->zdata_size;
519 	  shdr->sh_addralign = scn->zdata_align;
520 	  shdr->sh_flags &= ~SHF_COMPRESSED;
521 	}
522 
523       __libelf_reset_rawdata (scn, scn->zdata_base,
524 			      scn->zdata_size, scn->zdata_align,
525 			      __libelf_data_type (&ehdr, sh_type,
526 						  scn->zdata_align));
527 
528       return 1;
529     }
530   else
531     {
532       __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
533       return -1;
534     }
535 }
536