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 *
do_deflate_cleanup(void * result,z_stream * z,void * out_buf,Elf_Data * cdatap)46 do_deflate_cleanup (void *result, z_stream *z, void *out_buf,
47 Elf_Data *cdatap)
48 {
49 deflateEnd (z);
50 free (out_buf);
51 if (cdatap != NULL)
52 free (cdatap->d_buf);
53 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
__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)68 __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 Elf_Data *data = elf_getdata (scn, NULL);
79 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 Elf_Data *next_data = elf_getdata (scn, data);
87 if (next_data == NULL && !force
88 && data->d_size <= hsize + 5 + 6)
89 return (void *) -1;
90
91 *orig_addralign = data->d_align;
92 *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 size_t block = (data->d_size / 8) + hsize;
98 size_t out_size = 2 * block;
99 void *out_buf = malloc (out_size);
100 if (out_buf == NULL)
101 {
102 __libelf_seterrno (ELF_E_NOMEM);
103 return NULL;
104 }
105
106 /* Caller gets to fill in the header at the start. Just skip it here. */
107 size_t used = hsize;
108
109 z_stream z;
110 z.zalloc = Z_NULL;
111 z.zfree = Z_NULL;
112 z.opaque = Z_NULL;
113 int zrc = deflateInit (&z, Z_BEST_COMPRESSION);
114 if (zrc != Z_OK)
115 {
116 __libelf_seterrno (ELF_E_COMPRESS_ERROR);
117 return deflate_cleanup(NULL, NULL);
118 }
119
120 Elf_Data cdata;
121 cdata.d_buf = NULL;
122
123 /* Loop over data buffers. */
124 int flush = Z_NO_FLUSH;
125 do
126 {
127 /* Convert to raw if different endianness. */
128 cdata = *data;
129 bool convert = ei_data != MY_ELFDATA && data->d_size > 0;
130 if (convert)
131 {
132 /* Don't do this conversion in place, we might want to keep
133 the original data around, caller decides. */
134 cdata.d_buf = malloc (data->d_size);
135 if (cdata.d_buf == NULL)
136 {
137 __libelf_seterrno (ELF_E_NOMEM);
138 return deflate_cleanup (NULL, NULL);
139 }
140 if (gelf_xlatetof (scn->elf, &cdata, data, ei_data) == NULL)
141 return deflate_cleanup (NULL, &cdata);
142 }
143
144 z.avail_in = cdata.d_size;
145 z.next_in = cdata.d_buf;
146
147 /* Get next buffer to see if this is the last one. */
148 data = next_data;
149 if (data != NULL)
150 {
151 *orig_addralign = MAX (*orig_addralign, data->d_align);
152 *orig_size += data->d_size;
153 next_data = elf_getdata (scn, data);
154 }
155 else
156 flush = Z_FINISH;
157
158 /* Flush one data buffer. */
159 do
160 {
161 z.avail_out = out_size - used;
162 z.next_out = out_buf + used;
163 zrc = deflate (&z, flush);
164 if (zrc == Z_STREAM_ERROR)
165 {
166 __libelf_seterrno (ELF_E_COMPRESS_ERROR);
167 return deflate_cleanup (NULL, convert ? &cdata : NULL);
168 }
169 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 if (!force && flush == Z_FINISH && used >= *orig_size)
175 return deflate_cleanup ((void *) -1, convert ? &cdata : NULL);
176
177 if (z.avail_out == 0)
178 {
179 void *bigger = realloc (out_buf, out_size + block);
180 if (bigger == NULL)
181 {
182 __libelf_seterrno (ELF_E_NOMEM);
183 return deflate_cleanup (NULL, convert ? &cdata : NULL);
184 }
185 out_buf = bigger;
186 out_size += block;
187 }
188 }
189 while (z.avail_out == 0); /* Need more output buffer. */
190
191 if (convert)
192 {
193 free (cdata.d_buf);
194 cdata.d_buf = NULL;
195 }
196 }
197 while (flush != Z_FINISH); /* More data blocks. */
198
199 if (zrc != Z_STREAM_END)
200 {
201 __libelf_seterrno (ELF_E_COMPRESS_ERROR);
202 return deflate_cleanup (NULL, NULL);
203 }
204
205 deflateEnd (&z);
206 *new_size = used;
207 return out_buf;
208 }
209
210 void *
211 internal_function
__libelf_decompress(void * buf_in,size_t size_in,size_t size_out)212 __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 if (unlikely (size_out / 1032 > size_in))
218 {
219 __libelf_seterrno (ELF_E_INVALID_DATA);
220 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 void *buf_out = malloc (size_out ?: 1);
228 if (unlikely (buf_out == NULL))
229 {
230 __libelf_seterrno (ELF_E_NOMEM);
231 return NULL;
232 }
233
234 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 int zrc = inflateInit (&z);
242 while (z.avail_in > 0 && likely (zrc == Z_OK))
243 {
244 z.next_out = buf_out + (size_out - z.avail_out);
245 zrc = inflate (&z, Z_FINISH);
246 if (unlikely (zrc != Z_STREAM_END))
247 {
248 zrc = Z_DATA_ERROR;
249 break;
250 }
251 zrc = inflateReset (&z);
252 }
253
254 if (unlikely (zrc != Z_OK) || unlikely (z.avail_out != 0))
255 {
256 free (buf_out);
257 buf_out = NULL;
258 __libelf_seterrno (ELF_E_DECOMPRESS_ERROR);
259 }
260
261 inflateEnd(&z);
262 return buf_out;
263 }
264
265 void *
266 internal_function
__libelf_decompress_elf(Elf_Scn * scn,size_t * size_out,size_t * addralign)267 __libelf_decompress_elf (Elf_Scn *scn, size_t *size_out, size_t *addralign)
268 {
269 GElf_Chdr chdr;
270 if (gelf_getchdr (scn, &chdr) == NULL)
271 return NULL;
272
273 if (chdr.ch_type != ELFCOMPRESS_ZLIB)
274 {
275 __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
276 return NULL;
277 }
278
279 if (! powerof2 (chdr.ch_addralign))
280 {
281 __libelf_seterrno (ELF_E_INVALID_ALIGN);
282 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 Elf_Data *data = elf_getdata (scn, NULL);
292 if (data == NULL)
293 return NULL;
294
295 int elfclass = scn->elf->class;
296 size_t hsize = (elfclass == ELFCLASS32
297 ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
298 size_t size_in = data->d_size - hsize;
299 void *buf_in = data->d_buf + hsize;
300 void *buf_out = __libelf_decompress (buf_in, size_in, chdr.ch_size);
301 *size_out = chdr.ch_size;
302 *addralign = chdr.ch_addralign;
303 return buf_out;
304 }
305
306 /* Assumes buf is a malloced buffer. */
307 void
308 internal_function
__libelf_reset_rawdata(Elf_Scn * scn,void * buf,size_t size,size_t align,Elf_Type type)309 __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 scn->rawdata.d.d_off = 0;
314 scn->rawdata.d.d_version = EV_CURRENT;
315 scn->rawdata.d.d_buf = buf;
316 scn->rawdata.d.d_size = size;
317 scn->rawdata.d.d_align = align;
318 scn->rawdata.d.d_type = type;
319
320 /* Existing existing data is no longer valid. */
321 scn->data_list_rear = NULL;
322 if (scn->data_base != scn->rawdata_base)
323 free (scn->data_base);
324 scn->data_base = NULL;
325 if (scn->elf->map_address == NULL
326 || scn->rawdata_base == scn->zdata_base
327 || (scn->flags & ELF_F_MALLOCED) != 0)
328 free (scn->rawdata_base);
329
330 scn->rawdata_base = buf;
331 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 scn->data_read = 1;
336 scn->flags |= ELF_F_FILEDATA;
337 __libelf_set_data_list_rdlock (scn, 1);
338 }
339
340 int
elf_compress(Elf_Scn * scn,int type,unsigned int flags)341 elf_compress (Elf_Scn *scn, int type, unsigned int flags)
342 {
343 if (scn == NULL)
344 return -1;
345
346 if ((flags & ~ELF_CHF_FORCE) != 0)
347 {
348 __libelf_seterrno (ELF_E_INVALID_OPERAND);
349 return -1;
350 }
351
352 bool force = (flags & ELF_CHF_FORCE) != 0;
353
354 Elf *elf = scn->elf;
355 GElf_Ehdr ehdr;
356 if (gelf_getehdr (elf, &ehdr) == NULL)
357 return -1;
358
359 int elfclass = elf->class;
360 int elfdata = ehdr.e_ident[EI_DATA];
361
362 Elf64_Xword sh_flags;
363 Elf64_Word sh_type;
364 Elf64_Xword sh_addralign;
365 if (elfclass == ELFCLASS32)
366 {
367 Elf32_Shdr *shdr = elf32_getshdr (scn);
368 if (shdr == NULL)
369 return -1;
370
371 sh_flags = shdr->sh_flags;
372 sh_type = shdr->sh_type;
373 sh_addralign = shdr->sh_addralign;
374 }
375 else
376 {
377 Elf64_Shdr *shdr = elf64_getshdr (scn);
378 if (shdr == NULL)
379 return -1;
380
381 sh_flags = shdr->sh_flags;
382 sh_type = shdr->sh_type;
383 sh_addralign = shdr->sh_addralign;
384 }
385
386 if ((sh_flags & SHF_ALLOC) != 0)
387 {
388 __libelf_seterrno (ELF_E_INVALID_SECTION_FLAGS);
389 return -1;
390 }
391
392 if (sh_type == SHT_NULL || sh_type == SHT_NOBITS)
393 {
394 __libelf_seterrno (ELF_E_INVALID_SECTION_TYPE);
395 return -1;
396 }
397
398 int compressed = (sh_flags & SHF_COMPRESSED);
399 if (type == ELFCOMPRESS_ZLIB)
400 {
401 /* Compress/Deflate. */
402 if (compressed == 1)
403 {
404 __libelf_seterrno (ELF_E_ALREADY_COMPRESSED);
405 return -1;
406 }
407
408 size_t hsize = (elfclass == ELFCLASS32
409 ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
410 size_t orig_size, orig_addralign, new_size;
411 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 if (out_buf == (void *) -1)
417 return 0;
418
419 /* Compression failed, return error. */
420 if (out_buf == NULL)
421 return -1;
422
423 /* Put the header in front of the data. */
424 if (elfclass == ELFCLASS32)
425 {
426 Elf32_Chdr chdr;
427 chdr.ch_type = ELFCOMPRESS_ZLIB;
428 chdr.ch_size = orig_size;
429 chdr.ch_addralign = orig_addralign;
430 if (elfdata != MY_ELFDATA)
431 {
432 CONVERT (chdr.ch_type);
433 CONVERT (chdr.ch_size);
434 CONVERT (chdr.ch_addralign);
435 }
436 memcpy (out_buf, &chdr, sizeof (Elf32_Chdr));
437 }
438 else
439 {
440 Elf64_Chdr chdr;
441 chdr.ch_type = ELFCOMPRESS_ZLIB;
442 chdr.ch_reserved = 0;
443 chdr.ch_size = orig_size;
444 chdr.ch_addralign = sh_addralign;
445 if (elfdata != MY_ELFDATA)
446 {
447 CONVERT (chdr.ch_type);
448 CONVERT (chdr.ch_reserved);
449 CONVERT (chdr.ch_size);
450 CONVERT (chdr.ch_addralign);
451 }
452 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 if (elfclass == ELFCLASS32)
458 {
459 Elf32_Shdr *shdr = elf32_getshdr (scn);
460 shdr->sh_size = new_size;
461 shdr->sh_addralign = __libelf_type_align (ELFCLASS32, ELF_T_CHDR);
462 shdr->sh_flags |= SHF_COMPRESSED;
463 }
464 else
465 {
466 Elf64_Shdr *shdr = elf64_getshdr (scn);
467 shdr->sh_size = new_size;
468 shdr->sh_addralign = __libelf_type_align (ELFCLASS64, ELF_T_CHDR);
469 shdr->sh_flags |= SHF_COMPRESSED;
470 }
471
472 __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 scn->zdata_base = NULL;
479
480 return 1;
481 }
482 else if (type == 0)
483 {
484 /* Decompress/Inflate. */
485 if (compressed == 0)
486 {
487 __libelf_seterrno (ELF_E_NOT_COMPRESSED);
488 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 if (scn->zdata_base == NULL)
495 {
496 size_t size_out, addralign;
497 void *buf_out = __libelf_decompress_elf (scn, &size_out, &addralign);
498 if (buf_out == NULL)
499 return -1;
500
501 scn->zdata_base = buf_out;
502 scn->zdata_size = size_out;
503 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 if (elfclass == ELFCLASS32)
509 {
510 Elf32_Shdr *shdr = elf32_getshdr (scn);
511 shdr->sh_size = scn->zdata_size;
512 shdr->sh_addralign = scn->zdata_align;
513 shdr->sh_flags &= ~SHF_COMPRESSED;
514 }
515 else
516 {
517 Elf64_Shdr *shdr = elf64_getshdr (scn);
518 shdr->sh_size = scn->zdata_size;
519 shdr->sh_addralign = scn->zdata_align;
520 shdr->sh_flags &= ~SHF_COMPRESSED;
521 }
522
523 __libelf_reset_rawdata (scn, scn->zdata_base,
524 scn->zdata_size, scn->zdata_align,
525 __libelf_data_type (&ehdr, sh_type,
526 scn->zdata_align));
527
528 return 1;
529 }
530 else
531 {
532 __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
533 return -1;
534 }
535 }
536