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.  */
49static void *
50do_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.  */
70void *
71internal_function
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
213void *
214internal_function
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
256void *
257internal_function
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
297void
298internal_function
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
322int
323elf_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