xref: /aosp_15_r20/external/elfutils/libelf/elf_compress.c (revision 7304104da70ce23c86437a01be71edd1a2d7f37e)
1 /* Compress or decompress a section.
2    Copyright (C) 2015, 2016 Red Hat, Inc.
3    Copyright (C) 2023, Mark J. Wielaard <[email protected]>
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 *
do_deflate_cleanup(void * result,z_stream * z,void * out_buf,Elf_Data * cdatap)49 do_deflate_cleanup (void *result, z_stream *z, void *out_buf,
50                     Elf_Data *cdatap)
51 {
52   deflateEnd (z);
53   free (out_buf);
54   if (cdatap != NULL)
55     free (cdatap->d_buf);
56   return result;
57 }
58 
59 #define deflate_cleanup(result, cdata) \
60     do_deflate_cleanup(result, &z, out_buf, cdata)
61 
62 static
63 void *
__libelf_compress_zlib(Elf_Scn * scn,size_t hsize,int ei_data,size_t * orig_size,size_t * orig_addralign,size_t * new_size,bool force,Elf_Data * data,Elf_Data * next_data,void * out_buf,size_t out_size,size_t block)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 {
70   /* Caller gets to fill in the header at the start.  Just skip it here.  */
71   size_t used = hsize;
72 
73   z_stream z;
74   z.zalloc = Z_NULL;
75   z.zfree = Z_NULL;
76   z.opaque = Z_NULL;
77   int zrc = deflateInit (&z, Z_BEST_COMPRESSION);
78   if (zrc != Z_OK)
79     {
80       __libelf_seterrno (ELF_E_COMPRESS_ERROR);
81       return deflate_cleanup(NULL, NULL);
82     }
83 
84   Elf_Data cdata;
85   cdata.d_buf = NULL;
86 
87   /* Loop over data buffers.  */
88   int flush = Z_NO_FLUSH;
89   do
90     {
91       /* Convert to raw if different endianness.  */
92       cdata = *data;
93       bool convert = ei_data != MY_ELFDATA && data->d_size > 0;
94       if (convert)
95 	{
96 	  /* Don't do this conversion in place, we might want to keep
97 	     the original data around, caller decides.  */
98 	  cdata.d_buf = malloc (data->d_size);
99 	  if (cdata.d_buf == NULL)
100 	    {
101 	      __libelf_seterrno (ELF_E_NOMEM);
102 	      return deflate_cleanup (NULL, NULL);
103 	    }
104 	  if (gelf_xlatetof (scn->elf, &cdata, data, ei_data) == NULL)
105 	    return deflate_cleanup (NULL, &cdata);
106 	}
107 
108       z.avail_in = cdata.d_size;
109       z.next_in = cdata.d_buf;
110 
111       /* Get next buffer to see if this is the last one.  */
112       data = next_data;
113       if (data != NULL)
114 	{
115 	  *orig_addralign = MAX (*orig_addralign, data->d_align);
116 	  *orig_size += data->d_size;
117 	  next_data = elf_getdata (scn, data);
118 	}
119       else
120 	flush = Z_FINISH;
121 
122       /* Flush one data buffer.  */
123       do
124 	{
125 	  z.avail_out = out_size - used;
126 	  z.next_out = out_buf + used;
127 	  zrc = deflate (&z, flush);
128 	  if (zrc == Z_STREAM_ERROR)
129 	    {
130 	      __libelf_seterrno (ELF_E_COMPRESS_ERROR);
131 	      return deflate_cleanup (NULL, convert ? &cdata : NULL);
132 	    }
133 	  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 	  if (!force && flush == Z_FINISH && used >= *orig_size)
139 	    return deflate_cleanup ((void *) -1, convert ? &cdata : NULL);
140 
141 	  if (z.avail_out == 0)
142 	    {
143 	      void *bigger = realloc (out_buf, out_size + block);
144 	      if (bigger == NULL)
145 		{
146 		  __libelf_seterrno (ELF_E_NOMEM);
147 		  return deflate_cleanup (NULL, convert ? &cdata : NULL);
148 		}
149 	      out_buf = bigger;
150 	      out_size += block;
151 	    }
152 	}
153       while (z.avail_out == 0); /* Need more output buffer.  */
154 
155       if (convert)
156 	{
157 	  free (cdata.d_buf);
158 	  cdata.d_buf = NULL;
159 	}
160     }
161   while (flush != Z_FINISH); /* More data blocks.  */
162 
163   if (zrc != Z_STREAM_END)
164     {
165       __libelf_seterrno (ELF_E_COMPRESS_ERROR);
166       return deflate_cleanup (NULL, NULL);
167     }
168 
169   deflateEnd (&z);
170   *new_size = used;
171   return out_buf;
172 }
173 
174 #ifdef USE_ZSTD_COMPRESS
175 /* Cleanup and return result.  Don't leak memory.  */
176 static void *
do_zstd_cleanup(void * result,ZSTD_CCtx * const cctx,void * out_buf,Elf_Data * cdatap)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 *
__libelf_compress_zstd(Elf_Scn * scn,size_t hsize,int ei_data,size_t * orig_size,size_t * orig_addralign,size_t * new_size,bool force,Elf_Data * data,Elf_Data * next_data,void * out_buf,size_t out_size,size_t block)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
__libelf_compress(Elf_Scn * scn,size_t hsize,int ei_data,size_t * orig_size,size_t * orig_addralign,size_t * new_size,bool force,bool use_zstd)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 {
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   Elf_Data *data = elf_getdata (scn, NULL);
307   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   Elf_Data *next_data = elf_getdata (scn, data);
316   if (next_data == NULL && !force
317       && data->d_size <= hsize + 5 + 6)
318     return (void *) -1;
319 
320   *orig_addralign = data->d_align;
321   *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   size_t block = (data->d_size / 8) + hsize;
327   size_t out_size = 2 * block;
328   void *out_buf = malloc (out_size);
329   if (out_buf == NULL)
330     {
331       __libelf_seterrno (ELF_E_NOMEM);
332       return NULL;
333     }
334 
335   if (use_zstd)
336     {
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     __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
344     return NULL;
345 #endif
346     }
347   else
348     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
__libelf_decompress_zlib(void * buf_in,size_t size_in,size_t size_out)356 __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   if (unlikely (size_out / 1032 > size_in))
362     {
363       __libelf_seterrno (ELF_E_INVALID_DATA);
364       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   void *buf_out = malloc (size_out ?: 1);
372   if (unlikely (buf_out == NULL))
373     {
374       __libelf_seterrno (ELF_E_NOMEM);
375       return NULL;
376     }
377 
378   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   int zrc = inflateInit (&z);
386   while (z.avail_in > 0 && likely (zrc == Z_OK))
387     {
388       z.next_out = buf_out + (size_out - z.avail_out);
389       zrc = inflate (&z, Z_FINISH);
390       if (unlikely (zrc != Z_STREAM_END))
391 	{
392 	  zrc = Z_DATA_ERROR;
393 	  break;
394 	}
395       zrc = inflateReset (&z);
396     }
397 
398   if (unlikely (zrc != Z_OK) || unlikely (z.avail_out != 0))
399     {
400       free (buf_out);
401       buf_out = NULL;
402       __libelf_seterrno (ELF_E_DECOMPRESS_ERROR);
403     }
404 
405   inflateEnd(&z);
406   return buf_out;
407 }
408 
409 #ifdef USE_ZSTD
410 static void *
__libelf_decompress_zstd(void * buf_in,size_t size_in,size_t size_out)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
__libelf_decompress(int chtype,void * buf_in,size_t size_in,size_t size_out)438 __libelf_decompress (int chtype, void *buf_in, size_t size_in, size_t size_out)
439 {
440   if (chtype == ELFCOMPRESS_ZLIB)
441     return __libelf_decompress_zlib (buf_in, size_in, size_out);
442   else
443     {
444 #ifdef USE_ZSTD
445     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
__libelf_decompress_elf(Elf_Scn * scn,size_t * size_out,size_t * addralign)455 __libelf_decompress_elf (Elf_Scn *scn, size_t *size_out, size_t *addralign)
456 {
457   GElf_Chdr chdr;
458   if (gelf_getchdr (scn, &chdr) == NULL)
459     return NULL;
460 
461   bool unknown_compression = false;
462   if (chdr.ch_type != ELFCOMPRESS_ZLIB)
463     {
464       if (chdr.ch_type != ELFCOMPRESS_ZSTD)
465 	unknown_compression = true;
466 
467 #ifndef USE_ZSTD
468       if (chdr.ch_type == ELFCOMPRESS_ZSTD)
469 	unknown_compression = true;
470 #endif
471     }
472 
473   if (unknown_compression)
474     {
475       __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
476       return NULL;
477     }
478 
479   if (! powerof2 (chdr.ch_addralign))
480     {
481       __libelf_seterrno (ELF_E_INVALID_ALIGN);
482       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   Elf_Data *data = elf_getdata (scn, NULL);
492   if (data == NULL)
493     return NULL;
494 
495   int elfclass = scn->elf->class;
496   size_t hsize = (elfclass == ELFCLASS32
497 		  ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
498   size_t size_in = data->d_size - hsize;
499   void *buf_in = data->d_buf + hsize;
500   void *buf_out
501     = __libelf_decompress (chdr.ch_type, buf_in, size_in, chdr.ch_size);
502 
503   *size_out = chdr.ch_size;
504   *addralign = chdr.ch_addralign;
505   return buf_out;
506 }
507 
508 /* Assumes buf is a malloced buffer.  */
509 void
510 internal_function
__libelf_reset_rawdata(Elf_Scn * scn,void * buf,size_t size,size_t align,Elf_Type type)511 __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   scn->rawdata.d.d_off = 0;
516   scn->rawdata.d.d_version = EV_CURRENT;
517   scn->rawdata.d.d_buf = buf;
518   scn->rawdata.d.d_size = size;
519   scn->rawdata.d.d_align = align;
520   scn->rawdata.d.d_type = type;
521 
522   /* Existing existing data is no longer valid.  */
523   scn->data_list_rear = NULL;
524   if (scn->data_base != scn->rawdata_base)
525     free (scn->data_base);
526   scn->data_base = NULL;
527   if (scn->zdata_base != buf
528       && scn->zdata_base != scn->rawdata_base)
529     {
530       free (scn->zdata_base);
531       scn->zdata_base = NULL;
532     }
533   if (scn->elf->map_address == NULL
534       || scn->rawdata_base == scn->zdata_base
535       || (scn->flags & ELF_F_MALLOCED) != 0)
536     {
537       free (scn->rawdata_base);
538       scn->rawdata_base = NULL;
539       scn->zdata_base = NULL;
540     }
541 
542   scn->rawdata_base = buf;
543   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   scn->data_read = 1;
548   scn->flags |= ELF_F_FILEDATA;
549   __libelf_set_data_list_rdlock (scn, 1);
550 }
551 
552 int
elf_compress(Elf_Scn * scn,int type,unsigned int flags)553 elf_compress (Elf_Scn *scn, int type, unsigned int flags)
554 {
555   if (scn == NULL)
556     return -1;
557 
558   if ((flags & ~ELF_CHF_FORCE) != 0)
559     {
560       __libelf_seterrno (ELF_E_INVALID_OPERAND);
561       return -1;
562     }
563 
564   bool force = (flags & ELF_CHF_FORCE) != 0;
565 
566   Elf *elf = scn->elf;
567   GElf_Ehdr ehdr;
568   if (gelf_getehdr (elf, &ehdr) == NULL)
569     return -1;
570 
571   int elfclass = elf->class;
572   int elfdata = ehdr.e_ident[EI_DATA];
573 
574   Elf64_Xword sh_flags;
575   Elf64_Word sh_type;
576   Elf64_Xword sh_addralign;
577   if (elfclass == ELFCLASS32)
578     {
579       Elf32_Shdr *shdr = elf32_getshdr (scn);
580       if (shdr == NULL)
581 	return -1;
582 
583       sh_flags = shdr->sh_flags;
584       sh_type = shdr->sh_type;
585       sh_addralign = shdr->sh_addralign;
586     }
587   else
588     {
589       Elf64_Shdr *shdr = elf64_getshdr (scn);
590       if (shdr == NULL)
591 	return -1;
592 
593       sh_flags = shdr->sh_flags;
594       sh_type = shdr->sh_type;
595       sh_addralign = shdr->sh_addralign;
596     }
597 
598   if ((sh_flags & SHF_ALLOC) != 0)
599     {
600       __libelf_seterrno (ELF_E_INVALID_SECTION_FLAGS);
601       return -1;
602     }
603 
604   if (sh_type == SHT_NULL || sh_type == SHT_NOBITS)
605     {
606       __libelf_seterrno (ELF_E_INVALID_SECTION_TYPE);
607       return -1;
608     }
609 
610   int compressed = (sh_flags & SHF_COMPRESSED);
611   if (type == ELFCOMPRESS_ZLIB || type == ELFCOMPRESS_ZSTD)
612     {
613       /* Compress/Deflate.  */
614       if (compressed == 1)
615 	{
616 	  __libelf_seterrno (ELF_E_ALREADY_COMPRESSED);
617 	  return -1;
618 	}
619 
620       size_t hsize = (elfclass == ELFCLASS32
621 		      ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
622       size_t orig_size, orig_addralign, new_size;
623       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       if (out_buf == (void *) -1)
630 	return 0;
631 
632       /* Compression failed, return error.  */
633       if (out_buf == NULL)
634 	return -1;
635 
636       /* Put the header in front of the data.  */
637       if (elfclass == ELFCLASS32)
638 	{
639 	  Elf32_Chdr chdr;
640 	  chdr.ch_type = type;
641 	  chdr.ch_size = orig_size;
642 	  chdr.ch_addralign = orig_addralign;
643 	  if (elfdata != MY_ELFDATA)
644 	    {
645 	      CONVERT (chdr.ch_type);
646 	      CONVERT (chdr.ch_size);
647 	      CONVERT (chdr.ch_addralign);
648 	    }
649 	  memcpy (out_buf, &chdr, sizeof (Elf32_Chdr));
650 	}
651       else
652 	{
653 	  Elf64_Chdr chdr;
654 	  chdr.ch_type = type;
655 	  chdr.ch_reserved = 0;
656 	  chdr.ch_size = orig_size;
657 	  chdr.ch_addralign = sh_addralign;
658 	  if (elfdata != MY_ELFDATA)
659 	    {
660 	      CONVERT (chdr.ch_type);
661 	      CONVERT (chdr.ch_reserved);
662 	      CONVERT (chdr.ch_size);
663 	      CONVERT (chdr.ch_addralign);
664 	    }
665 	  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       if (elfclass == ELFCLASS32)
671 	{
672 	  Elf32_Shdr *shdr = elf32_getshdr (scn);
673 	  shdr->sh_size = new_size;
674 	  shdr->sh_addralign = __libelf_type_align (ELFCLASS32, ELF_T_CHDR);
675 	  shdr->sh_flags |= SHF_COMPRESSED;
676 	}
677       else
678 	{
679 	  Elf64_Shdr *shdr = elf64_getshdr (scn);
680 	  shdr->sh_size = new_size;
681 	  shdr->sh_addralign = __libelf_type_align (ELFCLASS64, ELF_T_CHDR);
682 	  shdr->sh_flags |= SHF_COMPRESSED;
683 	}
684 
685       __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       free (scn->zdata_base);
692       scn->zdata_base = NULL;
693 
694       return 1;
695     }
696   else if (type == 0)
697     {
698       /* Decompress/Inflate.  */
699       if (compressed == 0)
700 	{
701 	  __libelf_seterrno (ELF_E_NOT_COMPRESSED);
702 	  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       if (scn->zdata_base == NULL)
709 	{
710 	  size_t size_out, addralign;
711 	  void *buf_out = __libelf_decompress_elf (scn, &size_out, &addralign);
712 	  if (buf_out == NULL)
713 	    return -1;
714 
715 	  scn->zdata_base = buf_out;
716 	  scn->zdata_size = size_out;
717 	  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       if (elfclass == ELFCLASS32)
723 	{
724 	  Elf32_Shdr *shdr = elf32_getshdr (scn);
725 	  shdr->sh_size = scn->zdata_size;
726 	  shdr->sh_addralign = scn->zdata_align;
727 	  shdr->sh_flags &= ~SHF_COMPRESSED;
728 	}
729       else
730 	{
731 	  Elf64_Shdr *shdr = elf64_getshdr (scn);
732 	  shdr->sh_size = scn->zdata_size;
733 	  shdr->sh_addralign = scn->zdata_align;
734 	  shdr->sh_flags &= ~SHF_COMPRESSED;
735 	}
736 
737       __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       return 1;
743     }
744   else
745     {
746       __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
747       return -1;
748     }
749 }
750