1da0c48c4Sopenharmony_ci/* Compress or decompress a section.
2da0c48c4Sopenharmony_ci   Copyright (C) 2015, 2016 Red Hat, Inc.
3da0c48c4Sopenharmony_ci   This file is part of elfutils.
4da0c48c4Sopenharmony_ci
5da0c48c4Sopenharmony_ci   This file is free software; you can redistribute it and/or modify
6da0c48c4Sopenharmony_ci   it under the terms of either
7da0c48c4Sopenharmony_ci
8da0c48c4Sopenharmony_ci     * the GNU Lesser General Public License as published by the Free
9da0c48c4Sopenharmony_ci       Software Foundation; either version 3 of the License, or (at
10da0c48c4Sopenharmony_ci       your option) any later version
11da0c48c4Sopenharmony_ci
12da0c48c4Sopenharmony_ci   or
13da0c48c4Sopenharmony_ci
14da0c48c4Sopenharmony_ci     * the GNU General Public License as published by the Free
15da0c48c4Sopenharmony_ci       Software Foundation; either version 2 of the License, or (at
16da0c48c4Sopenharmony_ci       your option) any later version
17da0c48c4Sopenharmony_ci
18da0c48c4Sopenharmony_ci   or both in parallel, as here.
19da0c48c4Sopenharmony_ci
20da0c48c4Sopenharmony_ci   elfutils is distributed in the hope that it will be useful, but
21da0c48c4Sopenharmony_ci   WITHOUT ANY WARRANTY; without even the implied warranty of
22da0c48c4Sopenharmony_ci   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23da0c48c4Sopenharmony_ci   General Public License for more details.
24da0c48c4Sopenharmony_ci
25da0c48c4Sopenharmony_ci   You should have received copies of the GNU General Public License and
26da0c48c4Sopenharmony_ci   the GNU Lesser General Public License along with this program.  If
27da0c48c4Sopenharmony_ci   not, see <http://www.gnu.org/licenses/>.  */
28da0c48c4Sopenharmony_ci
29da0c48c4Sopenharmony_ci#ifdef HAVE_CONFIG_H
30da0c48c4Sopenharmony_ci# include <config.h>
31da0c48c4Sopenharmony_ci#endif
32da0c48c4Sopenharmony_ci
33da0c48c4Sopenharmony_ci#include <libelf.h>
34da0c48c4Sopenharmony_ci#include "libelfP.h"
35da0c48c4Sopenharmony_ci#include "common.h"
36da0c48c4Sopenharmony_ci
37da0c48c4Sopenharmony_ci#include <stddef.h>
38da0c48c4Sopenharmony_ci#include <stdlib.h>
39da0c48c4Sopenharmony_ci#include <string.h>
40da0c48c4Sopenharmony_ci#include <zlib.h>
41da0c48c4Sopenharmony_ci
42da0c48c4Sopenharmony_ci/* Cleanup and return result.  Don't leak memory.  */
43da0c48c4Sopenharmony_cistatic void *
44da0c48c4Sopenharmony_cido_deflate_cleanup (void *result, z_stream *z, void *out_buf,
45da0c48c4Sopenharmony_ci                    Elf_Data *cdatap)
46da0c48c4Sopenharmony_ci{
47da0c48c4Sopenharmony_ci  deflateEnd (z);
48da0c48c4Sopenharmony_ci  free (out_buf);
49da0c48c4Sopenharmony_ci  if (cdatap != NULL)
50da0c48c4Sopenharmony_ci    free (cdatap->d_buf);
51da0c48c4Sopenharmony_ci  return result;
52da0c48c4Sopenharmony_ci}
53da0c48c4Sopenharmony_ci
54da0c48c4Sopenharmony_ci#define deflate_cleanup(result, cdata) \
55da0c48c4Sopenharmony_ci    do_deflate_cleanup(result, &z, out_buf, cdata)
56da0c48c4Sopenharmony_ci
57da0c48c4Sopenharmony_ci/* Given a section, uses the (in-memory) Elf_Data to extract the
58da0c48c4Sopenharmony_ci   original data size (including the given header size) and data
59da0c48c4Sopenharmony_ci   alignment.  Returns a buffer that has at least hsize bytes (for the
60da0c48c4Sopenharmony_ci   caller to fill in with a header) plus zlib compressed date.  Also
61da0c48c4Sopenharmony_ci   returns the new buffer size in new_size (hsize + compressed data
62da0c48c4Sopenharmony_ci   size).  Returns (void *) -1 when FORCE is false and the compressed
63da0c48c4Sopenharmony_ci   data would be bigger than the original data.  */
64da0c48c4Sopenharmony_civoid *
65da0c48c4Sopenharmony_ciinternal_function
66da0c48c4Sopenharmony_ci__libelf_compress (Elf_Scn *scn, size_t hsize, int ei_data,
67da0c48c4Sopenharmony_ci		   size_t *orig_size, size_t *orig_addralign,
68da0c48c4Sopenharmony_ci		   size_t *new_size, bool force)
69da0c48c4Sopenharmony_ci{
70da0c48c4Sopenharmony_ci  /* The compressed data is the on-disk data.  We simplify the
71da0c48c4Sopenharmony_ci     implementation a bit by asking for the (converted) in-memory
72da0c48c4Sopenharmony_ci     data (which might be all there is if the user created it with
73da0c48c4Sopenharmony_ci     elf_newdata) and then convert back to raw if needed before
74da0c48c4Sopenharmony_ci     compressing.  Should be made a bit more clever to directly
75da0c48c4Sopenharmony_ci     use raw if that is directly available.  */
76da0c48c4Sopenharmony_ci  Elf_Data *data = elf_getdata (scn, NULL);
77da0c48c4Sopenharmony_ci  if (data == NULL)
78da0c48c4Sopenharmony_ci    return NULL;
79da0c48c4Sopenharmony_ci
80da0c48c4Sopenharmony_ci  /* When not forced and we immediately know we would use more data by
81da0c48c4Sopenharmony_ci     compressing, because of the header plus zlib overhead (five bytes
82da0c48c4Sopenharmony_ci     per 16 KB block, plus a one-time overhead of six bytes for the
83da0c48c4Sopenharmony_ci     entire stream), don't do anything.  */
84da0c48c4Sopenharmony_ci  Elf_Data *next_data = elf_getdata (scn, data);
85da0c48c4Sopenharmony_ci  if (next_data == NULL && !force
86da0c48c4Sopenharmony_ci      && data->d_size <= hsize + 5 + 6)
87da0c48c4Sopenharmony_ci    return (void *) -1;
88da0c48c4Sopenharmony_ci
89da0c48c4Sopenharmony_ci  *orig_addralign = data->d_align;
90da0c48c4Sopenharmony_ci  *orig_size = data->d_size;
91da0c48c4Sopenharmony_ci
92da0c48c4Sopenharmony_ci  /* Guess an output block size. 1/8th of the original Elf_Data plus
93da0c48c4Sopenharmony_ci     hsize.  Make the first chunk twice that size (25%), then increase
94da0c48c4Sopenharmony_ci     by a block (12.5%) when necessary.  */
95da0c48c4Sopenharmony_ci  size_t block = (data->d_size / 8) + hsize;
96da0c48c4Sopenharmony_ci  size_t out_size = 2 * block;
97da0c48c4Sopenharmony_ci  void *out_buf = malloc (out_size);
98da0c48c4Sopenharmony_ci  if (out_buf == NULL)
99da0c48c4Sopenharmony_ci    {
100da0c48c4Sopenharmony_ci      __libelf_seterrno (ELF_E_NOMEM);
101da0c48c4Sopenharmony_ci      return NULL;
102da0c48c4Sopenharmony_ci    }
103da0c48c4Sopenharmony_ci
104da0c48c4Sopenharmony_ci  /* Caller gets to fill in the header at the start.  Just skip it here.  */
105da0c48c4Sopenharmony_ci  size_t used = hsize;
106da0c48c4Sopenharmony_ci
107da0c48c4Sopenharmony_ci  z_stream z;
108da0c48c4Sopenharmony_ci  z.zalloc = Z_NULL;
109da0c48c4Sopenharmony_ci  z.zfree = Z_NULL;
110da0c48c4Sopenharmony_ci  z.opaque = Z_NULL;
111da0c48c4Sopenharmony_ci  int zrc = deflateInit (&z, Z_BEST_COMPRESSION);
112da0c48c4Sopenharmony_ci  if (zrc != Z_OK)
113da0c48c4Sopenharmony_ci    {
114da0c48c4Sopenharmony_ci      __libelf_seterrno (ELF_E_COMPRESS_ERROR);
115da0c48c4Sopenharmony_ci      return deflate_cleanup(NULL, NULL);
116da0c48c4Sopenharmony_ci    }
117da0c48c4Sopenharmony_ci
118da0c48c4Sopenharmony_ci  Elf_Data cdata;
119da0c48c4Sopenharmony_ci  cdata.d_buf = NULL;
120da0c48c4Sopenharmony_ci
121da0c48c4Sopenharmony_ci  /* Loop over data buffers.  */
122da0c48c4Sopenharmony_ci  int flush = Z_NO_FLUSH;
123da0c48c4Sopenharmony_ci  do
124da0c48c4Sopenharmony_ci    {
125da0c48c4Sopenharmony_ci      /* Convert to raw if different endianness.  */
126da0c48c4Sopenharmony_ci      cdata = *data;
127da0c48c4Sopenharmony_ci      bool convert = ei_data != MY_ELFDATA && data->d_size > 0;
128da0c48c4Sopenharmony_ci      if (convert)
129da0c48c4Sopenharmony_ci	{
130da0c48c4Sopenharmony_ci	  /* Don't do this conversion in place, we might want to keep
131da0c48c4Sopenharmony_ci	     the original data around, caller decides.  */
132da0c48c4Sopenharmony_ci	  cdata.d_buf = malloc (data->d_size);
133da0c48c4Sopenharmony_ci	  if (cdata.d_buf == NULL)
134da0c48c4Sopenharmony_ci	    {
135da0c48c4Sopenharmony_ci	      __libelf_seterrno (ELF_E_NOMEM);
136da0c48c4Sopenharmony_ci	      return deflate_cleanup (NULL, NULL);
137da0c48c4Sopenharmony_ci	    }
138da0c48c4Sopenharmony_ci	  if (gelf_xlatetof (scn->elf, &cdata, data, ei_data) == NULL)
139da0c48c4Sopenharmony_ci	    return deflate_cleanup (NULL, &cdata);
140da0c48c4Sopenharmony_ci	}
141da0c48c4Sopenharmony_ci
142da0c48c4Sopenharmony_ci      z.avail_in = cdata.d_size;
143da0c48c4Sopenharmony_ci      z.next_in = cdata.d_buf;
144da0c48c4Sopenharmony_ci
145da0c48c4Sopenharmony_ci      /* Get next buffer to see if this is the last one.  */
146da0c48c4Sopenharmony_ci      data = next_data;
147da0c48c4Sopenharmony_ci      if (data != NULL)
148da0c48c4Sopenharmony_ci	{
149da0c48c4Sopenharmony_ci	  *orig_addralign = MAX (*orig_addralign, data->d_align);
150da0c48c4Sopenharmony_ci	  *orig_size += data->d_size;
151da0c48c4Sopenharmony_ci	  next_data = elf_getdata (scn, data);
152da0c48c4Sopenharmony_ci	}
153da0c48c4Sopenharmony_ci      else
154da0c48c4Sopenharmony_ci	flush = Z_FINISH;
155da0c48c4Sopenharmony_ci
156da0c48c4Sopenharmony_ci      /* Flush one data buffer.  */
157da0c48c4Sopenharmony_ci      do
158da0c48c4Sopenharmony_ci	{
159da0c48c4Sopenharmony_ci	  z.avail_out = out_size - used;
160da0c48c4Sopenharmony_ci	  z.next_out = out_buf + used;
161da0c48c4Sopenharmony_ci	  zrc = deflate (&z, flush);
162da0c48c4Sopenharmony_ci	  if (zrc == Z_STREAM_ERROR)
163da0c48c4Sopenharmony_ci	    {
164da0c48c4Sopenharmony_ci	      __libelf_seterrno (ELF_E_COMPRESS_ERROR);
165da0c48c4Sopenharmony_ci	      return deflate_cleanup (NULL, convert ? &cdata : NULL);
166da0c48c4Sopenharmony_ci	    }
167da0c48c4Sopenharmony_ci	  used += (out_size - used) - z.avail_out;
168da0c48c4Sopenharmony_ci
169da0c48c4Sopenharmony_ci	  /* Bail out if we are sure the user doesn't want the
170da0c48c4Sopenharmony_ci	     compression forced and we are using more compressed data
171da0c48c4Sopenharmony_ci	     than original data.  */
172da0c48c4Sopenharmony_ci	  if (!force && flush == Z_FINISH && used >= *orig_size)
173da0c48c4Sopenharmony_ci	    return deflate_cleanup ((void *) -1, convert ? &cdata : NULL);
174da0c48c4Sopenharmony_ci
175da0c48c4Sopenharmony_ci	  if (z.avail_out == 0)
176da0c48c4Sopenharmony_ci	    {
177da0c48c4Sopenharmony_ci	      void *bigger = realloc (out_buf, out_size + block);
178da0c48c4Sopenharmony_ci	      if (bigger == NULL)
179da0c48c4Sopenharmony_ci		{
180da0c48c4Sopenharmony_ci		  __libelf_seterrno (ELF_E_NOMEM);
181da0c48c4Sopenharmony_ci		  return deflate_cleanup (NULL, convert ? &cdata : NULL);
182da0c48c4Sopenharmony_ci		}
183da0c48c4Sopenharmony_ci	      out_buf = bigger;
184da0c48c4Sopenharmony_ci	      out_size += block;
185da0c48c4Sopenharmony_ci	    }
186da0c48c4Sopenharmony_ci	}
187da0c48c4Sopenharmony_ci      while (z.avail_out == 0); /* Need more output buffer.  */
188da0c48c4Sopenharmony_ci
189da0c48c4Sopenharmony_ci      if (convert)
190da0c48c4Sopenharmony_ci	{
191da0c48c4Sopenharmony_ci	  free (cdata.d_buf);
192da0c48c4Sopenharmony_ci	  cdata.d_buf = NULL;
193da0c48c4Sopenharmony_ci	}
194da0c48c4Sopenharmony_ci    }
195da0c48c4Sopenharmony_ci  while (flush != Z_FINISH); /* More data blocks.  */
196da0c48c4Sopenharmony_ci
197da0c48c4Sopenharmony_ci  if (zrc != Z_STREAM_END)
198da0c48c4Sopenharmony_ci    {
199da0c48c4Sopenharmony_ci      __libelf_seterrno (ELF_E_COMPRESS_ERROR);
200da0c48c4Sopenharmony_ci      return deflate_cleanup (NULL, NULL);
201da0c48c4Sopenharmony_ci    }
202da0c48c4Sopenharmony_ci
203da0c48c4Sopenharmony_ci  deflateEnd (&z);
204da0c48c4Sopenharmony_ci  *new_size = used;
205da0c48c4Sopenharmony_ci  return out_buf;
206da0c48c4Sopenharmony_ci}
207da0c48c4Sopenharmony_ci
208da0c48c4Sopenharmony_civoid *
209da0c48c4Sopenharmony_ciinternal_function
210da0c48c4Sopenharmony_ci__libelf_decompress (void *buf_in, size_t size_in, size_t size_out)
211da0c48c4Sopenharmony_ci{
212da0c48c4Sopenharmony_ci  /* Catch highly unlikely compression ratios so we don't allocate
213da0c48c4Sopenharmony_ci     some giant amount of memory for nothing. The max compression
214da0c48c4Sopenharmony_ci     factor 1032:1 comes from http://www.zlib.net/zlib_tech.html  */
215da0c48c4Sopenharmony_ci  if (unlikely (size_out / 1032 > size_in))
216da0c48c4Sopenharmony_ci    {
217da0c48c4Sopenharmony_ci      __libelf_seterrno (ELF_E_INVALID_DATA);
218da0c48c4Sopenharmony_ci      return NULL;
219da0c48c4Sopenharmony_ci    }
220da0c48c4Sopenharmony_ci
221da0c48c4Sopenharmony_ci  /* Malloc might return NULL when requestion zero size.  This is highly
222da0c48c4Sopenharmony_ci     unlikely, it would only happen when the compression was forced.
223da0c48c4Sopenharmony_ci     But we do need a non-NULL buffer to return and set as result.
224da0c48c4Sopenharmony_ci     Just make sure to always allocate at least 1 byte.  */
225da0c48c4Sopenharmony_ci  void *buf_out = malloc (size_out ?: 1);
226da0c48c4Sopenharmony_ci  if (unlikely (buf_out == NULL))
227da0c48c4Sopenharmony_ci    {
228da0c48c4Sopenharmony_ci      __libelf_seterrno (ELF_E_NOMEM);
229da0c48c4Sopenharmony_ci      return NULL;
230da0c48c4Sopenharmony_ci    }
231da0c48c4Sopenharmony_ci
232da0c48c4Sopenharmony_ci  z_stream z =
233da0c48c4Sopenharmony_ci    {
234da0c48c4Sopenharmony_ci      .next_in = buf_in,
235da0c48c4Sopenharmony_ci      .avail_in = size_in,
236da0c48c4Sopenharmony_ci      .next_out = buf_out,
237da0c48c4Sopenharmony_ci      .avail_out = size_out
238da0c48c4Sopenharmony_ci    };
239da0c48c4Sopenharmony_ci  int zrc = inflateInit (&z);
240da0c48c4Sopenharmony_ci  while (z.avail_in > 0 && likely (zrc == Z_OK))
241da0c48c4Sopenharmony_ci    {
242da0c48c4Sopenharmony_ci      z.next_out = buf_out + (size_out - z.avail_out);
243da0c48c4Sopenharmony_ci      zrc = inflate (&z, Z_FINISH);
244da0c48c4Sopenharmony_ci      if (unlikely (zrc != Z_STREAM_END))
245da0c48c4Sopenharmony_ci	{
246da0c48c4Sopenharmony_ci	  zrc = Z_DATA_ERROR;
247da0c48c4Sopenharmony_ci	  break;
248da0c48c4Sopenharmony_ci	}
249da0c48c4Sopenharmony_ci      zrc = inflateReset (&z);
250da0c48c4Sopenharmony_ci    }
251da0c48c4Sopenharmony_ci
252da0c48c4Sopenharmony_ci  if (unlikely (zrc != Z_OK) || unlikely (z.avail_out != 0))
253da0c48c4Sopenharmony_ci    {
254da0c48c4Sopenharmony_ci      free (buf_out);
255da0c48c4Sopenharmony_ci      buf_out = NULL;
256da0c48c4Sopenharmony_ci      __libelf_seterrno (ELF_E_DECOMPRESS_ERROR);
257da0c48c4Sopenharmony_ci    }
258da0c48c4Sopenharmony_ci
259da0c48c4Sopenharmony_ci  inflateEnd(&z);
260da0c48c4Sopenharmony_ci  return buf_out;
261da0c48c4Sopenharmony_ci}
262da0c48c4Sopenharmony_ci
263da0c48c4Sopenharmony_civoid *
264da0c48c4Sopenharmony_ciinternal_function
265da0c48c4Sopenharmony_ci__libelf_decompress_elf (Elf_Scn *scn, size_t *size_out, size_t *addralign)
266da0c48c4Sopenharmony_ci{
267da0c48c4Sopenharmony_ci  GElf_Chdr chdr;
268da0c48c4Sopenharmony_ci  if (gelf_getchdr (scn, &chdr) == NULL)
269da0c48c4Sopenharmony_ci    return NULL;
270da0c48c4Sopenharmony_ci
271da0c48c4Sopenharmony_ci  if (chdr.ch_type != ELFCOMPRESS_ZLIB)
272da0c48c4Sopenharmony_ci    {
273da0c48c4Sopenharmony_ci      __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
274da0c48c4Sopenharmony_ci      return NULL;
275da0c48c4Sopenharmony_ci    }
276da0c48c4Sopenharmony_ci
277da0c48c4Sopenharmony_ci  if (! powerof2 (chdr.ch_addralign))
278da0c48c4Sopenharmony_ci    {
279da0c48c4Sopenharmony_ci      __libelf_seterrno (ELF_E_INVALID_ALIGN);
280da0c48c4Sopenharmony_ci      return NULL;
281da0c48c4Sopenharmony_ci    }
282da0c48c4Sopenharmony_ci
283da0c48c4Sopenharmony_ci  /* Take the in-memory representation, so we can even handle a
284da0c48c4Sopenharmony_ci     section that has just been constructed (maybe it was copied
285da0c48c4Sopenharmony_ci     over from some other ELF file first with elf_newdata).  This
286da0c48c4Sopenharmony_ci     is slightly inefficient when the raw data needs to be
287da0c48c4Sopenharmony_ci     converted since then we'll be converting the whole buffer and
288da0c48c4Sopenharmony_ci     not just Chdr.  */
289da0c48c4Sopenharmony_ci  Elf_Data *data = elf_getdata (scn, NULL);
290da0c48c4Sopenharmony_ci  if (data == NULL)
291da0c48c4Sopenharmony_ci    return NULL;
292da0c48c4Sopenharmony_ci
293da0c48c4Sopenharmony_ci  int elfclass = scn->elf->class;
294da0c48c4Sopenharmony_ci  size_t hsize = (elfclass == ELFCLASS32
295da0c48c4Sopenharmony_ci		  ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
296da0c48c4Sopenharmony_ci  size_t size_in = data->d_size - hsize;
297da0c48c4Sopenharmony_ci  void *buf_in = data->d_buf + hsize;
298da0c48c4Sopenharmony_ci  void *buf_out = __libelf_decompress (buf_in, size_in, chdr.ch_size);
299da0c48c4Sopenharmony_ci  *size_out = chdr.ch_size;
300da0c48c4Sopenharmony_ci  *addralign = chdr.ch_addralign;
301da0c48c4Sopenharmony_ci  return buf_out;
302da0c48c4Sopenharmony_ci}
303da0c48c4Sopenharmony_ci
304da0c48c4Sopenharmony_ci/* Assumes buf is a malloced buffer.  */
305da0c48c4Sopenharmony_civoid
306da0c48c4Sopenharmony_ciinternal_function
307da0c48c4Sopenharmony_ci__libelf_reset_rawdata (Elf_Scn *scn, void *buf, size_t size, size_t align,
308da0c48c4Sopenharmony_ci			Elf_Type type)
309da0c48c4Sopenharmony_ci{
310da0c48c4Sopenharmony_ci  /* This is the new raw data, replace and possibly free old data.  */
311da0c48c4Sopenharmony_ci  scn->rawdata.d.d_off = 0;
312da0c48c4Sopenharmony_ci  scn->rawdata.d.d_version = EV_CURRENT;
313da0c48c4Sopenharmony_ci  scn->rawdata.d.d_buf = buf;
314da0c48c4Sopenharmony_ci  scn->rawdata.d.d_size = size;
315da0c48c4Sopenharmony_ci  scn->rawdata.d.d_align = align;
316da0c48c4Sopenharmony_ci  scn->rawdata.d.d_type = type;
317da0c48c4Sopenharmony_ci
318da0c48c4Sopenharmony_ci  /* Existing existing data is no longer valid.  */
319da0c48c4Sopenharmony_ci  scn->data_list_rear = NULL;
320da0c48c4Sopenharmony_ci  if (scn->data_base != scn->rawdata_base)
321da0c48c4Sopenharmony_ci    free (scn->data_base);
322da0c48c4Sopenharmony_ci  scn->data_base = NULL;
323da0c48c4Sopenharmony_ci  if (scn->elf->map_address == NULL
324da0c48c4Sopenharmony_ci      || scn->rawdata_base == scn->zdata_base
325da0c48c4Sopenharmony_ci      || (scn->flags & ELF_F_MALLOCED) != 0)
326da0c48c4Sopenharmony_ci    free (scn->rawdata_base);
327da0c48c4Sopenharmony_ci
328da0c48c4Sopenharmony_ci  scn->rawdata_base = buf;
329da0c48c4Sopenharmony_ci  scn->flags |= ELF_F_MALLOCED;
330da0c48c4Sopenharmony_ci
331da0c48c4Sopenharmony_ci  /* Pretend we (tried to) read the data from the file and setup the
332da0c48c4Sopenharmony_ci     data (might have to convert the Chdr to native format).  */
333da0c48c4Sopenharmony_ci  scn->data_read = 1;
334da0c48c4Sopenharmony_ci  scn->flags |= ELF_F_FILEDATA;
335da0c48c4Sopenharmony_ci  __libelf_set_data_list_rdlock (scn, 1);
336da0c48c4Sopenharmony_ci}
337da0c48c4Sopenharmony_ci
338da0c48c4Sopenharmony_ciint
339da0c48c4Sopenharmony_cielf_compress (Elf_Scn *scn, int type, unsigned int flags)
340da0c48c4Sopenharmony_ci{
341da0c48c4Sopenharmony_ci  if (scn == NULL)
342da0c48c4Sopenharmony_ci    return -1;
343da0c48c4Sopenharmony_ci
344da0c48c4Sopenharmony_ci  if ((flags & ~ELF_CHF_FORCE) != 0)
345da0c48c4Sopenharmony_ci    {
346da0c48c4Sopenharmony_ci      __libelf_seterrno (ELF_E_INVALID_OPERAND);
347da0c48c4Sopenharmony_ci      return -1;
348da0c48c4Sopenharmony_ci    }
349da0c48c4Sopenharmony_ci
350da0c48c4Sopenharmony_ci  bool force = (flags & ELF_CHF_FORCE) != 0;
351da0c48c4Sopenharmony_ci
352da0c48c4Sopenharmony_ci  Elf *elf = scn->elf;
353da0c48c4Sopenharmony_ci  GElf_Ehdr ehdr;
354da0c48c4Sopenharmony_ci  if (gelf_getehdr (elf, &ehdr) == NULL)
355da0c48c4Sopenharmony_ci    return -1;
356da0c48c4Sopenharmony_ci
357da0c48c4Sopenharmony_ci  int elfclass = elf->class;
358da0c48c4Sopenharmony_ci  int elfdata = ehdr.e_ident[EI_DATA];
359da0c48c4Sopenharmony_ci
360da0c48c4Sopenharmony_ci  Elf64_Xword sh_flags;
361da0c48c4Sopenharmony_ci  Elf64_Word sh_type;
362da0c48c4Sopenharmony_ci  Elf64_Xword sh_addralign;
363da0c48c4Sopenharmony_ci  if (elfclass == ELFCLASS32)
364da0c48c4Sopenharmony_ci    {
365da0c48c4Sopenharmony_ci      Elf32_Shdr *shdr = elf32_getshdr (scn);
366da0c48c4Sopenharmony_ci      if (shdr == NULL)
367da0c48c4Sopenharmony_ci	return -1;
368da0c48c4Sopenharmony_ci
369da0c48c4Sopenharmony_ci      sh_flags = shdr->sh_flags;
370da0c48c4Sopenharmony_ci      sh_type = shdr->sh_type;
371da0c48c4Sopenharmony_ci      sh_addralign = shdr->sh_addralign;
372da0c48c4Sopenharmony_ci    }
373da0c48c4Sopenharmony_ci  else
374da0c48c4Sopenharmony_ci    {
375da0c48c4Sopenharmony_ci      Elf64_Shdr *shdr = elf64_getshdr (scn);
376da0c48c4Sopenharmony_ci      if (shdr == NULL)
377da0c48c4Sopenharmony_ci	return -1;
378da0c48c4Sopenharmony_ci
379da0c48c4Sopenharmony_ci      sh_flags = shdr->sh_flags;
380da0c48c4Sopenharmony_ci      sh_type = shdr->sh_type;
381da0c48c4Sopenharmony_ci      sh_addralign = shdr->sh_addralign;
382da0c48c4Sopenharmony_ci    }
383da0c48c4Sopenharmony_ci
384da0c48c4Sopenharmony_ci  if ((sh_flags & SHF_ALLOC) != 0)
385da0c48c4Sopenharmony_ci    {
386da0c48c4Sopenharmony_ci      __libelf_seterrno (ELF_E_INVALID_SECTION_FLAGS);
387da0c48c4Sopenharmony_ci      return -1;
388da0c48c4Sopenharmony_ci    }
389da0c48c4Sopenharmony_ci
390da0c48c4Sopenharmony_ci  if (sh_type == SHT_NULL || sh_type == SHT_NOBITS)
391da0c48c4Sopenharmony_ci    {
392da0c48c4Sopenharmony_ci      __libelf_seterrno (ELF_E_INVALID_SECTION_TYPE);
393da0c48c4Sopenharmony_ci      return -1;
394da0c48c4Sopenharmony_ci    }
395da0c48c4Sopenharmony_ci
396da0c48c4Sopenharmony_ci  int compressed = (sh_flags & SHF_COMPRESSED);
397da0c48c4Sopenharmony_ci  if (type == ELFCOMPRESS_ZLIB)
398da0c48c4Sopenharmony_ci    {
399da0c48c4Sopenharmony_ci      /* Compress/Deflate.  */
400da0c48c4Sopenharmony_ci      if (compressed == 1)
401da0c48c4Sopenharmony_ci	{
402da0c48c4Sopenharmony_ci	  __libelf_seterrno (ELF_E_ALREADY_COMPRESSED);
403da0c48c4Sopenharmony_ci	  return -1;
404da0c48c4Sopenharmony_ci	}
405da0c48c4Sopenharmony_ci
406da0c48c4Sopenharmony_ci      size_t hsize = (elfclass == ELFCLASS32
407da0c48c4Sopenharmony_ci		      ? sizeof (Elf32_Chdr) : sizeof (Elf64_Chdr));
408da0c48c4Sopenharmony_ci      size_t orig_size, orig_addralign, new_size;
409da0c48c4Sopenharmony_ci      void *out_buf = __libelf_compress (scn, hsize, elfdata,
410da0c48c4Sopenharmony_ci					 &orig_size, &orig_addralign,
411da0c48c4Sopenharmony_ci					 &new_size, force);
412da0c48c4Sopenharmony_ci
413da0c48c4Sopenharmony_ci      /* Compression would make section larger, don't change anything.  */
414da0c48c4Sopenharmony_ci      if (out_buf == (void *) -1)
415da0c48c4Sopenharmony_ci	return 0;
416da0c48c4Sopenharmony_ci
417da0c48c4Sopenharmony_ci      /* Compression failed, return error.  */
418da0c48c4Sopenharmony_ci      if (out_buf == NULL)
419da0c48c4Sopenharmony_ci	return -1;
420da0c48c4Sopenharmony_ci
421da0c48c4Sopenharmony_ci      /* Put the header in front of the data.  */
422da0c48c4Sopenharmony_ci      if (elfclass == ELFCLASS32)
423da0c48c4Sopenharmony_ci	{
424da0c48c4Sopenharmony_ci	  Elf32_Chdr chdr;
425da0c48c4Sopenharmony_ci	  chdr.ch_type = ELFCOMPRESS_ZLIB;
426da0c48c4Sopenharmony_ci	  chdr.ch_size = orig_size;
427da0c48c4Sopenharmony_ci	  chdr.ch_addralign = orig_addralign;
428da0c48c4Sopenharmony_ci	  if (elfdata != MY_ELFDATA)
429da0c48c4Sopenharmony_ci	    {
430da0c48c4Sopenharmony_ci	      CONVERT (chdr.ch_type);
431da0c48c4Sopenharmony_ci	      CONVERT (chdr.ch_size);
432da0c48c4Sopenharmony_ci	      CONVERT (chdr.ch_addralign);
433da0c48c4Sopenharmony_ci	    }
434da0c48c4Sopenharmony_ci	  memcpy (out_buf, &chdr, sizeof (Elf32_Chdr));
435da0c48c4Sopenharmony_ci	}
436da0c48c4Sopenharmony_ci      else
437da0c48c4Sopenharmony_ci	{
438da0c48c4Sopenharmony_ci	  Elf64_Chdr chdr;
439da0c48c4Sopenharmony_ci	  chdr.ch_type = ELFCOMPRESS_ZLIB;
440da0c48c4Sopenharmony_ci	  chdr.ch_reserved = 0;
441da0c48c4Sopenharmony_ci	  chdr.ch_size = orig_size;
442da0c48c4Sopenharmony_ci	  chdr.ch_addralign = sh_addralign;
443da0c48c4Sopenharmony_ci	  if (elfdata != MY_ELFDATA)
444da0c48c4Sopenharmony_ci	    {
445da0c48c4Sopenharmony_ci	      CONVERT (chdr.ch_type);
446da0c48c4Sopenharmony_ci	      CONVERT (chdr.ch_reserved);
447da0c48c4Sopenharmony_ci	      CONVERT (chdr.ch_size);
448da0c48c4Sopenharmony_ci	      CONVERT (chdr.ch_addralign);
449da0c48c4Sopenharmony_ci	    }
450da0c48c4Sopenharmony_ci	  memcpy (out_buf, &chdr, sizeof (Elf64_Chdr));
451da0c48c4Sopenharmony_ci	}
452da0c48c4Sopenharmony_ci
453da0c48c4Sopenharmony_ci      /* Note we keep the sh_entsize as is, we assume it is setup
454da0c48c4Sopenharmony_ci	 correctly and ignored when SHF_COMPRESSED is set.  */
455da0c48c4Sopenharmony_ci      if (elfclass == ELFCLASS32)
456da0c48c4Sopenharmony_ci	{
457da0c48c4Sopenharmony_ci	  Elf32_Shdr *shdr = elf32_getshdr (scn);
458da0c48c4Sopenharmony_ci	  shdr->sh_size = new_size;
459da0c48c4Sopenharmony_ci	  shdr->sh_addralign = __libelf_type_align (ELFCLASS32, ELF_T_CHDR);
460da0c48c4Sopenharmony_ci	  shdr->sh_flags |= SHF_COMPRESSED;
461da0c48c4Sopenharmony_ci	}
462da0c48c4Sopenharmony_ci      else
463da0c48c4Sopenharmony_ci	{
464da0c48c4Sopenharmony_ci	  Elf64_Shdr *shdr = elf64_getshdr (scn);
465da0c48c4Sopenharmony_ci	  shdr->sh_size = new_size;
466da0c48c4Sopenharmony_ci	  shdr->sh_addralign = __libelf_type_align (ELFCLASS64, ELF_T_CHDR);
467da0c48c4Sopenharmony_ci	  shdr->sh_flags |= SHF_COMPRESSED;
468da0c48c4Sopenharmony_ci	}
469da0c48c4Sopenharmony_ci
470da0c48c4Sopenharmony_ci      __libelf_reset_rawdata (scn, out_buf, new_size, 1, ELF_T_CHDR);
471da0c48c4Sopenharmony_ci
472da0c48c4Sopenharmony_ci      /* The section is now compressed, we could keep the uncompressed
473da0c48c4Sopenharmony_ci	 data around, but since that might have been multiple Elf_Data
474da0c48c4Sopenharmony_ci	 buffers let the user uncompress it explicitly again if they
475da0c48c4Sopenharmony_ci	 want it to simplify bookkeeping.  */
476da0c48c4Sopenharmony_ci      scn->zdata_base = NULL;
477da0c48c4Sopenharmony_ci
478da0c48c4Sopenharmony_ci      return 1;
479da0c48c4Sopenharmony_ci    }
480da0c48c4Sopenharmony_ci  else if (type == 0)
481da0c48c4Sopenharmony_ci    {
482da0c48c4Sopenharmony_ci      /* Decompress/Inflate.  */
483da0c48c4Sopenharmony_ci      if (compressed == 0)
484da0c48c4Sopenharmony_ci	{
485da0c48c4Sopenharmony_ci	  __libelf_seterrno (ELF_E_NOT_COMPRESSED);
486da0c48c4Sopenharmony_ci	  return -1;
487da0c48c4Sopenharmony_ci	}
488da0c48c4Sopenharmony_ci
489da0c48c4Sopenharmony_ci      /* If the data is already decompressed (by elf_strptr), then we
490da0c48c4Sopenharmony_ci	 only need to setup the rawdata and section header. XXX what
491da0c48c4Sopenharmony_ci	 about elf_newdata?  */
492da0c48c4Sopenharmony_ci      if (scn->zdata_base == NULL)
493da0c48c4Sopenharmony_ci	{
494da0c48c4Sopenharmony_ci	  size_t size_out, addralign;
495da0c48c4Sopenharmony_ci	  void *buf_out = __libelf_decompress_elf (scn, &size_out, &addralign);
496da0c48c4Sopenharmony_ci	  if (buf_out == NULL)
497da0c48c4Sopenharmony_ci	    return -1;
498da0c48c4Sopenharmony_ci
499da0c48c4Sopenharmony_ci	  scn->zdata_base = buf_out;
500da0c48c4Sopenharmony_ci	  scn->zdata_size = size_out;
501da0c48c4Sopenharmony_ci	  scn->zdata_align = addralign;
502da0c48c4Sopenharmony_ci	}
503da0c48c4Sopenharmony_ci
504da0c48c4Sopenharmony_ci      /* Note we keep the sh_entsize as is, we assume it is setup
505da0c48c4Sopenharmony_ci	 correctly and ignored when SHF_COMPRESSED is set.  */
506da0c48c4Sopenharmony_ci      if (elfclass == ELFCLASS32)
507da0c48c4Sopenharmony_ci	{
508da0c48c4Sopenharmony_ci	  Elf32_Shdr *shdr = elf32_getshdr (scn);
509da0c48c4Sopenharmony_ci	  shdr->sh_size = scn->zdata_size;
510da0c48c4Sopenharmony_ci	  shdr->sh_addralign = scn->zdata_align;
511da0c48c4Sopenharmony_ci	  shdr->sh_flags &= ~SHF_COMPRESSED;
512da0c48c4Sopenharmony_ci	}
513da0c48c4Sopenharmony_ci      else
514da0c48c4Sopenharmony_ci	{
515da0c48c4Sopenharmony_ci	  Elf64_Shdr *shdr = elf64_getshdr (scn);
516da0c48c4Sopenharmony_ci	  shdr->sh_size = scn->zdata_size;
517da0c48c4Sopenharmony_ci	  shdr->sh_addralign = scn->zdata_align;
518da0c48c4Sopenharmony_ci	  shdr->sh_flags &= ~SHF_COMPRESSED;
519da0c48c4Sopenharmony_ci	}
520da0c48c4Sopenharmony_ci
521da0c48c4Sopenharmony_ci      __libelf_reset_rawdata (scn, scn->zdata_base,
522da0c48c4Sopenharmony_ci			      scn->zdata_size, scn->zdata_align,
523da0c48c4Sopenharmony_ci			      __libelf_data_type (&ehdr, sh_type,
524da0c48c4Sopenharmony_ci						  scn->zdata_align));
525da0c48c4Sopenharmony_ci
526da0c48c4Sopenharmony_ci      return 1;
527da0c48c4Sopenharmony_ci    }
528da0c48c4Sopenharmony_ci  else
529da0c48c4Sopenharmony_ci    {
530da0c48c4Sopenharmony_ci      __libelf_seterrno (ELF_E_UNKNOWN_COMPRESSION_TYPE);
531da0c48c4Sopenharmony_ci      return -1;
532da0c48c4Sopenharmony_ci    }
533da0c48c4Sopenharmony_ci}
534