Branch data 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 "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 : : #ifdef USE_ZSTD
43 : : #include <zstd.h>
44 : : #endif
45 : :
46 : : /* Cleanup and return result. Don't leak memory. */
47 : : static void *
48 : 66 : do_deflate_cleanup (void *result, z_stream *z, void *out_buf,
49 : : Elf_Data *cdatap)
50 : : {
51 : 66 : deflateEnd (z);
52 : 66 : free (out_buf);
53 [ + + ]: 66 : if (cdatap != NULL)
54 : 29 : free (cdatap->d_buf);
55 : 66 : return result;
56 : : }
57 : :
58 : : #define deflate_cleanup(result, cdata) \
59 : : do_deflate_cleanup(result, &z, out_buf, cdata)
60 : :
61 : : static
62 : : void *
63 : 687 : __libelf_compress_zlib (Elf_Scn *scn, size_t hsize, int ei_data,
64 : : size_t *orig_size, size_t *orig_addralign,
65 : : size_t *new_size, bool force,
66 : : Elf_Data *data, Elf_Data *next_data,
67 : : void *out_buf, size_t out_size, size_t block)
68 : : {
69 : : /* Caller gets to fill in the header at the start. Just skip it here. */
70 : 687 : size_t used = hsize;
71 : :
72 : 687 : z_stream z;
73 : 687 : z.zalloc = Z_NULL;
74 : 687 : z.zfree = Z_NULL;
75 : 687 : z.opaque = Z_NULL;
76 : 687 : int zrc = deflateInit (&z, Z_BEST_COMPRESSION);
77 [ - + ]: 687 : if (zrc != Z_OK)
78 : : {
79 : 0 : __libelf_seterrno (ELF_E_COMPRESS_ERROR);
80 : 0 : return deflate_cleanup(NULL, NULL);
81 : : }
82 : :
83 : 687 : Elf_Data cdata;
84 : 687 : cdata.d_buf = NULL;
85 : :
86 : : /* Loop over data buffers. */
87 : 687 : int flush = Z_NO_FLUSH;
88 : 687 : do
89 : : {
90 : : /* Convert to raw if different endianness. */
91 : 687 : cdata = *data;
92 [ + + - + ]: 687 : bool convert = ei_data != MY_ELFDATA && data->d_size > 0;
93 [ + + ]: 687 : if (convert)
94 : : {
95 : : /* Don't do this conversion in place, we might want to keep
96 : : the original data around, caller decides. */
97 : 292 : cdata.d_buf = malloc (data->d_size);
98 [ - + ]: 292 : if (cdata.d_buf == NULL)
99 : : {
100 : 0 : __libelf_seterrno (ELF_E_NOMEM);
101 : 0 : return deflate_cleanup (NULL, NULL);
102 : : }
103 [ - + ]: 292 : if (gelf_xlatetof (scn->elf, &cdata, data, ei_data) == NULL)
104 : 0 : return deflate_cleanup (NULL, &cdata);
105 : : }
106 : :
107 : 687 : z.avail_in = cdata.d_size;
108 : 687 : z.next_in = cdata.d_buf;
109 : :
110 : : /* Get next buffer to see if this is the last one. */
111 : 687 : data = next_data;
112 [ - + ]: 687 : if (data != NULL)
113 : : {
114 : 0 : *orig_addralign = MAX (*orig_addralign, data->d_align);
115 : 0 : *orig_size += data->d_size;
116 : 0 : next_data = elf_getdata (scn, data);
117 : : }
118 : : else
119 : 687 : flush = Z_FINISH;
120 : :
121 : : /* Flush one data buffer. */
122 : 1606 : do
123 : : {
124 : 1606 : z.avail_out = out_size - used;
125 : 1606 : z.next_out = out_buf + used;
126 : 1606 : zrc = deflate (&z, flush);
127 [ - + ]: 1606 : if (zrc == Z_STREAM_ERROR)
128 : : {
129 : 0 : __libelf_seterrno (ELF_E_COMPRESS_ERROR);
130 [ # # ]: 0 : return deflate_cleanup (NULL, convert ? &cdata : NULL);
131 : : }
132 : 1606 : used += (out_size - used) - z.avail_out;
133 : :
134 : : /* Bail out if we are sure the user doesn't want the
135 : : compression forced and we are using more compressed data
136 : : than original data. */
137 [ + + + + ]: 1606 : if (!force && flush == Z_FINISH && used >= *orig_size)
138 [ + + ]: 103 : return deflate_cleanup ((void *) -1, convert ? &cdata : NULL);
139 : :
140 [ + + ]: 1540 : if (z.avail_out == 0)
141 : : {
142 : 919 : void *bigger = realloc (out_buf, out_size + block);
143 [ - + ]: 919 : if (bigger == NULL)
144 : : {
145 : 0 : __libelf_seterrno (ELF_E_NOMEM);
146 [ # # ]: 0 : return deflate_cleanup (NULL, convert ? &cdata : NULL);
147 : : }
148 : : out_buf = bigger;
149 : : out_size += block;
150 : : }
151 : : }
152 [ + + ]: 1540 : while (z.avail_out == 0); /* Need more output buffer. */
153 : :
154 [ + + ]: 621 : if (convert)
155 : : {
156 : 263 : free (cdata.d_buf);
157 : 263 : cdata.d_buf = NULL;
158 : : }
159 : : }
160 [ - + ]: 621 : while (flush != Z_FINISH); /* More data blocks. */
161 : :
162 [ - + ]: 621 : if (zrc != Z_STREAM_END)
163 : : {
164 : 0 : __libelf_seterrno (ELF_E_COMPRESS_ERROR);
165 : 0 : return deflate_cleanup (NULL, NULL);
166 : : }
167 : :
168 : 621 : deflateEnd (&z);
169 : 621 : *new_size = used;
170 : 621 : return out_buf;
171 : : }
172 : :
173 : : #ifdef USE_ZSTD_COMPRESS
174 : : /* Cleanup and return result. Don't leak memory. */
175 : : static void *
176 : 52 : do_zstd_cleanup (void *result, ZSTD_CCtx * const cctx, void *out_buf,
177 : : Elf_Data *cdatap)
178 : : {
179 : 52 : ZSTD_freeCCtx (cctx);
180 : 52 : free (out_buf);
181 [ + + ]: 52 : if (cdatap != NULL)
182 : 24 : free (cdatap->d_buf);
183 : 52 : return result;
184 : : }
185 : :
186 : : #define zstd_cleanup(result, cdata) \
187 : : do_zstd_cleanup(result, cctx, out_buf, cdata)
188 : :
189 : : static
190 : : void *
191 : 232 : __libelf_compress_zstd (Elf_Scn *scn, size_t hsize, int ei_data,
192 : : size_t *orig_size, size_t *orig_addralign,
193 : : size_t *new_size, bool force,
194 : : Elf_Data *data, Elf_Data *next_data,
195 : : void *out_buf, size_t out_size, size_t block)
196 : : {
197 : : /* Caller gets to fill in the header at the start. Just skip it here. */
198 : 232 : size_t used = hsize;
199 : :
200 : 232 : ZSTD_CCtx* const cctx = ZSTD_createCCtx();
201 : 232 : Elf_Data cdata;
202 : 232 : cdata.d_buf = NULL;
203 : :
204 : : /* Loop over data buffers. */
205 : 232 : ZSTD_EndDirective mode = ZSTD_e_continue;
206 : :
207 : 232 : do
208 : : {
209 : : /* Convert to raw if different endianness. */
210 : 232 : cdata = *data;
211 [ + + - + ]: 232 : bool convert = ei_data != MY_ELFDATA && data->d_size > 0;
212 [ + + ]: 232 : if (convert)
213 : : {
214 : : /* Don't do this conversion in place, we might want to keep
215 : : the original data around, caller decides. */
216 : 120 : cdata.d_buf = malloc (data->d_size);
217 [ - + ]: 120 : if (cdata.d_buf == NULL)
218 : : {
219 : 0 : __libelf_seterrno (ELF_E_NOMEM);
220 : 52 : return zstd_cleanup (NULL, NULL);
221 : : }
222 [ - + ]: 120 : if (gelf_xlatetof (scn->elf, &cdata, data, ei_data) == NULL)
223 : 0 : return zstd_cleanup (NULL, &cdata);
224 : : }
225 : :
226 : 232 : ZSTD_inBuffer ib = { cdata.d_buf, cdata.d_size, 0 };
227 : :
228 : : /* Get next buffer to see if this is the last one. */
229 : 232 : data = next_data;
230 [ - + ]: 232 : if (data != NULL)
231 : : {
232 : 0 : *orig_addralign = MAX (*orig_addralign, data->d_align);
233 : 0 : *orig_size += data->d_size;
234 : 0 : next_data = elf_getdata (scn, data);
235 : : }
236 : : else
237 : 232 : mode = ZSTD_e_end;
238 : :
239 : : /* Flush one data buffer. */
240 : 928 : for (;;)
241 : 348 : {
242 : 580 : ZSTD_outBuffer ob = { out_buf + used, out_size - used, 0 };
243 : 580 : size_t ret = ZSTD_compressStream2 (cctx, &ob, &ib, mode);
244 [ - + ]: 580 : if (ZSTD_isError (ret))
245 : : {
246 : 0 : __libelf_seterrno (ELF_E_COMPRESS_ERROR);
247 [ - - ]: 52 : return zstd_cleanup (NULL, convert ? &cdata : NULL);
248 : : }
249 : 580 : used += ob.pos;
250 : :
251 : : /* Bail out if we are sure the user doesn't want the
252 : : compression forced and we are using more compressed data
253 : : than original data. */
254 [ + - + + ]: 580 : if (!force && mode == ZSTD_e_end && used >= *orig_size)
255 [ + + ]: 80 : return zstd_cleanup ((void *) -1, convert ? &cdata : NULL);
256 : :
257 [ + + ]: 528 : if (ret > 0)
258 : : {
259 : 348 : void *bigger = realloc (out_buf, out_size + block);
260 [ - + ]: 348 : if (bigger == NULL)
261 : : {
262 : 0 : __libelf_seterrno (ELF_E_NOMEM);
263 [ # # ]: 0 : return zstd_cleanup (NULL, convert ? &cdata : NULL);
264 : : }
265 : 348 : out_buf = bigger;
266 : 348 : out_size += block;
267 : : }
268 : : else
269 : : break;
270 : : }
271 : :
272 [ + + ]: 180 : if (convert)
273 : : {
274 : 96 : free (cdata.d_buf);
275 : 96 : cdata.d_buf = NULL;
276 : : }
277 : : }
278 [ - + ]: 180 : while (mode != ZSTD_e_end); /* More data blocks. */
279 : :
280 : 180 : ZSTD_freeCCtx (cctx);
281 : 180 : *new_size = used;
282 : 180 : return out_buf;
283 : : }
284 : : #endif
285 : :
286 : : /* Given a section, uses the (in-memory) Elf_Data to extract the
287 : : original data size (including the given header size) and data
288 : : alignment. Returns a buffer that has at least hsize bytes (for the
289 : : caller to fill in with a header) plus zlib compressed date. Also
290 : : returns the new buffer size in new_size (hsize + compressed data
291 : : size). Returns (void *) -1 when FORCE is false and the compressed
292 : : data would be bigger than the original data. */
293 : : void *
294 : : internal_function
295 : 947 : __libelf_compress (Elf_Scn *scn, size_t hsize, int ei_data,
296 : : size_t *orig_size, size_t *orig_addralign,
297 : : size_t *new_size, bool force, bool use_zstd)
298 : : {
299 : : /* The compressed data is the on-disk data. We simplify the
300 : : implementation a bit by asking for the (converted) in-memory
301 : : data (which might be all there is if the user created it with
302 : : elf_newdata) and then convert back to raw if needed before
303 : : compressing. Should be made a bit more clever to directly
304 : : use raw if that is directly available. */
305 : 947 : Elf_Data *data = elf_getdata (scn, NULL);
306 [ + - ]: 947 : if (data == NULL)
307 : : return NULL;
308 : :
309 : : /* When not forced and we immediately know we would use more data by
310 : : compressing, because of the header plus zlib overhead (five bytes
311 : : per 16 KB block, plus a one-time overhead of six bytes for the
312 : : entire stream), don't do anything.
313 : : Size estimation for ZSTD compression would be similar. */
314 : 947 : Elf_Data *next_data = elf_getdata (scn, data);
315 [ + + ]: 947 : if (next_data == NULL && !force
316 [ + + ]: 714 : && data->d_size <= hsize + 5 + 6)
317 : : return (void *) -1;
318 : :
319 : 919 : *orig_addralign = data->d_align;
320 : 919 : *orig_size = data->d_size;
321 : :
322 : : /* Guess an output block size. 1/8th of the original Elf_Data plus
323 : : hsize. Make the first chunk twice that size (25%), then increase
324 : : by a block (12.5%) when necessary. */
325 : 919 : size_t block = (data->d_size / 8) + hsize;
326 : 919 : size_t out_size = 2 * block;
327 : 919 : void *out_buf = malloc (out_size);
328 [ - + ]: 919 : if (out_buf == NULL)
329 : : {
330 : 0 : __libelf_seterrno (ELF_E_NOMEM);
331 : 0 : return NULL;
332 : : }
333 : :
334 [ + + ]: 919 : if (use_zstd)
335 : : {
336 : : #ifdef USE_ZSTD_COMPRESS
337 : 232 : return __libelf_compress_zstd (scn, hsize, ei_data, orig_size,
338 : : orig_addralign, new_size, force,
339 : : data, next_data, out_buf, out_size,
340 : : block);
341 : : #else
342 : : __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
343 : : return NULL;
344 : : #endif
345 : : }
346 : : else
347 : 687 : return __libelf_compress_zlib (scn, hsize, ei_data, orig_size,
348 : : orig_addralign, new_size, force,
349 : : data, next_data, out_buf, out_size,
350 : : block);
351 : : }
352 : :
353 : : void *
354 : : internal_function
355 : 1137 : __libelf_decompress_zlib (void *buf_in, size_t size_in, size_t size_out)
356 : : {
357 : : /* Catch highly unlikely compression ratios so we don't allocate
358 : : some giant amount of memory for nothing. The max compression
359 : : factor 1032:1 comes from http://www.zlib.net/zlib_tech.html */
360 [ - + ]: 1137 : if (unlikely (size_out / 1032 > size_in))
361 : : {
362 : 0 : __libelf_seterrno (ELF_E_INVALID_DATA);
363 : 0 : return NULL;
364 : : }
365 : :
366 : : /* Malloc might return NULL when requesting zero size. This is highly
367 : : unlikely, it would only happen when the compression was forced.
368 : : But we do need a non-NULL buffer to return and set as result.
369 : : Just make sure to always allocate at least 1 byte. */
370 : 1137 : void *buf_out = malloc (size_out ?: 1);
371 [ - + ]: 1137 : if (unlikely (buf_out == NULL))
372 : : {
373 : 0 : __libelf_seterrno (ELF_E_NOMEM);
374 : 0 : return NULL;
375 : : }
376 : :
377 : 1137 : z_stream z =
378 : : {
379 : : .next_in = buf_in,
380 : : .avail_in = size_in,
381 : : .next_out = buf_out,
382 : : .avail_out = size_out
383 : : };
384 : 1137 : int zrc = inflateInit (&z);
385 [ + + + - ]: 2274 : while (z.avail_in > 0 && likely (zrc == Z_OK))
386 : : {
387 : 1137 : z.next_out = buf_out + (size_out - z.avail_out);
388 : 1137 : zrc = inflate (&z, Z_FINISH);
389 [ + - ]: 1137 : if (unlikely (zrc != Z_STREAM_END))
390 : : {
391 : : zrc = Z_DATA_ERROR;
392 : : break;
393 : : }
394 : 1137 : zrc = inflateReset (&z);
395 : : }
396 : :
397 [ + - - + ]: 1137 : if (unlikely (zrc != Z_OK) || unlikely (z.avail_out != 0))
398 : : {
399 : 0 : free (buf_out);
400 : 0 : buf_out = NULL;
401 : 0 : __libelf_seterrno (ELF_E_DECOMPRESS_ERROR);
402 : : }
403 : :
404 : 1137 : inflateEnd(&z);
405 : 1137 : return buf_out;
406 : : }
407 : :
408 : : #ifdef USE_ZSTD
409 : : static void *
410 : 90 : __libelf_decompress_zstd (void *buf_in, size_t size_in, size_t size_out)
411 : : {
412 : : /* Malloc might return NULL when requesting zero size. This is highly
413 : : unlikely, it would only happen when the compression was forced.
414 : : But we do need a non-NULL buffer to return and set as result.
415 : : Just make sure to always allocate at least 1 byte. */
416 : 90 : void *buf_out = malloc (size_out ?: 1);
417 [ - + ]: 90 : if (unlikely (buf_out == NULL))
418 : : {
419 : 0 : __libelf_seterrno (ELF_E_NOMEM);
420 : 0 : return NULL;
421 : : }
422 : :
423 : 90 : size_t ret = ZSTD_decompress (buf_out, size_out, buf_in, size_in);
424 [ - + ]: 90 : if (ZSTD_isError (ret))
425 : : {
426 : 0 : free (buf_out);
427 : 0 : __libelf_seterrno (ELF_E_DECOMPRESS_ERROR);
428 : 0 : return NULL;
429 : : }
430 : : else
431 : : return buf_out;
432 : : }
433 : : #endif
434 : :
435 : : void *
436 : : internal_function
437 : 1227 : __libelf_decompress (int chtype, void *buf_in, size_t size_in, size_t size_out)
438 : : {
439 [ + + ]: 1227 : if (chtype == ELFCOMPRESS_ZLIB)
440 : 1137 : return __libelf_decompress_zlib (buf_in, size_in, size_out);
441 : : else
442 : : {
443 : : #ifdef USE_ZSTD
444 : 90 : return __libelf_decompress_zstd (buf_in, size_in, size_out);
445 : : #else
446 : : __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
447 : : return NULL;
448 : : #endif
449 : : }
450 : : }
451 : :
452 : : void *
453 : : internal_function
454 : 943 : __libelf_decompress_elf (Elf_Scn *scn, size_t *size_out, size_t *addralign)
455 : : {
456 : 943 : GElf_Chdr chdr;
457 [ + - ]: 943 : if (gelf_getchdr (scn, &chdr) == NULL)
458 : : return NULL;
459 : :
460 : 943 : bool unknown_compression = false;
461 [ + + ]: 943 : if (chdr.ch_type != ELFCOMPRESS_ZLIB)
462 : : {
463 [ - + ]: 90 : if (chdr.ch_type != ELFCOMPRESS_ZSTD)
464 : 0 : unknown_compression = true;
465 : :
466 : : #ifndef USE_ZSTD
467 : : if (chdr.ch_type == ELFCOMPRESS_ZSTD)
468 : : unknown_compression = true;
469 : : #endif
470 : : }
471 : :
472 : 0 : if (unknown_compression)
473 : : {
474 : 0 : __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
475 : 0 : return NULL;
476 : : }
477 : :
478 [ - + ]: 943 : if (! powerof2 (chdr.ch_addralign))
479 : : {
480 : 0 : __libelf_seterrno (ELF_E_INVALID_ALIGN);
481 : 0 : return NULL;
482 : : }
483 : :
484 : : /* Take the in-memory representation, so we can even handle a
485 : : section that has just been constructed (maybe it was copied
486 : : over from some other ELF file first with elf_newdata). This
487 : : is slightly inefficient when the raw data needs to be
488 : : converted since then we'll be converting the whole buffer and
489 : : not just Chdr. */
490 : 943 : Elf_Data *data = elf_getdata (scn, NULL);
491 [ + - ]: 943 : if (data == NULL)
492 : : return NULL;
493 : :
494 : 943 : int elfclass = scn->elf->class;
495 : 1886 : size_t hsize = (elfclass == ELFCLASS32
496 [ + + ]: 943 : ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
497 : 943 : size_t size_in = data->d_size - hsize;
498 : 943 : void *buf_in = data->d_buf + hsize;
499 : 943 : void *buf_out
500 : 943 : = __libelf_decompress (chdr.ch_type, buf_in, size_in, chdr.ch_size);
501 : :
502 : 943 : *size_out = chdr.ch_size;
503 : 943 : *addralign = chdr.ch_addralign;
504 : 943 : return buf_out;
505 : : }
506 : :
507 : : /* Assumes buf is a malloced buffer. */
508 : : void
509 : : internal_function
510 : 2024 : __libelf_reset_rawdata (Elf_Scn *scn, void *buf, size_t size, size_t align,
511 : : Elf_Type type)
512 : : {
513 : : /* This is the new raw data, replace and possibly free old data. */
514 : 2024 : scn->rawdata.d.d_off = 0;
515 : 2024 : scn->rawdata.d.d_version = EV_CURRENT;
516 : 2024 : scn->rawdata.d.d_buf = buf;
517 : 2024 : scn->rawdata.d.d_size = size;
518 : 2024 : scn->rawdata.d.d_align = align;
519 : 2024 : scn->rawdata.d.d_type = type;
520 : :
521 : : /* Existing existing data is no longer valid. */
522 : 2024 : scn->data_list_rear = NULL;
523 [ + + ]: 2024 : if (scn->data_base != scn->rawdata_base)
524 : 512 : free (scn->data_base);
525 : 2024 : scn->data_base = NULL;
526 [ + + ]: 2024 : if (scn->elf->map_address == NULL
527 [ + - ]: 647 : || scn->rawdata_base == scn->zdata_base
528 [ - + ]: 647 : || (scn->flags & ELF_F_MALLOCED) != 0)
529 : 1377 : free (scn->rawdata_base);
530 : :
531 : 2024 : scn->rawdata_base = buf;
532 : 2024 : scn->flags |= ELF_F_MALLOCED;
533 : :
534 : : /* Pretend we (tried to) read the data from the file and setup the
535 : : data (might have to convert the Chdr to native format). */
536 : 2024 : scn->data_read = 1;
537 : 2024 : scn->flags |= ELF_F_FILEDATA;
538 : 2024 : __libelf_set_data_list_rdlock (scn, 1);
539 : 2024 : }
540 : :
541 : : int
542 : 400678 : elf_compress (Elf_Scn *scn, int type, unsigned int flags)
543 : : {
544 [ + - ]: 400678 : if (scn == NULL)
545 : : return -1;
546 : :
547 [ - + ]: 400678 : if ((flags & ~ELF_CHF_FORCE) != 0)
548 : : {
549 : 0 : __libelf_seterrno (ELF_E_INVALID_OPERAND);
550 : 0 : return -1;
551 : : }
552 : :
553 : 400678 : bool force = (flags & ELF_CHF_FORCE) != 0;
554 : :
555 : 400678 : Elf *elf = scn->elf;
556 : 400678 : GElf_Ehdr ehdr;
557 [ + - ]: 400678 : if (gelf_getehdr (elf, &ehdr) == NULL)
558 : : return -1;
559 : :
560 : 400678 : int elfclass = elf->class;
561 : 400678 : int elfdata = ehdr.e_ident[EI_DATA];
562 : :
563 : 400678 : Elf64_Xword sh_flags;
564 : 400678 : Elf64_Word sh_type;
565 : 400678 : Elf64_Xword sh_addralign;
566 [ + + ]: 400678 : if (elfclass == ELFCLASS32)
567 : : {
568 : 396637 : Elf32_Shdr *shdr = elf32_getshdr (scn);
569 [ + - ]: 396637 : if (shdr == NULL)
570 : : return -1;
571 : :
572 : 396637 : sh_flags = shdr->sh_flags;
573 : 396637 : sh_type = shdr->sh_type;
574 : 396637 : sh_addralign = shdr->sh_addralign;
575 : : }
576 : : else
577 : : {
578 : 4041 : Elf64_Shdr *shdr = elf64_getshdr (scn);
579 [ + - ]: 4041 : if (shdr == NULL)
580 : : return -1;
581 : :
582 : 4041 : sh_flags = shdr->sh_flags;
583 : 4041 : sh_type = shdr->sh_type;
584 : 4041 : sh_addralign = shdr->sh_addralign;
585 : : }
586 : :
587 [ + + ]: 400678 : if ((sh_flags & SHF_ALLOC) != 0)
588 : : {
589 : 156979 : __libelf_seterrno (ELF_E_INVALID_SECTION_FLAGS);
590 : 156979 : return -1;
591 : : }
592 : :
593 [ + + ]: 243699 : if (sh_type == SHT_NULL || sh_type == SHT_NOBITS)
594 : : {
595 : 65582 : __libelf_seterrno (ELF_E_INVALID_SECTION_TYPE);
596 : 65582 : return -1;
597 : : }
598 : :
599 : 178117 : int compressed = (sh_flags & SHF_COMPRESSED);
600 [ + + ]: 178117 : if (type == ELFCOMPRESS_ZLIB || type == ELFCOMPRESS_ZSTD)
601 : : {
602 : : /* Compress/Deflate. */
603 : 530 : if (compressed == 1)
604 : : {
605 : : __libelf_seterrno (ELF_E_ALREADY_COMPRESSED);
606 : : return -1;
607 : : }
608 : :
609 : 1060 : size_t hsize = (elfclass == ELFCLASS32
610 [ + + ]: 530 : ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
611 : 530 : size_t orig_size, orig_addralign, new_size;
612 : 530 : void *out_buf = __libelf_compress (scn, hsize, elfdata,
613 : : &orig_size, &orig_addralign,
614 : : &new_size, force,
615 : : type == ELFCOMPRESS_ZSTD);
616 : :
617 : : /* Compression would make section larger, don't change anything. */
618 [ + + ]: 530 : if (out_buf == (void *) -1)
619 : : return 0;
620 : :
621 : : /* Compression failed, return error. */
622 [ + - ]: 437 : if (out_buf == NULL)
623 : : return -1;
624 : :
625 : : /* Put the header in front of the data. */
626 [ + + ]: 437 : if (elfclass == ELFCLASS32)
627 : : {
628 : 169 : Elf32_Chdr chdr;
629 : 169 : chdr.ch_type = type;
630 : 169 : chdr.ch_size = orig_size;
631 : 169 : chdr.ch_addralign = orig_addralign;
632 [ + + ]: 169 : if (elfdata != MY_ELFDATA)
633 : : {
634 : 96 : CONVERT (chdr.ch_type);
635 : 96 : CONVERT (chdr.ch_size);
636 : 96 : CONVERT (chdr.ch_addralign);
637 : : }
638 : 169 : memcpy (out_buf, &chdr, sizeof (Elf32_Chdr));
639 : : }
640 : : else
641 : : {
642 : 268 : Elf64_Chdr chdr;
643 : 268 : chdr.ch_type = type;
644 : 268 : chdr.ch_reserved = 0;
645 : 268 : chdr.ch_size = orig_size;
646 : 268 : chdr.ch_addralign = sh_addralign;
647 [ + + ]: 268 : if (elfdata != MY_ELFDATA)
648 : : {
649 : 93 : CONVERT (chdr.ch_type);
650 : 93 : CONVERT (chdr.ch_reserved);
651 : 93 : CONVERT (chdr.ch_size);
652 : 93 : CONVERT (chdr.ch_addralign);
653 : : }
654 : 268 : memcpy (out_buf, &chdr, sizeof (Elf64_Chdr));
655 : : }
656 : :
657 : : /* Note we keep the sh_entsize as is, we assume it is setup
658 : : correctly and ignored when SHF_COMPRESSED is set. */
659 [ + + ]: 437 : if (elfclass == ELFCLASS32)
660 : : {
661 : 169 : Elf32_Shdr *shdr = elf32_getshdr (scn);
662 : 169 : shdr->sh_size = new_size;
663 : 169 : shdr->sh_addralign = __libelf_type_align (ELFCLASS32, ELF_T_CHDR);
664 : 169 : shdr->sh_flags |= SHF_COMPRESSED;
665 : : }
666 : : else
667 : : {
668 : 268 : Elf64_Shdr *shdr = elf64_getshdr (scn);
669 : 268 : shdr->sh_size = new_size;
670 : 268 : shdr->sh_addralign = __libelf_type_align (ELFCLASS64, ELF_T_CHDR);
671 : 268 : shdr->sh_flags |= SHF_COMPRESSED;
672 : : }
673 : :
674 : 437 : __libelf_reset_rawdata (scn, out_buf, new_size, 1, ELF_T_CHDR);
675 : :
676 : : /* The section is now compressed, we could keep the uncompressed
677 : : data around, but since that might have been multiple Elf_Data
678 : : buffers let the user uncompress it explicitly again if they
679 : : want it to simplify bookkeeping. */
680 : 437 : scn->zdata_base = NULL;
681 : :
682 : 437 : return 1;
683 : : }
684 [ + - ]: 177587 : else if (type == 0)
685 : : {
686 : : /* Decompress/Inflate. */
687 [ + + ]: 177587 : if (compressed == 0)
688 : : {
689 : 176648 : __libelf_seterrno (ELF_E_NOT_COMPRESSED);
690 : 176648 : return -1;
691 : : }
692 : :
693 : : /* If the data is already decompressed (by elf_strptr), then we
694 : : only need to setup the rawdata and section header. XXX what
695 : : about elf_newdata? */
696 [ + + ]: 939 : if (scn->zdata_base == NULL)
697 : : {
698 : 937 : size_t size_out, addralign;
699 : 937 : void *buf_out = __libelf_decompress_elf (scn, &size_out, &addralign);
700 [ - + ]: 937 : if (buf_out == NULL)
701 : 0 : return -1;
702 : :
703 : 937 : scn->zdata_base = buf_out;
704 : 937 : scn->zdata_size = size_out;
705 : 937 : scn->zdata_align = addralign;
706 : : }
707 : :
708 : : /* Note we keep the sh_entsize as is, we assume it is setup
709 : : correctly and ignored when SHF_COMPRESSED is set. */
710 [ + + ]: 939 : if (elfclass == ELFCLASS32)
711 : : {
712 : 246 : Elf32_Shdr *shdr = elf32_getshdr (scn);
713 : 246 : shdr->sh_size = scn->zdata_size;
714 : 246 : shdr->sh_addralign = scn->zdata_align;
715 : 246 : shdr->sh_flags &= ~SHF_COMPRESSED;
716 : : }
717 : : else
718 : : {
719 : 693 : Elf64_Shdr *shdr = elf64_getshdr (scn);
720 : 693 : shdr->sh_size = scn->zdata_size;
721 : 693 : shdr->sh_addralign = scn->zdata_align;
722 : 693 : shdr->sh_flags &= ~SHF_COMPRESSED;
723 : : }
724 : :
725 : 939 : __libelf_reset_rawdata (scn, scn->zdata_base,
726 : : scn->zdata_size, scn->zdata_align,
727 : : __libelf_data_type (&ehdr, sh_type,
728 : : scn->zdata_align));
729 : :
730 : 939 : return 1;
731 : : }
732 : : else
733 : : {
734 : 0 : __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
735 : 0 : return -1;
736 : : }
737 : : }
|