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