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