• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 *
do_deflate_cleanup(void * result,z_stream * z,void * out_buf,int ei_data,Elf_Data * cdatap)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
__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)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
__libelf_decompress(void * buf_in,size_t size_in,size_t size_out)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
__libelf_decompress_elf(Elf_Scn * scn,size_t * size_out,size_t * addralign)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
__libelf_reset_rawdata(Elf_Scn * scn,void * buf,size_t size,size_t align,Elf_Type type)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
elf_compress(Elf_Scn * scn,int type,unsigned int flags)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