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