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