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