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 : int ei_data, Elf_Data *cdatap)
48 : {
49 44 : deflateEnd (z);
50 44 : free (out_buf);
51 44 : if (ei_data != MY_ELFDATA)
52 19 : 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. */
66 : void *
67 : internal_function
68 484 : __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 484 : Elf_Data *data = elf_getdata (scn, NULL);
79 484 : 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 484 : Elf_Data *next_data = elf_getdata (scn, data);
87 484 : if (next_data == NULL && !force
88 348 : && data->d_size <= hsize + 5 + 6)
89 : return (void *) -1;
90 :
91 470 : *orig_addralign = data->d_align;
92 470 : *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 470 : size_t block = (data->d_size / 8) + hsize;
98 470 : size_t out_size = 2 * block;
99 470 : void *out_buf = malloc (out_size);
100 470 : 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 470 : size_t used = hsize;
108 :
109 : z_stream z;
110 470 : z.zalloc = Z_NULL;
111 470 : z.zfree = Z_NULL;
112 470 : z.opaque = Z_NULL;
113 470 : int zrc = deflateInit (&z, Z_BEST_COMPRESSION);
114 470 : 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 : Elf_Data cdata;
122 470 : cdata.d_buf = NULL;
123 :
124 : /* Loop over data buffers. */
125 470 : int flush = Z_NO_FLUSH;
126 : do
127 : {
128 : /* Convert to raw if different endianess. */
129 470 : cdata = *data;
130 470 : 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 192 : cdata.d_buf = malloc (data->d_size);
135 192 : if (cdata.d_buf == NULL)
136 : {
137 0 : __libelf_seterrno (ELF_E_NOMEM);
138 0 : return deflate_cleanup (NULL);
139 : }
140 192 : if (gelf_xlatetof (scn->elf, &cdata, data, ei_data) == NULL)
141 0 : return deflate_cleanup (NULL);
142 : }
143 :
144 470 : z.avail_in = cdata.d_size;
145 470 : z.next_in = cdata.d_buf;
146 :
147 : /* Get next buffer to see if this is the last one. */
148 470 : data = next_data;
149 470 : if (data != NULL)
150 : {
151 0 : *orig_addralign = MAX (*orig_addralign, data->d_align);
152 0 : *orig_size += data->d_size;
153 0 : next_data = elf_getdata (scn, data);
154 : }
155 : else
156 : flush = Z_FINISH;
157 :
158 : /* Flush one data buffer. */
159 : do
160 : {
161 1074 : z.avail_out = out_size - used;
162 1074 : z.next_out = out_buf + used;
163 1074 : zrc = deflate (&z, flush);
164 1074 : if (zrc == Z_STREAM_ERROR)
165 : {
166 0 : __libelf_seterrno (ELF_E_COMPRESS_ERROR);
167 0 : return deflate_cleanup (NULL);
168 : }
169 1074 : 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 1074 : if (!force && flush == Z_FINISH && used >= *orig_size)
175 44 : return deflate_cleanup ((void *) -1);
176 :
177 1030 : if (z.avail_out == 0)
178 : {
179 604 : void *bigger = realloc (out_buf, out_size + block);
180 604 : if (bigger == NULL)
181 : {
182 0 : __libelf_seterrno (ELF_E_NOMEM);
183 0 : return deflate_cleanup (NULL);
184 : }
185 : out_buf = bigger;
186 : out_size += block;
187 : }
188 : }
189 1030 : while (z.avail_out == 0); /* Need more output buffer. */
190 :
191 426 : if (ei_data != MY_ELFDATA)
192 : {
193 173 : free (cdata.d_buf);
194 173 : cdata.d_buf = NULL;
195 : }
196 : }
197 426 : while (flush != Z_FINISH); /* More data blocks. */
198 :
199 426 : zrc = deflateEnd (&z);
200 426 : if (zrc != Z_OK)
201 : {
202 0 : __libelf_seterrno (ELF_E_COMPRESS_ERROR);
203 0 : return deflate_cleanup (NULL);
204 : }
205 :
206 426 : *new_size = used;
207 426 : return out_buf;
208 : }
209 :
210 : void *
211 : internal_function
212 1020 : __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 1020 : if (unlikely (size_out / 1032 > size_in))
218 : {
219 0 : __libelf_seterrno (ELF_E_INVALID_DATA);
220 0 : return NULL;
221 : }
222 :
223 1020 : void *buf_out = malloc (size_out);
224 1020 : if (unlikely (buf_out == NULL))
225 : {
226 0 : __libelf_seterrno (ELF_E_NOMEM);
227 0 : return NULL;
228 : }
229 :
230 1020 : 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 1020 : int zrc = inflateInit (&z);
238 3060 : while (z.avail_in > 0 && likely (zrc == Z_OK))
239 : {
240 1020 : z.next_out = buf_out + (size_out - z.avail_out);
241 1020 : zrc = inflate (&z, Z_FINISH);
242 1020 : if (unlikely (zrc != Z_STREAM_END))
243 : {
244 : zrc = Z_DATA_ERROR;
245 : break;
246 : }
247 1020 : zrc = inflateReset (&z);
248 : }
249 1020 : if (likely (zrc == Z_OK))
250 1020 : zrc = inflateEnd (&z);
251 :
252 1020 : if (unlikely (zrc != Z_OK) || unlikely (z.avail_out != 0))
253 : {
254 0 : free (buf_out);
255 0 : __libelf_seterrno (ELF_E_DECOMPRESS_ERROR);
256 0 : return NULL;
257 : }
258 :
259 : return buf_out;
260 : }
261 :
262 : void *
263 : internal_function
264 736 : __libelf_decompress_elf (Elf_Scn *scn, size_t *size_out, size_t *addralign)
265 : {
266 : GElf_Chdr chdr;
267 736 : if (gelf_getchdr (scn, &chdr) == NULL)
268 : return NULL;
269 :
270 736 : if (chdr.ch_type != ELFCOMPRESS_ZLIB)
271 : {
272 0 : __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
273 0 : return NULL;
274 : }
275 :
276 736 : if (! powerof2 (chdr.ch_addralign))
277 : {
278 0 : __libelf_seterrno (ELF_E_INVALID_ALIGN);
279 0 : 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 736 : Elf_Data *data = elf_getdata (scn, NULL);
289 736 : if (data == NULL)
290 : return NULL;
291 :
292 736 : int elfclass = scn->elf->class;
293 736 : size_t hsize = (elfclass == ELFCLASS32
294 736 : ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
295 736 : size_t size_in = data->d_size - hsize;
296 736 : void *buf_in = data->d_buf + hsize;
297 736 : void *buf_out = __libelf_decompress (buf_in, size_in, chdr.ch_size);
298 736 : *size_out = chdr.ch_size;
299 736 : *addralign = chdr.ch_addralign;
300 736 : return buf_out;
301 : }
302 :
303 : /* Assumes buf is a malloced buffer. */
304 : void
305 : internal_function
306 1442 : __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 1442 : scn->rawdata.d.d_off = 0;
311 1442 : scn->rawdata.d.d_version = __libelf_version;
312 1442 : scn->rawdata.d.d_buf = buf;
313 1442 : scn->rawdata.d.d_size = size;
314 1442 : scn->rawdata.d.d_align = align;
315 1442 : scn->rawdata.d.d_type = type;
316 :
317 : /* Existing existing data is no longer valid. */
318 1442 : scn->data_list_rear = NULL;
319 1442 : if (scn->data_base != scn->rawdata_base)
320 401 : free (scn->data_base);
321 1442 : scn->data_base = NULL;
322 1442 : if (scn->elf->map_address == NULL
323 630 : || scn->rawdata_base == scn->zdata_base
324 630 : || (scn->flags & ELF_F_MALLOCED) != 0)
325 812 : free (scn->rawdata_base);
326 :
327 1442 : scn->rawdata_base = buf;
328 1442 : scn->flags |= ELF_F_MALLOCED;
329 :
330 : /* Pretend we (tried to) read the data from the file and setup the
331 : data (might have to convert the Chdr to native format). */
332 1442 : scn->data_read = 1;
333 1442 : scn->flags |= ELF_F_FILEDATA;
334 1442 : __libelf_set_data_list_rdlock (scn, 1);
335 1442 : }
336 :
337 : int
338 333735 : elf_compress (Elf_Scn *scn, int type, unsigned int flags)
339 : {
340 333735 : if (scn == NULL)
341 : return -1;
342 :
343 333735 : if ((flags & ~ELF_CHF_FORCE) != 0)
344 : {
345 0 : __libelf_seterrno (ELF_E_INVALID_OPERAND);
346 0 : return -1;
347 : }
348 :
349 333735 : bool force = (flags & ELF_CHF_FORCE) != 0;
350 :
351 333735 : Elf *elf = scn->elf;
352 : GElf_Ehdr ehdr;
353 333735 : if (gelf_getehdr (elf, &ehdr) == NULL)
354 : return -1;
355 :
356 333735 : int elfclass = elf->class;
357 333735 : int elfdata = ehdr.e_ident[EI_DATA];
358 :
359 : Elf64_Xword sh_flags;
360 : Elf64_Word sh_type;
361 : Elf64_Xword sh_addralign;
362 333735 : if (elfclass == ELFCLASS32)
363 : {
364 330705 : Elf32_Shdr *shdr = elf32_getshdr (scn);
365 330705 : if (shdr == NULL)
366 : return -1;
367 :
368 330705 : sh_flags = shdr->sh_flags;
369 330705 : sh_type = shdr->sh_type;
370 330705 : sh_addralign = shdr->sh_addralign;
371 : }
372 : else
373 : {
374 3030 : Elf64_Shdr *shdr = elf64_getshdr (scn);
375 3030 : if (shdr == NULL)
376 : return -1;
377 :
378 3030 : sh_flags = shdr->sh_flags;
379 3030 : sh_type = shdr->sh_type;
380 3030 : sh_addralign = shdr->sh_addralign;
381 : }
382 :
383 333735 : if ((sh_flags & SHF_ALLOC) != 0)
384 : {
385 156369 : __libelf_seterrno (ELF_E_INVALID_SECTION_FLAGS);
386 156369 : return -1;
387 : }
388 :
389 177366 : if (sh_type == SHT_NULL || sh_type == SHT_NOBITS)
390 : {
391 40 : __libelf_seterrno (ELF_E_INVALID_SECTION_TYPE);
392 40 : return -1;
393 : }
394 :
395 177326 : int compressed = (sh_flags & SHF_COMPRESSED);
396 177326 : if (type == ELFCOMPRESS_ZLIB)
397 : {
398 : /* Compress/Deflate. */
399 : if (compressed == 1)
400 : {
401 : __libelf_seterrno (ELF_E_ALREADY_COMPRESSED);
402 : return -1;
403 : }
404 :
405 304 : size_t hsize = (elfclass == ELFCLASS32
406 304 : ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
407 : size_t orig_size, orig_addralign, new_size;
408 304 : void *out_buf = __libelf_compress (scn, hsize, elfdata,
409 : &orig_size, &orig_addralign,
410 : &new_size, force);
411 :
412 : /* Compression would make section larger, don't change anything. */
413 304 : if (out_buf == (void *) -1)
414 : return 0;
415 :
416 : /* Compression failed, return error. */
417 275 : if (out_buf == NULL)
418 : return -1;
419 :
420 : /* Put the header in front of the data. */
421 275 : if (elfclass == ELFCLASS32)
422 : {
423 : Elf32_Chdr chdr;
424 85 : chdr.ch_type = ELFCOMPRESS_ZLIB;
425 85 : chdr.ch_size = orig_size;
426 85 : chdr.ch_addralign = orig_addralign;
427 85 : if (elfdata != MY_ELFDATA)
428 : {
429 48 : CONVERT (chdr.ch_type);
430 96 : CONVERT (chdr.ch_size);
431 96 : CONVERT (chdr.ch_addralign);
432 : }
433 85 : memcpy (out_buf, &chdr, sizeof (Elf32_Chdr));
434 : }
435 : else
436 : {
437 : Elf64_Chdr chdr;
438 190 : chdr.ch_type = ELFCOMPRESS_ZLIB;
439 190 : chdr.ch_reserved = 0;
440 190 : chdr.ch_size = orig_size;
441 190 : chdr.ch_addralign = sh_addralign;
442 190 : if (elfdata != MY_ELFDATA)
443 : {
444 45 : CONVERT (chdr.ch_type);
445 45 : CONVERT (chdr.ch_reserved);
446 90 : CONVERT (chdr.ch_size);
447 90 : CONVERT (chdr.ch_addralign);
448 : }
449 190 : memcpy (out_buf, &chdr, sizeof (Elf64_Chdr));
450 : }
451 :
452 : /* Note we keep the sh_entsize as is, we assume it is setup
453 : correctly and ignored when SHF_COMPRESSED is set. */
454 275 : if (elfclass == ELFCLASS32)
455 : {
456 85 : Elf32_Shdr *shdr = elf32_getshdr (scn);
457 85 : shdr->sh_size = new_size;
458 85 : shdr->sh_addralign = __libelf_type_align (ELFCLASS32, ELF_T_CHDR);
459 85 : shdr->sh_flags |= SHF_COMPRESSED;
460 : }
461 : else
462 : {
463 190 : Elf64_Shdr *shdr = elf64_getshdr (scn);
464 190 : shdr->sh_size = new_size;
465 190 : shdr->sh_addralign = __libelf_type_align (ELFCLASS64, ELF_T_CHDR);
466 190 : shdr->sh_flags |= SHF_COMPRESSED;
467 : }
468 :
469 275 : __libelf_reset_rawdata (scn, out_buf, new_size, 1, ELF_T_CHDR);
470 :
471 : /* The section is now compressed, we could keep the uncompressed
472 : data around, but since that might have been multiple Elf_Data
473 : buffers let the user uncompress it explicitly again if they
474 : want it to simplify bookkeeping. */
475 275 : scn->zdata_base = NULL;
476 :
477 275 : return 1;
478 : }
479 177022 : else if (type == 0)
480 : {
481 : /* Decompress/Inflate. */
482 177022 : if (compressed == 0)
483 : {
484 176290 : __libelf_seterrno (ELF_E_NOT_COMPRESSED);
485 176290 : return -1;
486 : }
487 :
488 : /* If the data is already decompressed (by elf_strptr), then we
489 : only need to setup the rawdata and section header. XXX what
490 : about elf_newdata? */
491 732 : if (scn->zdata_base == NULL)
492 : {
493 : size_t size_out, addralign;
494 730 : void *buf_out = __libelf_decompress_elf (scn, &size_out, &addralign);
495 730 : if (buf_out == NULL)
496 0 : return -1;
497 :
498 730 : scn->zdata_base = buf_out;
499 730 : scn->zdata_size = size_out;
500 730 : scn->zdata_align = addralign;
501 : }
502 :
503 : /* Note we keep the sh_entsize as is, we assume it is setup
504 : correctly and ignored when SHF_COMPRESSED is set. */
505 732 : if (elfclass == ELFCLASS32)
506 : {
507 156 : Elf32_Shdr *shdr = elf32_getshdr (scn);
508 156 : shdr->sh_size = scn->zdata_size;
509 156 : shdr->sh_addralign = scn->zdata_align;
510 156 : shdr->sh_flags &= ~SHF_COMPRESSED;
511 : }
512 : else
513 : {
514 576 : Elf64_Shdr *shdr = elf64_getshdr (scn);
515 576 : shdr->sh_size = scn->zdata_size;
516 576 : shdr->sh_addralign = scn->zdata_align;
517 576 : shdr->sh_flags &= ~SHF_COMPRESSED;
518 : }
519 :
520 732 : __libelf_reset_rawdata (scn, scn->zdata_base,
521 : scn->zdata_size, scn->zdata_align,
522 : __libelf_data_type (elf, sh_type,
523 : scn->zdata_align));
524 :
525 732 : return 1;
526 : }
527 : else
528 : {
529 0 : __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
530 0 : return -1;
531 : }
532 : }
|