/src/elfutils/libelf/elf_compress.c
Line | Count | Source (jump to first uncovered line) |
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 | | do_deflate_cleanup (void *result, z_stream *z, void *out_buf, |
50 | | Elf_Data *cdatap) |
51 | 9.20k | { |
52 | 9.20k | deflateEnd (z); |
53 | 9.20k | free (out_buf); |
54 | 9.20k | if (cdatap != NULL) |
55 | 7.14k | free (cdatap->d_buf); |
56 | 9.20k | return result; |
57 | 9.20k | } |
58 | | |
59 | | #define deflate_cleanup(result, cdata) \ |
60 | 9.20k | do_deflate_cleanup(result, &z, out_buf, cdata) |
61 | | |
62 | | static |
63 | | void * |
64 | | __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 | 24.1k | { |
70 | | /* Caller gets to fill in the header at the start. Just skip it here. */ |
71 | 24.1k | size_t used = hsize; |
72 | | |
73 | 24.1k | z_stream z; |
74 | 24.1k | z.zalloc = Z_NULL; |
75 | 24.1k | z.zfree = Z_NULL; |
76 | 24.1k | z.opaque = Z_NULL; |
77 | 24.1k | int zrc = deflateInit (&z, Z_BEST_COMPRESSION); |
78 | 24.1k | if (zrc != Z_OK) |
79 | 0 | { |
80 | 0 | __libelf_seterrno (ELF_E_COMPRESS_ERROR); |
81 | 0 | return deflate_cleanup(NULL, NULL); |
82 | 0 | } |
83 | | |
84 | 24.1k | Elf_Data cdata; |
85 | 24.1k | cdata.d_buf = NULL; |
86 | | |
87 | | /* Loop over data buffers. */ |
88 | 24.1k | int flush = Z_NO_FLUSH; |
89 | 24.1k | do |
90 | 24.1k | { |
91 | | /* Convert to raw if different endianness. */ |
92 | 24.1k | cdata = *data; |
93 | 24.1k | bool convert = ei_data != MY_ELFDATA && data->d_size > 0; |
94 | 24.1k | if (convert) |
95 | 11.6k | { |
96 | | /* Don't do this conversion in place, we might want to keep |
97 | | the original data around, caller decides. */ |
98 | 11.6k | cdata.d_buf = malloc (data->d_size); |
99 | 11.6k | if (cdata.d_buf == NULL) |
100 | 0 | { |
101 | 0 | __libelf_seterrno (ELF_E_NOMEM); |
102 | 0 | return deflate_cleanup (NULL, NULL); |
103 | 0 | } |
104 | 11.6k | if (gelf_xlatetof (scn->elf, &cdata, data, ei_data) == NULL) |
105 | 6.50k | return deflate_cleanup (NULL, &cdata); |
106 | 11.6k | } |
107 | | |
108 | 17.6k | z.avail_in = cdata.d_size; |
109 | 17.6k | z.next_in = cdata.d_buf; |
110 | | |
111 | | /* Get next buffer to see if this is the last one. */ |
112 | 17.6k | data = next_data; |
113 | 17.6k | if (data != NULL) |
114 | 0 | { |
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 | 0 | } |
119 | 17.6k | else |
120 | 17.6k | flush = Z_FINISH; |
121 | | |
122 | | /* Flush one data buffer. */ |
123 | 17.6k | do |
124 | 50.5k | { |
125 | 50.5k | z.avail_out = out_size - used; |
126 | 50.5k | z.next_out = out_buf + used; |
127 | 50.5k | zrc = deflate (&z, flush); |
128 | 50.5k | if (zrc == Z_STREAM_ERROR) |
129 | 0 | { |
130 | 0 | __libelf_seterrno (ELF_E_COMPRESS_ERROR); |
131 | 0 | return deflate_cleanup (NULL, convert ? &cdata : NULL); |
132 | 0 | } |
133 | 50.5k | 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 | 50.5k | if (!force && flush == Z_FINISH && used >= *orig_size) |
139 | 2.69k | return deflate_cleanup ((void *) -1, convert ? &cdata : NULL); |
140 | | |
141 | 47.8k | if (z.avail_out == 0) |
142 | 32.8k | { |
143 | 32.8k | void *bigger = realloc (out_buf, out_size + block); |
144 | 32.8k | if (bigger == NULL) |
145 | 0 | { |
146 | 0 | __libelf_seterrno (ELF_E_NOMEM); |
147 | 0 | return deflate_cleanup (NULL, convert ? &cdata : NULL); |
148 | 0 | } |
149 | 32.8k | out_buf = bigger; |
150 | 32.8k | out_size += block; |
151 | 32.8k | } |
152 | 47.8k | } |
153 | 47.8k | while (z.avail_out == 0); /* Need more output buffer. */ |
154 | | |
155 | 14.9k | if (convert) |
156 | 4.51k | { |
157 | 4.51k | free (cdata.d_buf); |
158 | 4.51k | cdata.d_buf = NULL; |
159 | 4.51k | } |
160 | 14.9k | } |
161 | 24.1k | while (flush != Z_FINISH); /* More data blocks. */ |
162 | | |
163 | 14.9k | if (zrc != Z_STREAM_END) |
164 | 0 | { |
165 | 0 | __libelf_seterrno (ELF_E_COMPRESS_ERROR); |
166 | 0 | return deflate_cleanup (NULL, NULL); |
167 | 0 | } |
168 | | |
169 | 14.9k | deflateEnd (&z); |
170 | 14.9k | *new_size = used; |
171 | 14.9k | return out_buf; |
172 | 14.9k | } |
173 | | |
174 | | #ifdef USE_ZSTD_COMPRESS |
175 | | /* Cleanup and return result. Don't leak memory. */ |
176 | | static void * |
177 | | do_zstd_cleanup (void *result, ZSTD_CCtx * const cctx, void *out_buf, |
178 | | Elf_Data *cdatap) |
179 | | { |
180 | | ZSTD_freeCCtx (cctx); |
181 | | free (out_buf); |
182 | | if (cdatap != NULL) |
183 | | free (cdatap->d_buf); |
184 | | 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 | | __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 | | size_t used = hsize; |
200 | | |
201 | | ZSTD_CCtx* const cctx = ZSTD_createCCtx(); |
202 | | Elf_Data cdata; |
203 | | cdata.d_buf = NULL; |
204 | | |
205 | | /* Loop over data buffers. */ |
206 | | ZSTD_EndDirective mode = ZSTD_e_continue; |
207 | | |
208 | | do |
209 | | { |
210 | | /* Convert to raw if different endianness. */ |
211 | | cdata = *data; |
212 | | bool convert = ei_data != MY_ELFDATA && data->d_size > 0; |
213 | | if (convert) |
214 | | { |
215 | | /* Don't do this conversion in place, we might want to keep |
216 | | the original data around, caller decides. */ |
217 | | cdata.d_buf = malloc (data->d_size); |
218 | | if (cdata.d_buf == NULL) |
219 | | { |
220 | | __libelf_seterrno (ELF_E_NOMEM); |
221 | | return zstd_cleanup (NULL, NULL); |
222 | | } |
223 | | if (gelf_xlatetof (scn->elf, &cdata, data, ei_data) == NULL) |
224 | | return zstd_cleanup (NULL, &cdata); |
225 | | } |
226 | | |
227 | | ZSTD_inBuffer ib = { cdata.d_buf, cdata.d_size, 0 }; |
228 | | |
229 | | /* Get next buffer to see if this is the last one. */ |
230 | | data = next_data; |
231 | | if (data != NULL) |
232 | | { |
233 | | *orig_addralign = MAX (*orig_addralign, data->d_align); |
234 | | *orig_size += data->d_size; |
235 | | next_data = elf_getdata (scn, data); |
236 | | } |
237 | | else |
238 | | mode = ZSTD_e_end; |
239 | | |
240 | | /* Flush one data buffer. */ |
241 | | for (;;) |
242 | | { |
243 | | ZSTD_outBuffer ob = { out_buf + used, out_size - used, 0 }; |
244 | | size_t ret = ZSTD_compressStream2 (cctx, &ob, &ib, mode); |
245 | | if (ZSTD_isError (ret)) |
246 | | { |
247 | | __libelf_seterrno (ELF_E_COMPRESS_ERROR); |
248 | | return zstd_cleanup (NULL, convert ? &cdata : NULL); |
249 | | } |
250 | | 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 | | if (!force && mode == ZSTD_e_end && used >= *orig_size) |
256 | | return zstd_cleanup ((void *) -1, convert ? &cdata : NULL); |
257 | | |
258 | | if (ret > 0) |
259 | | { |
260 | | void *bigger = realloc (out_buf, out_size + block); |
261 | | if (bigger == NULL) |
262 | | { |
263 | | __libelf_seterrno (ELF_E_NOMEM); |
264 | | return zstd_cleanup (NULL, convert ? &cdata : NULL); |
265 | | } |
266 | | out_buf = bigger; |
267 | | out_size += block; |
268 | | } |
269 | | else |
270 | | break; |
271 | | } |
272 | | |
273 | | if (convert) |
274 | | { |
275 | | free (cdata.d_buf); |
276 | | cdata.d_buf = NULL; |
277 | | } |
278 | | } |
279 | | while (mode != ZSTD_e_end); /* More data blocks. */ |
280 | | |
281 | | ZSTD_freeCCtx (cctx); |
282 | | *new_size = used; |
283 | | 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 | | __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 | 222k | { |
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 | 222k | Elf_Data *data = elf_getdata (scn, NULL); |
307 | 222k | if (data == NULL) |
308 | 174k | 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 | 47.8k | Elf_Data *next_data = elf_getdata (scn, data); |
316 | 47.8k | if (next_data == NULL && !force |
317 | 47.8k | && data->d_size <= hsize + 5 + 6) |
318 | 23.6k | return (void *) -1; |
319 | | |
320 | 24.1k | *orig_addralign = data->d_align; |
321 | 24.1k | *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 | 24.1k | size_t block = (data->d_size / 8) + hsize; |
327 | 24.1k | size_t out_size = 2 * block; |
328 | 24.1k | void *out_buf = malloc (out_size); |
329 | 24.1k | if (out_buf == NULL) |
330 | 0 | { |
331 | 0 | __libelf_seterrno (ELF_E_NOMEM); |
332 | 0 | return NULL; |
333 | 0 | } |
334 | | |
335 | 24.1k | if (use_zstd) |
336 | 0 | { |
337 | | #ifdef USE_ZSTD_COMPRESS |
338 | | 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 | 0 | __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE); |
344 | 0 | return NULL; |
345 | 0 | #endif |
346 | 0 | } |
347 | 24.1k | else |
348 | 24.1k | return __libelf_compress_zlib (scn, hsize, ei_data, orig_size, |
349 | 24.1k | orig_addralign, new_size, force, |
350 | 24.1k | data, next_data, out_buf, out_size, |
351 | 24.1k | block); |
352 | 24.1k | } |
353 | | |
354 | | void * |
355 | | internal_function |
356 | | __libelf_decompress_zlib (void *buf_in, size_t size_in, size_t size_out) |
357 | 634k | { |
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 | 634k | if (unlikely (size_out / 1032 > size_in)) |
362 | 1.98k | { |
363 | 1.98k | __libelf_seterrno (ELF_E_INVALID_DATA); |
364 | 1.98k | return NULL; |
365 | 1.98k | } |
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 | 632k | void *buf_out = malloc (size_out ?: 1); |
372 | 632k | if (unlikely (buf_out == NULL)) |
373 | 0 | { |
374 | 0 | __libelf_seterrno (ELF_E_NOMEM); |
375 | 0 | return NULL; |
376 | 0 | } |
377 | | |
378 | 31.8k | z_stream z = |
379 | 31.8k | { |
380 | 31.8k | .next_in = buf_in, |
381 | 31.8k | .avail_in = size_in, |
382 | 31.8k | .next_out = buf_out, |
383 | 31.8k | .avail_out = size_out |
384 | 31.8k | }; |
385 | 31.8k | int zrc = inflateInit (&z); |
386 | 630k | while (z.avail_in > 0 && likely (zrc == Z_OK)) |
387 | 630k | { |
388 | 630k | z.next_out = buf_out + (size_out - z.avail_out); |
389 | 630k | zrc = inflate (&z, Z_FINISH); |
390 | 630k | if (unlikely (zrc != Z_STREAM_END)) |
391 | 628k | { |
392 | 628k | zrc = Z_DATA_ERROR; |
393 | 628k | break; |
394 | 628k | } |
395 | 1.42k | zrc = inflateReset (&z); |
396 | 1.42k | } |
397 | | |
398 | 31.8k | if (unlikely (zrc != Z_OK) || unlikely (z.avail_out != 0)) |
399 | 629k | { |
400 | 629k | free (buf_out); |
401 | 629k | buf_out = NULL; |
402 | 629k | __libelf_seterrno (ELF_E_DECOMPRESS_ERROR); |
403 | 629k | } |
404 | | |
405 | 31.8k | inflateEnd(&z); |
406 | 31.8k | return buf_out; |
407 | 31.8k | } |
408 | | |
409 | | #ifdef USE_ZSTD |
410 | | static void * |
411 | | __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 | | void *buf_out = malloc (size_out ?: 1); |
418 | | if (unlikely (buf_out == NULL)) |
419 | | { |
420 | | __libelf_seterrno (ELF_E_NOMEM); |
421 | | return NULL; |
422 | | } |
423 | | |
424 | | size_t ret = ZSTD_decompress (buf_out, size_out, buf_in, size_in); |
425 | | if (unlikely (ZSTD_isError (ret)) || unlikely (ret != size_out)) |
426 | | { |
427 | | free (buf_out); |
428 | | __libelf_seterrno (ELF_E_DECOMPRESS_ERROR); |
429 | | return NULL; |
430 | | } |
431 | | else |
432 | | return buf_out; |
433 | | } |
434 | | #endif |
435 | | |
436 | | void * |
437 | | internal_function |
438 | | __libelf_decompress (int chtype, void *buf_in, size_t size_in, size_t size_out) |
439 | 634k | { |
440 | 634k | if (chtype == ELFCOMPRESS_ZLIB) |
441 | 634k | return __libelf_decompress_zlib (buf_in, size_in, size_out); |
442 | 0 | else |
443 | 0 | { |
444 | | #ifdef USE_ZSTD |
445 | | return __libelf_decompress_zstd (buf_in, size_in, size_out); |
446 | | #else |
447 | 0 | __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE); |
448 | 0 | return NULL; |
449 | 0 | #endif |
450 | 0 | } |
451 | 634k | } |
452 | | |
453 | | void * |
454 | | internal_function |
455 | | __libelf_decompress_elf (Elf_Scn *scn, size_t *size_out, size_t *addralign) |
456 | 1.17M | { |
457 | 1.17M | GElf_Chdr chdr; |
458 | 1.17M | if (gelf_getchdr (scn, &chdr) == NULL) |
459 | 322k | return NULL; |
460 | | |
461 | 855k | bool unknown_compression = false; |
462 | 855k | if (chdr.ch_type != ELFCOMPRESS_ZLIB) |
463 | 218k | { |
464 | 218k | if (chdr.ch_type != ELFCOMPRESS_ZSTD) |
465 | 218k | unknown_compression = true; |
466 | | |
467 | 218k | #ifndef USE_ZSTD |
468 | 218k | if (chdr.ch_type == ELFCOMPRESS_ZSTD) |
469 | 406 | unknown_compression = true; |
470 | 218k | #endif |
471 | 218k | } |
472 | | |
473 | 855k | if (unknown_compression) |
474 | 218k | { |
475 | 218k | __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE); |
476 | 218k | return NULL; |
477 | 218k | } |
478 | | |
479 | 636k | if (! powerof2 (chdr.ch_addralign)) |
480 | 4.86k | { |
481 | 4.86k | __libelf_seterrno (ELF_E_INVALID_ALIGN); |
482 | 4.86k | return NULL; |
483 | 4.86k | } |
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 | 631k | Elf_Data *data = elf_getdata (scn, NULL); |
492 | 631k | if (data == NULL) |
493 | 0 | return NULL; |
494 | | |
495 | 631k | int elfclass = scn->elf->class; |
496 | 631k | size_t hsize = (elfclass == ELFCLASS32 |
497 | 631k | ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr)); |
498 | 631k | size_t size_in = data->d_size - hsize; |
499 | 631k | void *buf_in = data->d_buf + hsize; |
500 | 631k | void *buf_out |
501 | 631k | = __libelf_decompress (chdr.ch_type, buf_in, size_in, chdr.ch_size); |
502 | | |
503 | 631k | *size_out = chdr.ch_size; |
504 | 631k | *addralign = chdr.ch_addralign; |
505 | 631k | return buf_out; |
506 | 631k | } |
507 | | |
508 | | /* Assumes buf is a malloced buffer. */ |
509 | | void |
510 | | internal_function |
511 | | __libelf_reset_rawdata (Elf_Scn *scn, void *buf, size_t size, size_t align, |
512 | | Elf_Type type) |
513 | 17.7k | { |
514 | | /* This is the new raw data, replace and possibly free old data. */ |
515 | 17.7k | scn->rawdata.d.d_off = 0; |
516 | 17.7k | scn->rawdata.d.d_version = EV_CURRENT; |
517 | 17.7k | scn->rawdata.d.d_buf = buf; |
518 | 17.7k | scn->rawdata.d.d_size = size; |
519 | 17.7k | scn->rawdata.d.d_align = align; |
520 | 17.7k | scn->rawdata.d.d_type = type; |
521 | | |
522 | | /* Remove the old data. */ |
523 | 17.7k | Elf_Data_List *runp = scn->data_list.next; |
524 | 17.7k | while (runp != NULL) |
525 | 0 | { |
526 | 0 | Elf_Data_List *oldp = runp; |
527 | 0 | runp = runp->next; |
528 | 0 | if ((oldp->flags & ELF_F_MALLOCED) != 0) |
529 | 0 | free (oldp); |
530 | 0 | } |
531 | | /* Existing existing data is no longer valid. */ |
532 | 17.7k | scn->data_list.next = NULL; |
533 | 17.7k | scn->data_list_rear = NULL; |
534 | 17.7k | if (scn->data_base != scn->rawdata_base) |
535 | 6.35k | free (scn->data_base); |
536 | 17.7k | scn->data_base = NULL; |
537 | 17.7k | if (scn->zdata_base != buf |
538 | 17.7k | && scn->zdata_base != scn->rawdata_base) |
539 | 16.0k | { |
540 | 16.0k | free (scn->zdata_base); |
541 | 16.0k | scn->zdata_base = NULL; |
542 | 16.0k | } |
543 | 17.7k | if (scn->elf->map_address == NULL |
544 | 17.7k | || scn->rawdata_base == scn->zdata_base |
545 | 17.7k | || (scn->flags & ELF_F_MALLOCED) != 0) |
546 | 16.0k | { |
547 | 16.0k | free (scn->rawdata_base); |
548 | 16.0k | scn->rawdata_base = NULL; |
549 | 16.0k | scn->zdata_base = NULL; |
550 | 16.0k | } |
551 | | |
552 | 17.7k | scn->rawdata_base = buf; |
553 | 17.7k | 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 | 17.7k | scn->data_read = 1; |
558 | 17.7k | scn->flags |= ELF_F_FILEDATA; |
559 | 17.7k | __libelf_set_data_list_rdlock (scn, 1); |
560 | 17.7k | } |
561 | | |
562 | | int |
563 | | elf_compress (Elf_Scn *scn, int type, unsigned int flags) |
564 | 929k | { |
565 | 929k | if (scn == NULL) |
566 | 0 | return -1; |
567 | | |
568 | 929k | if ((flags & ~ELF_CHF_FORCE) != 0) |
569 | 0 | { |
570 | 0 | __libelf_seterrno (ELF_E_INVALID_OPERAND); |
571 | 0 | return -1; |
572 | 0 | } |
573 | | |
574 | 929k | bool force = (flags & ELF_CHF_FORCE) != 0; |
575 | | |
576 | 929k | Elf *elf = scn->elf; |
577 | 929k | GElf_Ehdr ehdr; |
578 | 929k | if (gelf_getehdr (elf, &ehdr) == NULL) |
579 | 0 | return -1; |
580 | | |
581 | 929k | int elfclass = elf->class; |
582 | 929k | int elfdata = ehdr.e_ident[EI_DATA]; |
583 | | |
584 | 929k | Elf64_Xword sh_flags; |
585 | 929k | Elf64_Word sh_type; |
586 | 929k | Elf64_Xword sh_addralign; |
587 | 929k | union shdr |
588 | 929k | { |
589 | 929k | Elf32_Shdr *s32; |
590 | 929k | Elf64_Shdr *s64; |
591 | 929k | } shdr; |
592 | 929k | if (elfclass == ELFCLASS32) |
593 | 807k | { |
594 | 807k | shdr.s32 = elf32_getshdr (scn); |
595 | 807k | if (shdr.s32 == NULL) |
596 | 0 | return -1; |
597 | | |
598 | 807k | sh_flags = shdr.s32->sh_flags; |
599 | 807k | sh_type = shdr.s32->sh_type; |
600 | 807k | sh_addralign = shdr.s32->sh_addralign; |
601 | 807k | } |
602 | 121k | else |
603 | 121k | { |
604 | 121k | shdr.s64 = elf64_getshdr (scn); |
605 | 121k | if (shdr.s64 == NULL) |
606 | 0 | return -1; |
607 | | |
608 | 121k | sh_flags = shdr.s64->sh_flags; |
609 | 121k | sh_type = shdr.s64->sh_type; |
610 | 121k | sh_addralign = shdr.s64->sh_addralign; |
611 | 121k | } |
612 | | |
613 | 929k | if ((sh_flags & SHF_ALLOC) != 0) |
614 | 438k | { |
615 | 438k | __libelf_seterrno (ELF_E_INVALID_SECTION_FLAGS); |
616 | 438k | return -1; |
617 | 438k | } |
618 | | |
619 | 491k | if (sh_type == SHT_NULL || sh_type == SHT_NOBITS) |
620 | 41.5k | { |
621 | 41.5k | __libelf_seterrno (ELF_E_INVALID_SECTION_TYPE); |
622 | 41.5k | return -1; |
623 | 41.5k | } |
624 | | |
625 | 449k | int compressed = (sh_flags & SHF_COMPRESSED); |
626 | 449k | if (type == ELFCOMPRESS_ZLIB || type == ELFCOMPRESS_ZSTD) |
627 | 222k | { |
628 | | /* Compress/Deflate. */ |
629 | 222k | if (compressed == 1) |
630 | 0 | { |
631 | 0 | __libelf_seterrno (ELF_E_ALREADY_COMPRESSED); |
632 | 0 | return -1; |
633 | 0 | } |
634 | | |
635 | 222k | size_t hsize = (elfclass == ELFCLASS32 |
636 | 222k | ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr)); |
637 | 222k | size_t orig_size, orig_addralign, new_size; |
638 | 222k | void *out_buf = __libelf_compress (scn, hsize, elfdata, |
639 | 222k | &orig_size, &orig_addralign, |
640 | 222k | &new_size, force, |
641 | 222k | type == ELFCOMPRESS_ZSTD); |
642 | | |
643 | | /* Compression would make section larger, don't change anything. */ |
644 | 222k | if (out_buf == (void *) -1) |
645 | 26.3k | return 0; |
646 | | |
647 | | /* Compression failed, return error. */ |
648 | 196k | if (out_buf == NULL) |
649 | 181k | return -1; |
650 | | |
651 | | /* Put the header in front of the data. */ |
652 | 14.9k | if (elfclass == ELFCLASS32) |
653 | 14.4k | { |
654 | 14.4k | Elf32_Chdr chdr; |
655 | 14.4k | chdr.ch_type = type; |
656 | 14.4k | chdr.ch_size = orig_size; |
657 | 14.4k | chdr.ch_addralign = orig_addralign; |
658 | 14.4k | if (elfdata != MY_ELFDATA) |
659 | 4.31k | { |
660 | 4.31k | CONVERT (chdr.ch_type); |
661 | 4.31k | CONVERT (chdr.ch_size); |
662 | 4.31k | CONVERT (chdr.ch_addralign); |
663 | 4.31k | } |
664 | 14.4k | memcpy (out_buf, &chdr, sizeof (Elf32_Chdr)); |
665 | 14.4k | } |
666 | 548 | else |
667 | 548 | { |
668 | 548 | Elf64_Chdr chdr; |
669 | 548 | chdr.ch_type = type; |
670 | 548 | chdr.ch_reserved = 0; |
671 | 548 | chdr.ch_size = orig_size; |
672 | 548 | chdr.ch_addralign = sh_addralign; |
673 | 548 | if (elfdata != MY_ELFDATA) |
674 | 198 | { |
675 | 198 | CONVERT (chdr.ch_type); |
676 | 198 | CONVERT (chdr.ch_reserved); |
677 | 198 | CONVERT (chdr.ch_size); |
678 | 198 | CONVERT (chdr.ch_addralign); |
679 | 198 | } |
680 | 548 | memcpy (out_buf, &chdr, sizeof (Elf64_Chdr)); |
681 | 548 | } |
682 | | |
683 | | /* Note we keep the sh_entsize as is, we assume it is setup |
684 | | correctly and ignored when SHF_COMPRESSED is set. */ |
685 | 14.9k | if (elfclass == ELFCLASS32) |
686 | 14.4k | { |
687 | 14.4k | shdr.s32->sh_size = new_size; |
688 | 14.4k | shdr.s32->sh_addralign = __libelf_type_align (ELFCLASS32, |
689 | 14.4k | ELF_T_CHDR); |
690 | 14.4k | shdr.s32->sh_flags |= SHF_COMPRESSED; |
691 | 0 | } |
692 | 548 | else |
693 | 548 | { |
694 | 548 | shdr.s64->sh_size = new_size; |
695 | 548 | shdr.s64->sh_addralign = __libelf_type_align (ELFCLASS64, |
696 | 548 | ELF_T_CHDR); |
697 | 548 | shdr.s64->sh_flags |= SHF_COMPRESSED; |
698 | 0 | } |
699 | | |
700 | 14.9k | __libelf_reset_rawdata (scn, out_buf, new_size, 1, ELF_T_CHDR); |
701 | | |
702 | | /* The section is now compressed, we could keep the uncompressed |
703 | | data around, but since that might have been multiple Elf_Data |
704 | | buffers let the user uncompress it explicitly again if they |
705 | | want it to simplify bookkeeping. */ |
706 | 0 | free (scn->zdata_base); |
707 | 0 | scn->zdata_base = NULL; |
708 | |
|
709 | 0 | return 1; |
710 | 14.9k | } |
711 | 227k | else if (type == 0) |
712 | 227k | { |
713 | | /* Decompress/Inflate. */ |
714 | 227k | if (compressed == 0) |
715 | 0 | { |
716 | 0 | __libelf_seterrno (ELF_E_NOT_COMPRESSED); |
717 | 0 | return -1; |
718 | 0 | } |
719 | | |
720 | | /* If the data is already decompressed (by elf_strptr), then we |
721 | | only need to setup the rawdata and section header. XXX what |
722 | | about elf_newdata? */ |
723 | 227k | if (scn->zdata_base == NULL) |
724 | 227k | { |
725 | 227k | size_t size_out, addralign; |
726 | 227k | void *buf_out = __libelf_decompress_elf (scn, &size_out, &addralign); |
727 | 227k | if (buf_out == NULL) |
728 | 225k | return -1; |
729 | | |
730 | 1.67k | scn->zdata_base = buf_out; |
731 | 1.67k | scn->zdata_size = size_out; |
732 | 1.67k | scn->zdata_align = addralign; |
733 | 1.67k | } |
734 | | |
735 | | /* Note we keep the sh_entsize as is, we assume it is setup |
736 | | correctly and ignored when SHF_COMPRESSED is set. */ |
737 | 1.68k | if (elfclass == ELFCLASS32) |
738 | 1.64k | { |
739 | 1.64k | shdr.s32->sh_size = scn->zdata_size; |
740 | 1.64k | shdr.s32->sh_addralign = scn->zdata_align; |
741 | 1.64k | shdr.s32->sh_flags &= ~SHF_COMPRESSED; |
742 | 1.64k | } |
743 | 36 | else |
744 | 36 | { |
745 | 36 | shdr.s64->sh_size = scn->zdata_size; |
746 | 36 | shdr.s64->sh_addralign = scn->zdata_align; |
747 | 36 | shdr.s64->sh_flags &= ~SHF_COMPRESSED; |
748 | 36 | } |
749 | | |
750 | 1.68k | __libelf_reset_rawdata (scn, scn->zdata_base, |
751 | 1.68k | scn->zdata_size, scn->zdata_align, |
752 | 1.68k | __libelf_data_type (&ehdr, sh_type, |
753 | 1.68k | scn->zdata_align)); |
754 | | |
755 | 1.68k | return 1; |
756 | 227k | } |
757 | 0 | else |
758 | 0 | { |
759 | 0 | __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE); |
760 | 0 | return -1; |
761 | 0 | } |
762 | 449k | } |