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 : 1400 : __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 : 1400 : size_t used = hsize;
72 : :
73 : 1400 : z_stream z;
74 : 1400 : z.zalloc = Z_NULL;
75 : 1400 : z.zfree = Z_NULL;
76 : 1400 : z.opaque = Z_NULL;
77 : 1400 : int zrc = deflateInit (&z, Z_BEST_COMPRESSION);
78 [ - + ]: 1400 : if (zrc != Z_OK)
79 : : {
80 : 0 : __libelf_seterrno (ELF_E_COMPRESS_ERROR);
81 : 0 : return deflate_cleanup(NULL, NULL);
82 : : }
83 : :
84 : 1400 : Elf_Data cdata;
85 : 1400 : cdata.d_buf = NULL;
86 : :
87 : : /* Loop over data buffers. */
88 : 1400 : int flush = Z_NO_FLUSH;
89 : 1416 : do
90 : : {
91 : : /* Convert to raw if different endianness. */
92 : 1416 : cdata = *data;
93 [ + + + - ]: 1416 : bool convert = ei_data != MY_ELFDATA && data->d_size > 0;
94 : 596 : if (convert)
95 : : {
96 : : /* Don't do this conversion in place, we might want to keep
97 : : the original data around, caller decides. */
98 : 596 : cdata.d_buf = malloc (data->d_size);
99 [ - + ]: 596 : if (cdata.d_buf == NULL)
100 : : {
101 : 0 : __libelf_seterrno (ELF_E_NOMEM);
102 : 0 : return deflate_cleanup (NULL, NULL);
103 : : }
104 [ - + ]: 596 : if (gelf_xlatetof (scn->elf, &cdata, data, ei_data) == NULL)
105 : 0 : return deflate_cleanup (NULL, &cdata);
106 : : }
107 : :
108 : 1416 : z.avail_in = cdata.d_size;
109 : 1416 : z.next_in = cdata.d_buf;
110 : :
111 : : /* Get next buffer to see if this is the last one. */
112 : 1416 : data = next_data;
113 [ + + ]: 1416 : if (data != NULL)
114 : : {
115 : 16 : *orig_addralign = MAX (*orig_addralign, data->d_align);
116 : 16 : *orig_size += data->d_size;
117 : 16 : next_data = elf_getdata (scn, data);
118 : : }
119 : : else
120 : 1416 : flush = Z_FINISH;
121 : :
122 : : /* Flush one data buffer. */
123 : 3370 : do
124 : : {
125 : 3370 : z.avail_out = out_size - used;
126 : 3370 : z.next_out = out_buf + used;
127 : 3370 : zrc = deflate (&z, flush);
128 [ - + ]: 3370 : 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 : 3370 : 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 [ + + + + ]: 3370 : if (!force && flush == Z_FINISH && used >= *orig_size)
139 [ + + ]: 206 : return deflate_cleanup ((void *) -1, convert ? &cdata : NULL);
140 : :
141 [ + + ]: 3238 : if (z.avail_out == 0)
142 : : {
143 : 1954 : void *bigger = realloc (out_buf, out_size + block);
144 [ - + ]: 1954 : 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 [ + + ]: 3238 : while (z.avail_out == 0); /* Need more output buffer. */
154 : :
155 [ + + ]: 1284 : if (convert)
156 : : {
157 : 538 : free (cdata.d_buf);
158 : 538 : cdata.d_buf = NULL;
159 : : }
160 : : }
161 [ + + ]: 1284 : while (flush != Z_FINISH); /* More data blocks. */
162 : :
163 [ - + ]: 1268 : if (zrc != Z_STREAM_END)
164 : : {
165 : 0 : __libelf_seterrno (ELF_E_COMPRESS_ERROR);
166 : 0 : return deflate_cleanup (NULL, NULL);
167 : : }
168 : :
169 : 1268 : deflateEnd (&z);
170 : 1268 : *new_size = used;
171 : 1268 : 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 : 1920 : __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 : 1920 : Elf_Data *data = elf_getdata (scn, NULL);
307 [ - + ]: 1920 : 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 : 1920 : Elf_Data *next_data = elf_getdata (scn, data);
316 [ + + ]: 1920 : if (next_data == NULL && !force
317 [ + + ]: 1428 : && data->d_size <= hsize + 5 + 6)
318 : : return (void *) -1;
319 : :
320 : 1864 : *orig_addralign = data->d_align;
321 : 1864 : *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 : 1864 : size_t block = (data->d_size / 8) + hsize;
327 : 1864 : size_t out_size = 2 * block;
328 : 1864 : void *out_buf = malloc (out_size);
329 [ - + ]: 1864 : if (out_buf == NULL)
330 : : {
331 : 0 : __libelf_seterrno (ELF_E_NOMEM);
332 : 0 : return NULL;
333 : : }
334 : :
335 [ + + ]: 1864 : 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 : 1400 : 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 : 2552 : __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 [ - + ]: 2552 : 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 : 2552 : void *buf_out = malloc (size_out ?: 1);
372 [ - + ]: 2552 : if (unlikely (buf_out == NULL))
373 : : {
374 : 0 : __libelf_seterrno (ELF_E_NOMEM);
375 : 0 : return NULL;
376 : : }
377 : :
378 : 2552 : 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 : 2552 : int zrc = inflateInit (&z);
386 [ + + + - ]: 5104 : while (z.avail_in > 0 && likely (zrc == Z_OK))
387 : : {
388 : 2552 : z.next_out = buf_out + (size_out - z.avail_out);
389 : 2552 : zrc = inflate (&z, Z_FINISH);
390 [ + - ]: 2552 : if (unlikely (zrc != Z_STREAM_END))
391 : : {
392 : : zrc = Z_DATA_ERROR;
393 : : break;
394 : : }
395 : 2552 : zrc = inflateReset (&z);
396 : : }
397 : :
398 [ + - - + ]: 2552 : 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 : 2552 : inflateEnd(&z);
406 : 2552 : 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 : 2732 : __libelf_decompress (int chtype, void *buf_in, size_t size_in, size_t size_out)
439 : : {
440 [ + + ]: 2732 : if (chtype == ELFCOMPRESS_ZLIB)
441 : 2552 : 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 : 2140 : __libelf_decompress_elf (Elf_Scn *scn, size_t *size_out, size_t *addralign)
456 : : {
457 : 2140 : GElf_Chdr chdr;
458 [ - + ]: 2140 : if (gelf_getchdr (scn, &chdr) == NULL)
459 : : return NULL;
460 : :
461 : 2140 : bool unknown_compression = false;
462 [ + + ]: 2140 : 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 [ - + ]: 2140 : 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 : 2140 : Elf_Data *data = elf_getdata (scn, NULL);
492 [ - + ]: 2140 : if (data == NULL)
493 : : return NULL;
494 : :
495 : 2140 : int elfclass = scn->elf->class;
496 : 4280 : size_t hsize = (elfclass == ELFCLASS32
497 [ + + ]: 2140 : ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
498 : 2140 : size_t size_in = data->d_size - hsize;
499 : 2140 : void *buf_in = data->d_buf + hsize;
500 : 2140 : void *buf_out
501 : 2140 : = __libelf_decompress (chdr.ch_type, buf_in, size_in, chdr.ch_size);
502 : :
503 : 2140 : *size_out = chdr.ch_size;
504 : 2140 : *addralign = chdr.ch_addralign;
505 : 2140 : return buf_out;
506 : : }
507 : :
508 : : /* Assumes buf is a malloced buffer. */
509 : : void
510 : : internal_function
511 : 4352 : __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 : 4352 : scn->rawdata.d.d_off = 0;
516 : 4352 : scn->rawdata.d.d_version = EV_CURRENT;
517 : 4352 : scn->rawdata.d.d_buf = buf;
518 : 4352 : scn->rawdata.d.d_size = size;
519 : 4352 : scn->rawdata.d.d_align = align;
520 : 4352 : scn->rawdata.d.d_type = type;
521 : :
522 : : /* Remove the old data. */
523 : 4352 : Elf_Data_List *runp = scn->data_list.next;
524 [ + + ]: 4368 : while (runp != NULL)
525 : : {
526 : 16 : Elf_Data_List *oldp = runp;
527 : 16 : runp = runp->next;
528 [ + - ]: 16 : if ((oldp->flags & ELF_F_MALLOCED) != 0)
529 : 16 : free (oldp);
530 : : }
531 : : /* Existing existing data is no longer valid. */
532 : 4352 : scn->data_list.next = NULL;
533 : 4352 : scn->data_list_rear = NULL;
534 [ + + ]: 4352 : if (scn->data_base != scn->rawdata_base)
535 : 1092 : free (scn->data_base);
536 : 4352 : scn->data_base = NULL;
537 [ + + ]: 4352 : if (scn->zdata_base != buf
538 [ + + ]: 2220 : && scn->zdata_base != scn->rawdata_base)
539 : : {
540 : 2192 : free (scn->zdata_base);
541 : 2192 : scn->zdata_base = NULL;
542 : : }
543 [ + + ]: 4352 : if (scn->elf->map_address == NULL
544 [ + - ]: 1544 : || scn->rawdata_base == scn->zdata_base
545 [ + + ]: 1544 : || (scn->flags & ELF_F_MALLOCED) != 0)
546 : : {
547 : 2812 : free (scn->rawdata_base);
548 : 2812 : scn->rawdata_base = NULL;
549 : 2812 : scn->zdata_base = NULL;
550 : : }
551 : :
552 : 4352 : scn->rawdata_base = buf;
553 : 4352 : scn->flags |= ELF_F_MALLOCED;
554 : :
555 : : /* Pretend we (tried to) read the data from the file and setup the
556 : : data (might have to convert the Chdr to native format). */
557 : 4352 : scn->data_read = 1;
558 : 4352 : scn->flags |= ELF_F_FILEDATA;
559 : 4352 : __libelf_set_data_list_rdlock (scn, 1);
560 : 4352 : }
561 : :
562 : : int
563 : 801610 : elf_compress (Elf_Scn *scn, int type, unsigned int flags)
564 : : {
565 [ - + ]: 801610 : if (scn == NULL)
566 : : return -1;
567 : :
568 [ - + ]: 801610 : if ((flags & ~ELF_CHF_FORCE) != 0)
569 : : {
570 : 0 : __libelf_seterrno (ELF_E_INVALID_OPERAND);
571 : 0 : return -1;
572 : : }
573 : :
574 : 801610 : bool force = (flags & ELF_CHF_FORCE) != 0;
575 : :
576 : 801610 : Elf *elf = scn->elf;
577 : 801610 : GElf_Ehdr ehdr;
578 [ - + ]: 801610 : if (gelf_getehdr (elf, &ehdr) == NULL)
579 : : return -1;
580 : :
581 : 801610 : int elfclass = elf->class;
582 : 801610 : int elfdata = ehdr.e_ident[EI_DATA];
583 : :
584 : 801610 : Elf64_Xword sh_flags;
585 : 801610 : Elf64_Word sh_type;
586 : 801610 : Elf64_Xword sh_addralign;
587 [ + + ]: 801610 : if (elfclass == ELFCLASS32)
588 : : {
589 : 793306 : Elf32_Shdr *shdr = elf32_getshdr (scn);
590 [ - + ]: 793306 : if (shdr == NULL)
591 : : return -1;
592 : :
593 : 793306 : sh_flags = shdr->sh_flags;
594 : 793306 : sh_type = shdr->sh_type;
595 : 793306 : sh_addralign = shdr->sh_addralign;
596 : : }
597 : : else
598 : : {
599 : 8304 : Elf64_Shdr *shdr = elf64_getshdr (scn);
600 [ - + ]: 8304 : if (shdr == NULL)
601 : : return -1;
602 : :
603 : 8304 : sh_flags = shdr->sh_flags;
604 : 8304 : sh_type = shdr->sh_type;
605 : 8304 : sh_addralign = shdr->sh_addralign;
606 : : }
607 : :
608 [ + + ]: 801610 : if ((sh_flags & SHF_ALLOC) != 0)
609 : : {
610 : 313912 : __libelf_seterrno (ELF_E_INVALID_SECTION_FLAGS);
611 : 313912 : return -1;
612 : : }
613 : :
614 [ + + ]: 487698 : if (sh_type == SHT_NULL || sh_type == SHT_NOBITS)
615 : : {
616 : 131156 : __libelf_seterrno (ELF_E_INVALID_SECTION_TYPE);
617 : 131156 : return -1;
618 : : }
619 : :
620 : 356542 : int compressed = (sh_flags & SHF_COMPRESSED);
621 [ + + ]: 356542 : if (type == ELFCOMPRESS_ZLIB || type == ELFCOMPRESS_ZSTD)
622 : : {
623 : : /* Compress/Deflate. */
624 : 1086 : if (compressed == 1)
625 : : {
626 : : __libelf_seterrno (ELF_E_ALREADY_COMPRESSED);
627 : : return -1;
628 : : }
629 : :
630 : 2172 : size_t hsize = (elfclass == ELFCLASS32
631 [ + + ]: 1086 : ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
632 : 1086 : size_t orig_size, orig_addralign, new_size;
633 : 1086 : void *out_buf = __libelf_compress (scn, hsize, elfdata,
634 : : &orig_size, &orig_addralign,
635 : : &new_size, force,
636 : : type == ELFCOMPRESS_ZSTD);
637 : :
638 : : /* Compression would make section larger, don't change anything. */
639 [ + + ]: 1086 : if (out_buf == (void *) -1)
640 : : return 0;
641 : :
642 : : /* Compression failed, return error. */
643 [ + - ]: 900 : if (out_buf == NULL)
644 : : return -1;
645 : :
646 : : /* Put the header in front of the data. */
647 [ + + ]: 900 : if (elfclass == ELFCLASS32)
648 : : {
649 : 342 : Elf32_Chdr chdr;
650 : 342 : chdr.ch_type = type;
651 : 342 : chdr.ch_size = orig_size;
652 : 342 : chdr.ch_addralign = orig_addralign;
653 [ + + ]: 342 : if (elfdata != MY_ELFDATA)
654 : : {
655 : 192 : CONVERT (chdr.ch_type);
656 : 192 : CONVERT (chdr.ch_size);
657 : 192 : CONVERT (chdr.ch_addralign);
658 : : }
659 : 342 : memcpy (out_buf, &chdr, sizeof (Elf32_Chdr));
660 : : }
661 : : else
662 : : {
663 : 558 : Elf64_Chdr chdr;
664 : 558 : chdr.ch_type = type;
665 : 558 : chdr.ch_reserved = 0;
666 : 558 : chdr.ch_size = orig_size;
667 : 558 : chdr.ch_addralign = sh_addralign;
668 [ + + ]: 558 : if (elfdata != MY_ELFDATA)
669 : : {
670 : 190 : CONVERT (chdr.ch_type);
671 : 190 : CONVERT (chdr.ch_reserved);
672 : 190 : CONVERT (chdr.ch_size);
673 : 190 : CONVERT (chdr.ch_addralign);
674 : : }
675 : 558 : memcpy (out_buf, &chdr, sizeof (Elf64_Chdr));
676 : : }
677 : :
678 : : /* Note we keep the sh_entsize as is, we assume it is setup
679 : : correctly and ignored when SHF_COMPRESSED is set. */
680 [ + + ]: 900 : if (elfclass == ELFCLASS32)
681 : : {
682 : 342 : Elf32_Shdr *shdr = elf32_getshdr (scn);
683 : 342 : shdr->sh_size = new_size;
684 : 342 : shdr->sh_addralign = __libelf_type_align (ELFCLASS32, ELF_T_CHDR);
685 : 342 : shdr->sh_flags |= SHF_COMPRESSED;
686 : : }
687 : : else
688 : : {
689 : 558 : Elf64_Shdr *shdr = elf64_getshdr (scn);
690 : 558 : shdr->sh_size = new_size;
691 : 558 : shdr->sh_addralign = __libelf_type_align (ELFCLASS64, ELF_T_CHDR);
692 : 558 : shdr->sh_flags |= SHF_COMPRESSED;
693 : : }
694 : :
695 : 900 : __libelf_reset_rawdata (scn, out_buf, new_size, 1, ELF_T_CHDR);
696 : :
697 : : /* The section is now compressed, we could keep the uncompressed
698 : : data around, but since that might have been multiple Elf_Data
699 : : buffers let the user uncompress it explicitly again if they
700 : : want it to simplify bookkeeping. */
701 : 900 : free (scn->zdata_base);
702 : 900 : scn->zdata_base = NULL;
703 : :
704 : 900 : return 1;
705 : : }
706 [ + - ]: 355456 : else if (type == 0)
707 : : {
708 : : /* Decompress/Inflate. */
709 [ + + ]: 355456 : if (compressed == 0)
710 : : {
711 : 353324 : __libelf_seterrno (ELF_E_NOT_COMPRESSED);
712 : 353324 : return -1;
713 : : }
714 : :
715 : : /* If the data is already decompressed (by elf_strptr), then we
716 : : only need to setup the rawdata and section header. XXX what
717 : : about elf_newdata? */
718 [ + + ]: 2132 : if (scn->zdata_base == NULL)
719 : : {
720 : 2128 : size_t size_out, addralign;
721 : 2128 : void *buf_out = __libelf_decompress_elf (scn, &size_out, &addralign);
722 [ - + ]: 2128 : if (buf_out == NULL)
723 : 0 : return -1;
724 : :
725 : 2128 : scn->zdata_base = buf_out;
726 : 2128 : scn->zdata_size = size_out;
727 : 2128 : scn->zdata_align = addralign;
728 : : }
729 : :
730 : : /* Note we keep the sh_entsize as is, we assume it is setup
731 : : correctly and ignored when SHF_COMPRESSED is set. */
732 [ + + ]: 2132 : if (elfclass == ELFCLASS32)
733 : : {
734 : 520 : Elf32_Shdr *shdr = elf32_getshdr (scn);
735 : 520 : shdr->sh_size = scn->zdata_size;
736 : 520 : shdr->sh_addralign = scn->zdata_align;
737 : 520 : shdr->sh_flags &= ~SHF_COMPRESSED;
738 : : }
739 : : else
740 : : {
741 : 1612 : Elf64_Shdr *shdr = elf64_getshdr (scn);
742 : 1612 : shdr->sh_size = scn->zdata_size;
743 : 1612 : shdr->sh_addralign = scn->zdata_align;
744 : 1612 : shdr->sh_flags &= ~SHF_COMPRESSED;
745 : : }
746 : :
747 : 2132 : __libelf_reset_rawdata (scn, scn->zdata_base,
748 : : scn->zdata_size, scn->zdata_align,
749 : : __libelf_data_type (&ehdr, sh_type,
750 : : scn->zdata_align));
751 : :
752 : 2132 : return 1;
753 : : }
754 : : else
755 : : {
756 : 0 : __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
757 : 0 : return -1;
758 : : }
759 : : }
|