blob: 711be591b5e466ec207351ca1d31cc0bcbef59a6 [file] [log] [blame]
Brian Silverman86497922018-02-10 19:28:39 -05001/* 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. */
45static void *
46do_deflate_cleanup (void *result, z_stream *z, void *out_buf,
47 int ei_data, Elf_Data *cdatap)
48{
49 deflateEnd (z);
50 free (out_buf);
51 if (ei_data != MY_ELFDATA)
52 free (cdatap->d_buf);
53 return result;
54}
55
56#define deflate_cleanup(result) \
57 do_deflate_cleanup(result, &z, out_buf, ei_data, &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. */
66void *
67internal_function
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 if (ei_data != MY_ELFDATA)
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);
139 }
140 if (gelf_xlatetof (scn->elf, &cdata, data, ei_data) == NULL)
141 return deflate_cleanup (NULL);
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);
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);
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);
184 }
185 out_buf = bigger;
186 out_size += block;
187 }
188 }
189 while (z.avail_out == 0); /* Need more output buffer. */
190
191 if (ei_data != MY_ELFDATA)
192 {
193 free (cdata.d_buf);
194 cdata.d_buf = NULL;
195 }
196 }
197 while (flush != Z_FINISH); /* More data blocks. */
198
199 zrc = deflateEnd (&z);
200 if (zrc != Z_OK)
201 {
202 __libelf_seterrno (ELF_E_COMPRESS_ERROR);
203 return deflate_cleanup (NULL);
204 }
205
206 *new_size = used;
207 return out_buf;
208}
209
210void *
211internal_function
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 void *buf_out = malloc (size_out);
224 if (unlikely (buf_out == NULL))
225 {
226 __libelf_seterrno (ELF_E_NOMEM);
227 return NULL;
228 }
229
230 z_stream z =
231 {
232 .next_in = buf_in,
233 .avail_in = size_in,
234 .next_out = buf_out,
235 .avail_out = size_out
236 };
237 int zrc = inflateInit (&z);
238 while (z.avail_in > 0 && likely (zrc == Z_OK))
239 {
240 z.next_out = buf_out + (size_out - z.avail_out);
241 zrc = inflate (&z, Z_FINISH);
242 if (unlikely (zrc != Z_STREAM_END))
243 {
244 zrc = Z_DATA_ERROR;
245 break;
246 }
247 zrc = inflateReset (&z);
248 }
249 if (likely (zrc == Z_OK))
250 zrc = inflateEnd (&z);
251
252 if (unlikely (zrc != Z_OK) || unlikely (z.avail_out != 0))
253 {
254 free (buf_out);
255 __libelf_seterrno (ELF_E_DECOMPRESS_ERROR);
256 return NULL;
257 }
258
259 return buf_out;
260}
261
262void *
263internal_function
264__libelf_decompress_elf (Elf_Scn *scn, size_t *size_out, size_t *addralign)
265{
266 GElf_Chdr chdr;
267 if (gelf_getchdr (scn, &chdr) == NULL)
268 return NULL;
269
270 if (chdr.ch_type != ELFCOMPRESS_ZLIB)
271 {
272 __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
273 return NULL;
274 }
275
276 if (! powerof2 (chdr.ch_addralign))
277 {
278 __libelf_seterrno (ELF_E_INVALID_ALIGN);
279 return NULL;
280 }
281
282 /* Take the in-memory representation, so we can even handle a
283 section that has just been constructed (maybe it was copied
284 over from some other ELF file first with elf_newdata). This
285 is slightly inefficient when the raw data needs to be
286 converted since then we'll be converting the whole buffer and
287 not just Chdr. */
288 Elf_Data *data = elf_getdata (scn, NULL);
289 if (data == NULL)
290 return NULL;
291
292 int elfclass = scn->elf->class;
293 size_t hsize = (elfclass == ELFCLASS32
294 ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
295 size_t size_in = data->d_size - hsize;
296 void *buf_in = data->d_buf + hsize;
297 void *buf_out = __libelf_decompress (buf_in, size_in, chdr.ch_size);
298 *size_out = chdr.ch_size;
299 *addralign = chdr.ch_addralign;
300 return buf_out;
301}
302
303/* Assumes buf is a malloced buffer. */
304void
305internal_function
306__libelf_reset_rawdata (Elf_Scn *scn, void *buf, size_t size, size_t align,
307 Elf_Type type)
308{
309 /* This is the new raw data, replace and possibly free old data. */
310 scn->rawdata.d.d_off = 0;
311 scn->rawdata.d.d_version = __libelf_version;
312 scn->rawdata.d.d_buf = buf;
313 scn->rawdata.d.d_size = size;
314 scn->rawdata.d.d_align = align;
315 scn->rawdata.d.d_type = type;
316
317 /* Existing existing data is no longer valid. */
318 scn->data_list_rear = NULL;
319 if (scn->data_base != scn->rawdata_base)
320 free (scn->data_base);
321 scn->data_base = NULL;
322 if (scn->elf->map_address == NULL
323 || scn->rawdata_base == scn->zdata_base
324 || (scn->flags & ELF_F_MALLOCED) != 0)
325 free (scn->rawdata_base);
326
327 scn->rawdata_base = buf;
328 scn->flags |= ELF_F_MALLOCED;
329}
330
331int
332elf_compress (Elf_Scn *scn, int type, unsigned int flags)
333{
334 if (scn == NULL)
335 return -1;
336
337 if ((flags & ~ELF_CHF_FORCE) != 0)
338 {
339 __libelf_seterrno (ELF_E_INVALID_OPERAND);
340 return -1;
341 }
342
343 bool force = (flags & ELF_CHF_FORCE) != 0;
344
345 Elf *elf = scn->elf;
346 GElf_Ehdr ehdr;
347 if (gelf_getehdr (elf, &ehdr) == NULL)
348 return -1;
349
350 int elfclass = elf->class;
351 int elfdata = ehdr.e_ident[EI_DATA];
352
353 Elf64_Xword sh_flags;
354 Elf64_Word sh_type;
355 Elf64_Xword sh_addralign;
356 if (elfclass == ELFCLASS32)
357 {
358 Elf32_Shdr *shdr = elf32_getshdr (scn);
359 if (shdr == NULL)
360 return -1;
361
362 sh_flags = shdr->sh_flags;
363 sh_type = shdr->sh_type;
364 sh_addralign = shdr->sh_addralign;
365 }
366 else
367 {
368 Elf64_Shdr *shdr = elf64_getshdr (scn);
369 if (shdr == NULL)
370 return -1;
371
372 sh_flags = shdr->sh_flags;
373 sh_type = shdr->sh_type;
374 sh_addralign = shdr->sh_addralign;
375 }
376
377 if ((sh_flags & SHF_ALLOC) != 0)
378 {
379 __libelf_seterrno (ELF_E_INVALID_SECTION_FLAGS);
380 return -1;
381 }
382
383 if (sh_type == SHT_NULL || sh_type == SHT_NOBITS)
384 {
385 __libelf_seterrno (ELF_E_INVALID_SECTION_TYPE);
386 return -1;
387 }
388
389 int compressed = (sh_flags & SHF_COMPRESSED);
390 if (type == ELFCOMPRESS_ZLIB)
391 {
392 /* Compress/Deflate. */
393 if (compressed == 1)
394 {
395 __libelf_seterrno (ELF_E_ALREADY_COMPRESSED);
396 return -1;
397 }
398
399 size_t hsize = (elfclass == ELFCLASS32
400 ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
401 size_t orig_size, orig_addralign, new_size;
402 void *out_buf = __libelf_compress (scn, hsize, elfdata,
403 &orig_size, &orig_addralign,
404 &new_size, force);
405
406 /* Compression would make section larger, don't change anything. */
407 if (out_buf == (void *) -1)
408 return 0;
409
410 /* Compression failed, return error. */
411 if (out_buf == NULL)
412 return -1;
413
414 /* Put the header in front of the data. */
415 if (elfclass == ELFCLASS32)
416 {
417 Elf32_Chdr chdr;
418 chdr.ch_type = ELFCOMPRESS_ZLIB;
419 chdr.ch_size = orig_size;
420 chdr.ch_addralign = orig_addralign;
421 if (elfdata != MY_ELFDATA)
422 {
423 CONVERT (chdr.ch_type);
424 CONVERT (chdr.ch_size);
425 CONVERT (chdr.ch_addralign);
426 }
427 memcpy (out_buf, &chdr, sizeof (Elf32_Chdr));
428 }
429 else
430 {
431 Elf64_Chdr chdr;
432 chdr.ch_type = ELFCOMPRESS_ZLIB;
433 chdr.ch_reserved = 0;
434 chdr.ch_size = orig_size;
435 chdr.ch_addralign = sh_addralign;
436 if (elfdata != MY_ELFDATA)
437 {
438 CONVERT (chdr.ch_type);
439 CONVERT (chdr.ch_reserved);
440 CONVERT (chdr.ch_size);
441 CONVERT (chdr.ch_addralign);
442 }
443 memcpy (out_buf, &chdr, sizeof (Elf64_Chdr));
444 }
445
446 /* Note we keep the sh_entsize as is, we assume it is setup
447 correctly and ignored when SHF_COMPRESSED is set. */
448 if (elfclass == ELFCLASS32)
449 {
450 Elf32_Shdr *shdr = elf32_getshdr (scn);
451 shdr->sh_size = new_size;
452 shdr->sh_addralign = 1;
453 shdr->sh_flags |= SHF_COMPRESSED;
454 }
455 else
456 {
457 Elf64_Shdr *shdr = elf64_getshdr (scn);
458 shdr->sh_size = new_size;
459 shdr->sh_addralign = 1;
460 shdr->sh_flags |= SHF_COMPRESSED;
461 }
462
463 __libelf_reset_rawdata (scn, out_buf, new_size, 1, ELF_T_CHDR);
464
465 /* The section is now compressed, we could keep the uncompressed
466 data around, but since that might have been multiple Elf_Data
467 buffers let the user uncompress it explicitly again if they
468 want it to simplify bookkeeping. */
469 scn->zdata_base = NULL;
470
471 return 1;
472 }
473 else if (type == 0)
474 {
475 /* Decompress/Inflate. */
476 if (compressed == 0)
477 {
478 __libelf_seterrno (ELF_E_NOT_COMPRESSED);
479 return -1;
480 }
481
482 /* If the data is already decompressed (by elf_strptr), then we
483 only need to setup the rawdata and section header. XXX what
484 about elf_newdata? */
485 if (scn->zdata_base == NULL)
486 {
487 size_t size_out, addralign;
488 void *buf_out = __libelf_decompress_elf (scn, &size_out, &addralign);
489 if (buf_out == NULL)
490 return -1;
491
492 scn->zdata_base = buf_out;
493 scn->zdata_size = size_out;
494 scn->zdata_align = addralign;
495 }
496
497 /* Note we keep the sh_entsize as is, we assume it is setup
498 correctly and ignored when SHF_COMPRESSED is set. */
499 if (elfclass == ELFCLASS32)
500 {
501 Elf32_Shdr *shdr = elf32_getshdr (scn);
502 shdr->sh_size = scn->zdata_size;
503 shdr->sh_addralign = scn->zdata_align;
504 shdr->sh_flags &= ~SHF_COMPRESSED;
505 }
506 else
507 {
508 Elf64_Shdr *shdr = elf64_getshdr (scn);
509 shdr->sh_size = scn->zdata_size;
510 shdr->sh_addralign = scn->zdata_align;
511 shdr->sh_flags &= ~SHF_COMPRESSED;
512 }
513
514 __libelf_reset_rawdata (scn, scn->zdata_base,
515 scn->zdata_size, scn->zdata_align,
516 __libelf_data_type (elf, sh_type));
517
518 return 1;
519 }
520 else
521 {
522 __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
523 return -1;
524 }
525}