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 370 : __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 370 : Elf_Data *data = elf_getdata (scn, NULL);
79 370 : 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 370 : Elf_Data *next_data = elf_getdata (scn, data);
87 370 : if (next_data == NULL && !force
88 348 : && data->d_size <= hsize + 5 + 6)
89 : return (void *) -1;
90 :
91 356 : *orig_addralign = data->d_align;
92 356 : *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 356 : size_t block = (data->d_size / 8) + hsize;
98 356 : size_t out_size = 2 * block;
99 356 : void *out_buf = malloc (out_size);
100 356 : 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 356 : size_t used = hsize;
108 :
109 : z_stream z;
110 356 : z.zalloc = Z_NULL;
111 356 : z.zfree = Z_NULL;
112 356 : z.opaque = Z_NULL;
113 356 : int zrc = deflateInit (&z, Z_BEST_COMPRESSION);
114 356 : 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 356 : cdata.d_buf = NULL;
123 :
124 : /* Loop over data buffers. */
125 356 : int flush = Z_NO_FLUSH;
126 : do
127 : {
128 : /* Convert to raw if different endianess. */
129 356 : cdata = *data;
130 356 : 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 173 : cdata.d_buf = malloc (data->d_size);
135 173 : if (cdata.d_buf == NULL)
136 : {
137 0 : __libelf_seterrno (ELF_E_NOMEM);
138 0 : return deflate_cleanup (NULL);
139 : }
140 173 : if (gelf_xlatetof (scn->elf, &cdata, data, ei_data) == NULL)
141 0 : return deflate_cleanup (NULL);
142 : }
143 :
144 356 : z.avail_in = cdata.d_size;
145 356 : z.next_in = cdata.d_buf;
146 :
147 : /* Get next buffer to see if this is the last one. */
148 356 : data = next_data;
149 356 : 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 870 : z.avail_out = out_size - used;
162 870 : z.next_out = out_buf + used;
163 870 : zrc = deflate (&z, flush);
164 870 : if (zrc == Z_STREAM_ERROR)
165 : {
166 0 : __libelf_seterrno (ELF_E_COMPRESS_ERROR);
167 0 : return deflate_cleanup (NULL);
168 : }
169 870 : 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 870 : if (!force && flush == Z_FINISH && used >= *orig_size)
175 44 : return deflate_cleanup ((void *) -1);
176 :
177 826 : if (z.avail_out == 0)
178 : {
179 514 : void *bigger = realloc (out_buf, out_size + block);
180 514 : 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 826 : while (z.avail_out == 0); /* Need more output buffer. */
190 :
191 312 : if (ei_data != MY_ELFDATA)
192 : {
193 154 : free (cdata.d_buf);
194 154 : cdata.d_buf = NULL;
195 : }
196 : }
197 312 : while (flush != Z_FINISH); /* More data blocks. */
198 :
199 312 : zrc = deflateEnd (&z);
200 312 : if (zrc != Z_OK)
201 : {
202 0 : __libelf_seterrno (ELF_E_COMPRESS_ERROR);
203 0 : return deflate_cleanup (NULL);
204 : }
205 :
206 312 : *new_size = used;
207 312 : return out_buf;
208 : }
209 :
210 : void *
211 : internal_function
212 490 : __libelf_decompress (void *buf_in, size_t size_in, size_t size_out)
213 : {
214 490 : void *buf_out = malloc (size_out);
215 490 : if (unlikely (buf_out == NULL))
216 : {
217 0 : __libelf_seterrno (ELF_E_NOMEM);
218 0 : return NULL;
219 : }
220 :
221 490 : z_stream z =
222 : {
223 : .next_in = buf_in,
224 : .avail_in = size_in,
225 : .next_out = buf_out,
226 : .avail_out = size_out
227 : };
228 490 : int zrc = inflateInit (&z);
229 1470 : while (z.avail_in > 0 && likely (zrc == Z_OK))
230 : {
231 490 : z.next_out = buf_out + (size_out - z.avail_out);
232 490 : zrc = inflate (&z, Z_FINISH);
233 490 : if (unlikely (zrc != Z_STREAM_END))
234 : {
235 : zrc = Z_DATA_ERROR;
236 : break;
237 : }
238 490 : zrc = inflateReset (&z);
239 : }
240 490 : if (likely (zrc == Z_OK))
241 490 : zrc = inflateEnd (&z);
242 :
243 490 : if (unlikely (zrc != Z_OK) || unlikely (z.avail_out != 0))
244 : {
245 0 : free (buf_out);
246 0 : __libelf_seterrno (ELF_E_DECOMPRESS_ERROR);
247 0 : return NULL;
248 : }
249 :
250 : return buf_out;
251 : }
252 :
253 : void *
254 : internal_function
255 230 : __libelf_decompress_elf (Elf_Scn *scn, size_t *size_out, size_t *addralign)
256 : {
257 : GElf_Chdr chdr;
258 230 : if (gelf_getchdr (scn, &chdr) == NULL)
259 : return NULL;
260 :
261 230 : if (chdr.ch_type != ELFCOMPRESS_ZLIB)
262 : {
263 0 : __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
264 0 : return NULL;
265 : }
266 :
267 230 : if (! powerof2 (chdr.ch_addralign))
268 : {
269 0 : __libelf_seterrno (ELF_E_INVALID_ALIGN);
270 0 : return NULL;
271 : }
272 :
273 : /* Take the in-memory representation, so we can even handle a
274 : section that has just been constructed (maybe it was copied
275 : over from some other ELF file first with elf_newdata). This
276 : is slightly inefficient when the raw data needs to be
277 : converted since then we'll be converting the whole buffer and
278 : not just Chdr. */
279 230 : Elf_Data *data = elf_getdata (scn, NULL);
280 230 : if (data == NULL)
281 : return NULL;
282 :
283 230 : int elfclass = scn->elf->class;
284 230 : size_t hsize = (elfclass == ELFCLASS32
285 230 : ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
286 230 : size_t size_in = data->d_size - hsize;
287 230 : void *buf_in = data->d_buf + hsize;
288 230 : void *buf_out = __libelf_decompress (buf_in, size_in, chdr.ch_size);
289 230 : *size_out = chdr.ch_size;
290 230 : *addralign = chdr.ch_addralign;
291 230 : return buf_out;
292 : }
293 :
294 : /* Assumes buf is a malloced buffer. */
295 : void
296 : internal_function
297 798 : __libelf_reset_rawdata (Elf_Scn *scn, void *buf, size_t size, size_t align,
298 : Elf_Type type)
299 : {
300 : /* This is the new raw data, replace and possibly free old data. */
301 798 : scn->rawdata.d.d_off = 0;
302 798 : scn->rawdata.d.d_version = __libelf_version;
303 798 : scn->rawdata.d.d_buf = buf;
304 798 : scn->rawdata.d.d_size = size;
305 798 : scn->rawdata.d.d_align = align;
306 798 : scn->rawdata.d.d_type = type;
307 :
308 : /* Existing existing data is no longer valid. */
309 798 : scn->data_list_rear = NULL;
310 798 : if (scn->data_base != scn->rawdata_base)
311 424 : free (scn->data_base);
312 798 : scn->data_base = NULL;
313 798 : if (scn->elf->map_address == NULL
314 113 : || scn->rawdata_base == scn->zdata_base
315 113 : || (scn->flags & ELF_F_MALLOCED) != 0)
316 685 : free (scn->rawdata_base);
317 :
318 798 : scn->rawdata_base = buf;
319 798 : scn->flags |= ELF_F_MALLOCED;
320 798 : }
321 :
322 : int
323 420 : elf_compress (Elf_Scn *scn, int type, unsigned int flags)
324 : {
325 420 : if (scn == NULL)
326 : return -1;
327 :
328 420 : if ((flags & ~ELF_CHF_FORCE) != 0)
329 : {
330 0 : __libelf_seterrno (ELF_E_INVALID_OPERAND);
331 0 : return -1;
332 : }
333 :
334 420 : bool force = (flags & ELF_CHF_FORCE) != 0;
335 :
336 420 : Elf *elf = scn->elf;
337 : GElf_Ehdr ehdr;
338 420 : if (gelf_getehdr (elf, &ehdr) == NULL)
339 : return -1;
340 :
341 420 : int elfclass = elf->class;
342 420 : int elfdata = ehdr.e_ident[EI_DATA];
343 :
344 : Elf64_Xword sh_flags;
345 : Elf64_Word sh_type;
346 : Elf64_Xword sh_addralign;
347 420 : if (elfclass == ELFCLASS32)
348 : {
349 187 : Elf32_Shdr *shdr = elf32_getshdr (scn);
350 187 : if (shdr == NULL)
351 : return -1;
352 :
353 187 : sh_flags = shdr->sh_flags;
354 187 : sh_type = shdr->sh_type;
355 187 : sh_addralign = shdr->sh_addralign;
356 : }
357 : else
358 : {
359 233 : Elf64_Shdr *shdr = elf64_getshdr (scn);
360 233 : if (shdr == NULL)
361 : return -1;
362 :
363 233 : sh_flags = shdr->sh_flags;
364 233 : sh_type = shdr->sh_type;
365 233 : sh_addralign = shdr->sh_addralign;
366 : }
367 :
368 420 : if ((sh_flags & SHF_ALLOC) != 0)
369 : {
370 0 : __libelf_seterrno (ELF_E_INVALID_SECTION_FLAGS);
371 0 : return -1;
372 : }
373 :
374 420 : if (sh_type == SHT_NULL || sh_type == SHT_NOBITS)
375 : {
376 0 : __libelf_seterrno (ELF_E_INVALID_SECTION_TYPE);
377 0 : return -1;
378 : }
379 :
380 420 : int compressed = (sh_flags & SHF_COMPRESSED);
381 420 : 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 194 : size_t hsize = (elfclass == ELFCLASS32
391 194 : ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
392 : size_t orig_size, orig_addralign, new_size;
393 194 : 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 194 : if (out_buf == (void *) -1)
399 : return 0;
400 :
401 : /* Compression failed, return error. */
402 165 : if (out_buf == NULL)
403 : return -1;
404 :
405 : /* Put the header in front of the data. */
406 165 : if (elfclass == ELFCLASS32)
407 : {
408 : Elf32_Chdr chdr;
409 76 : chdr.ch_type = ELFCOMPRESS_ZLIB;
410 76 : chdr.ch_size = orig_size;
411 76 : chdr.ch_addralign = orig_addralign;
412 76 : if (elfdata != MY_ELFDATA)
413 : {
414 39 : CONVERT (chdr.ch_type);
415 78 : CONVERT (chdr.ch_size);
416 78 : CONVERT (chdr.ch_addralign);
417 : }
418 76 : memcpy (out_buf, &chdr, sizeof (Elf32_Chdr));
419 : }
420 : else
421 : {
422 : Elf64_Chdr chdr;
423 89 : chdr.ch_type = ELFCOMPRESS_ZLIB;
424 89 : chdr.ch_reserved = 0;
425 89 : chdr.ch_size = orig_size;
426 89 : chdr.ch_addralign = sh_addralign;
427 89 : if (elfdata != MY_ELFDATA)
428 : {
429 39 : CONVERT (chdr.ch_type);
430 39 : CONVERT (chdr.ch_reserved);
431 78 : CONVERT (chdr.ch_size);
432 78 : CONVERT (chdr.ch_addralign);
433 : }
434 89 : 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 165 : if (elfclass == ELFCLASS32)
440 : {
441 76 : Elf32_Shdr *shdr = elf32_getshdr (scn);
442 76 : shdr->sh_size = new_size;
443 76 : shdr->sh_addralign = 1;
444 76 : shdr->sh_flags |= SHF_COMPRESSED;
445 : }
446 : else
447 : {
448 89 : Elf64_Shdr *shdr = elf64_getshdr (scn);
449 89 : shdr->sh_size = new_size;
450 89 : shdr->sh_addralign = 1;
451 89 : shdr->sh_flags |= SHF_COMPRESSED;
452 : }
453 :
454 165 : __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 165 : scn->zdata_base = NULL;
461 :
462 165 : return 1;
463 : }
464 226 : else if (type == 0)
465 : {
466 : /* Decompress/Inflate. */
467 226 : if (compressed == 0)
468 : {
469 0 : __libelf_seterrno (ELF_E_NOT_COMPRESSED);
470 0 : 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 226 : if (scn->zdata_base == NULL)
477 : {
478 : size_t size_out, addralign;
479 224 : void *buf_out = __libelf_decompress_elf (scn, &size_out, &addralign);
480 224 : if (buf_out == NULL)
481 0 : return -1;
482 :
483 224 : scn->zdata_base = buf_out;
484 224 : scn->zdata_size = size_out;
485 224 : 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 226 : if (elfclass == ELFCLASS32)
491 : {
492 99 : Elf32_Shdr *shdr = elf32_getshdr (scn);
493 99 : shdr->sh_size = scn->zdata_size;
494 99 : shdr->sh_addralign = scn->zdata_align;
495 99 : shdr->sh_flags &= ~SHF_COMPRESSED;
496 : }
497 : else
498 : {
499 127 : Elf64_Shdr *shdr = elf64_getshdr (scn);
500 127 : shdr->sh_size = scn->zdata_size;
501 127 : shdr->sh_addralign = scn->zdata_align;
502 127 : shdr->sh_flags &= ~SHF_COMPRESSED;
503 : }
504 :
505 226 : __libelf_reset_rawdata (scn, scn->zdata_base,
506 : scn->zdata_size, scn->zdata_align,
507 : __libelf_data_type (elf, sh_type));
508 :
509 226 : return 1;
510 : }
511 : else
512 : {
513 0 : __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
514 0 : return -1;
515 : }
516 : }
|