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