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