blob: ba8ecfba6c316b261ee38bb288ab163664ade9e5 [file] [log] [blame]
Roland McGrathbca43152009-01-05 23:59:32 -08001/* Decompression support for libdwfl: zlib (gzip) and/or bzlib (bzip2).
2 Copyright (C) 2009 Red Hat, Inc.
Mark Wielaardde2ed972012-06-05 17:15:16 +02003 This file is part of elfutils.
Roland McGrathbca43152009-01-05 23:59:32 -08004
Mark Wielaardde2ed972012-06-05 17:15:16 +02005 This file is free software; you can redistribute it and/or modify
6 it under the terms of either
Roland McGrathbca43152009-01-05 23:59:32 -08007
Mark Wielaardde2ed972012-06-05 17:15:16 +02008 * 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
Roland McGrathbca43152009-01-05 23:59:32 -080021 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
Mark Wielaardde2ed972012-06-05 17:15:16 +020025 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/>. */
Roland McGrathbca43152009-01-05 23:59:32 -080028
Ulf Hermann575198c2017-04-20 16:31:02 +020029#ifdef HAVE_CONFIG_H
30# include <config.h>
31#endif
32
Roland McGrathbca43152009-01-05 23:59:32 -080033#include "libdwflP.h"
Roland McGrathae1d7dc2009-08-26 01:27:59 -070034#include "system.h"
Roland McGrathbca43152009-01-05 23:59:32 -080035
36#include <unistd.h>
37
Roland McGrath24169642009-08-26 02:26:34 -070038#ifdef LZMA
39# define USE_INFLATE 1
40# include <lzma.h>
41# define unzip __libdw_unlzma
42# define DWFL_E_ZLIB DWFL_E_LZMA
Roland McGrath6bb90712009-08-27 12:36:47 -070043# define MAGIC "\xFD" "7zXZ\0" /* XZ file format. */
44# define MAGIC2 "\x5d\0" /* Raw LZMA format. */
Roland McGrath24169642009-08-26 02:26:34 -070045# define Z(what) LZMA_##what
46# define LZMA_ERRNO LZMA_PROG_ERROR
47# define z_stream lzma_stream
48# define inflateInit(z) lzma_auto_decoder (z, 1 << 30, 0)
49# define do_inflate(z) lzma_code (z, LZMA_RUN)
50# define inflateEnd(z) lzma_end (z)
Mark Wielaard52536d72020-09-18 12:49:29 +020051#elif defined ZSTD
52# define USE_INFLATE 1
53# include <zstd.h>
54# define unzip __libdw_unzstd
55# define DWFL_E_ZLIB DWFL_E_ZSTD
56# define MAGIC "\x28\xb5\x2f\xfd"
Roland McGrath24169642009-08-26 02:26:34 -070057#elif defined BZLIB
Roland McGrathae1d7dc2009-08-26 01:27:59 -070058# define USE_INFLATE 1
Roland McGrathbca43152009-01-05 23:59:32 -080059# include <bzlib.h>
60# define unzip __libdw_bunzip2
61# define DWFL_E_ZLIB DWFL_E_BZLIB
62# define MAGIC "BZh"
63# define Z(what) BZ_##what
Roland McGrathae1d7dc2009-08-26 01:27:59 -070064# define BZ_ERRNO BZ_IO_ERROR
Roland McGrathbca43152009-01-05 23:59:32 -080065# define z_stream bz_stream
66# define inflateInit(z) BZ2_bzDecompressInit (z, 0, 0)
Roland McGrath24169642009-08-26 02:26:34 -070067# define do_inflate(z) BZ2_bzDecompress (z)
Roland McGrathbca43152009-01-05 23:59:32 -080068# define inflateEnd(z) BZ2_bzDecompressEnd (z)
Roland McGrathbca43152009-01-05 23:59:32 -080069#else
Roland McGrathae1d7dc2009-08-26 01:27:59 -070070# define USE_INFLATE 0
71# define crc32 loser_crc32
Roland McGrathbca43152009-01-05 23:59:32 -080072# include <zlib.h>
73# define unzip __libdw_gunzip
74# define MAGIC "\037\213"
75# define Z(what) Z_##what
Roland McGrathd6ccdc12009-08-26 00:23:01 -070076#endif
Roland McGrathc4ac8612009-01-14 03:01:49 -080077
Roland McGrathae1d7dc2009-08-26 01:27:59 -070078#define READ_SIZE (1 << 20)
79
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -080080struct unzip_state {
81#if !USE_INFLATE
82 gzFile zf;
83#endif
84 size_t mapped_size;
85 void **whole;
86 void *buffer;
87 size_t size;
88 void *input_buffer;
89 off_t input_pos;
90};
91
92static inline bool
93bigger_buffer (struct unzip_state *state, size_t start)
94{
95 size_t more = state->size ? state->size * 2 : start;
96 char *b = realloc (state->buffer, more);
97 while (unlikely (b == NULL) && more >= state->size + 1024)
98 b = realloc (state->buffer, more -= 1024);
99 if (unlikely (b == NULL))
100 return false;
101 state->buffer = b;
102 state->size = more;
103 return true;
104}
105
106static inline void
107smaller_buffer (struct unzip_state *state, size_t end)
108{
109 state->buffer =
110 realloc (state->buffer, end) ?: end == 0 ? NULL : state->buffer;
111 state->size = end;
112}
113
114static inline Dwfl_Error
115fail (struct unzip_state *state, Dwfl_Error failure)
116{
117 if (state->input_pos == (off_t) state->mapped_size)
118 *state->whole = state->input_buffer;
119 else
120 {
121 free (state->input_buffer);
122 *state->whole = NULL;
123 }
124 free (state->buffer);
125 return failure;
126}
127
Mark Wielaard52536d72020-09-18 12:49:29 +0200128#ifndef ZSTD
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800129static inline Dwfl_Error
130zlib_fail (struct unzip_state *state, int result)
131{
132 switch (result)
133 {
134 case Z (MEM_ERROR):
135 return fail (state, DWFL_E_NOMEM);
136 case Z (ERRNO):
137 return fail (state, DWFL_E_ERRNO);
138 default:
139 return fail (state, DWFL_E_ZLIB);
140 }
141}
Mark Wielaard52536d72020-09-18 12:49:29 +0200142#endif
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800143
144#if !USE_INFLATE
145static Dwfl_Error
146open_stream (int fd, off_t start_offset, struct unzip_state *state)
147{
148 int d = dup (fd);
149 if (unlikely (d < 0))
Mark Wielaard7ea82242019-08-12 00:43:22 +0200150 return DWFL_E_ERRNO;
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800151 if (start_offset != 0)
152 {
153 off_t off = lseek (d, start_offset, SEEK_SET);
154 if (off != start_offset)
155 {
156 close (d);
Mark Wielaard7ea82242019-08-12 00:43:22 +0200157 return DWFL_E_ERRNO;
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800158 }
159 }
160 state->zf = gzdopen (d, "r");
161 if (unlikely (state->zf == NULL))
162 {
163 close (d);
Mark Wielaarda894c632020-04-26 02:10:41 +0200164 return DWFL_E_NOMEM;
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800165 }
166
167 /* From here on, zlib will close D. */
168
169 return DWFL_E_NOERROR;
170}
171#endif
172
Roland McGrathbca43152009-01-05 23:59:32 -0800173/* If this is not a compressed image, return DWFL_E_BADELF.
174 If we uncompressed it into *WHOLE, *WHOLE_SIZE, return DWFL_E_NOERROR.
Roland McGrath6bb90712009-08-27 12:36:47 -0700175 Otherwise return an error for bad compressed data or I/O failure.
176 If we return an error after reading the first part of the file,
177 leave that portion malloc'd in *WHOLE, *WHOLE_SIZE. If *WHOLE
178 is not null on entry, we'll use it in lieu of repeating a read. */
Roland McGrathbca43152009-01-05 23:59:32 -0800179
180Dwfl_Error internal_function
Josh Stone34254542015-10-09 10:10:37 -0700181unzip (int fd, off_t start_offset,
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800182 void *mapped, size_t _mapped_size,
183 void **_whole, size_t *whole_size)
Roland McGrathbca43152009-01-05 23:59:32 -0800184{
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800185 struct unzip_state state =
186 {
187#if !USE_INFLATE
188 .zf = NULL,
189#endif
190 .mapped_size = _mapped_size,
191 .whole = _whole,
192 .buffer = NULL,
193 .size = 0,
194 .input_buffer = NULL,
195 .input_pos = 0
196 };
Roland McGrathbca43152009-01-05 23:59:32 -0800197
Roland McGrathae1d7dc2009-08-26 01:27:59 -0700198 if (mapped == NULL)
199 {
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800200 if (*state.whole == NULL)
Roland McGrath6bb90712009-08-27 12:36:47 -0700201 {
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800202 state.input_buffer = malloc (READ_SIZE);
203 if (unlikely (state.input_buffer == NULL))
Roland McGrath6bb90712009-08-27 12:36:47 -0700204 return DWFL_E_NOMEM;
Roland McGrathae1d7dc2009-08-26 01:27:59 -0700205
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800206 ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE, start_offset);
Roland McGrath6bb90712009-08-27 12:36:47 -0700207 if (unlikely (n < 0))
Mark Wielaard52536d72020-09-18 12:49:29 +0200208 return fail (&state, DWFL_E_ERRNO);
Roland McGrathae1d7dc2009-08-26 01:27:59 -0700209
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800210 state.input_pos = n;
211 mapped = state.input_buffer;
212 state.mapped_size = n;
Roland McGrath6bb90712009-08-27 12:36:47 -0700213 }
214 else
215 {
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800216 state.input_buffer = *state.whole;
217 state.input_pos = state.mapped_size = *whole_size;
Roland McGrath6bb90712009-08-27 12:36:47 -0700218 }
Roland McGrathae1d7dc2009-08-26 01:27:59 -0700219 }
220
Roland McGrath6bb90712009-08-27 12:36:47 -0700221#define NOMAGIC(magic) \
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800222 (state.mapped_size <= sizeof magic || \
223 memcmp (mapped, magic, sizeof magic - 1))
Roland McGrath6bb90712009-08-27 12:36:47 -0700224
Roland McGrathae1d7dc2009-08-26 01:27:59 -0700225 /* First, look at the header. */
Roland McGrath6bb90712009-08-27 12:36:47 -0700226 if (NOMAGIC (MAGIC)
227#ifdef MAGIC2
228 && NOMAGIC (MAGIC2)
229#endif
230 )
Roland McGratha6ef1dc2009-01-06 00:54:49 -0800231 /* Not a compressed file. */
232 return DWFL_E_BADELF;
233
Mark Wielaard52536d72020-09-18 12:49:29 +0200234#ifdef ZSTD
235 /* special case for libzstd since it is slightly different from the
236 API provided by bzlib and liblzma. */
237
238 void *next_in = mapped;
239 size_t avail_in = state.mapped_size;
240 void *next_out = NULL;
241 size_t avail_out = 0;
242 size_t total_out = 0;
243
244 size_t result;
245 ZSTD_DCtx *dctx = ZSTD_createDCtx();
246 if (dctx == NULL)
247 return fail (&state, DWFL_E_NOMEM);
248
249 do
250 {
251 if (avail_in == 0 && state.input_buffer != NULL)
252 {
253 ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE,
254 start_offset + state.input_pos);
255 if (unlikely (n < 0))
256 {
257 ZSTD_freeDCtx (dctx);
258 return fail (&state, DWFL_E_ERRNO);
259 }
260 next_in = state.input_buffer;
261 avail_in = n;
262 state.input_pos += n;
263 }
264 if (avail_out == 0)
265 {
266 ptrdiff_t pos = (void *) next_out - state.buffer;
267 if (!bigger_buffer (&state, avail_in))
268 {
269 ZSTD_freeDCtx (dctx);
270 return fail (&state, DWFL_E_NOMEM);
271 }
272 next_out = state.buffer + pos;
273 avail_out = state.size - pos;
274 }
275
276 ZSTD_inBuffer input = { next_in, avail_in, 0 };
277 ZSTD_outBuffer output = { next_out, avail_out, 0 };
278 result = ZSTD_decompressStream (dctx, &output, &input);
279
280 if (! ZSTD_isError (result))
281 {
282 total_out += output.pos;
283 next_out += output.pos;
284 avail_out -= output.pos;
285 next_in += input.pos;
286 avail_in -= input.pos;
287 }
288
289 if (result == 0)
290 break;
291 }
292 while (avail_in > 0 && ! ZSTD_isError (result));
293
294 ZSTD_freeDCtx (dctx);
295
296 if (ZSTD_isError (result))
297 return fail (&state, DWFL_E_ZSTD);
298
299 smaller_buffer (&state, total_out);
300
301#elif USE_INFLATE
Roland McGrathae1d7dc2009-08-26 01:27:59 -0700302
303 /* This style actually only works with bzlib and liblzma.
304 The stupid zlib interface has nothing to grok the
305 gzip file headers except the slow gzFile interface. */
306
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800307 z_stream z = { .next_in = mapped, .avail_in = state.mapped_size };
Roland McGrathae1d7dc2009-08-26 01:27:59 -0700308 int result = inflateInit (&z);
309 if (result != Z (OK))
Roland McGrath24169642009-08-26 02:26:34 -0700310 {
311 inflateEnd (&z);
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800312 return zlib_fail (&state, result);
Roland McGrath24169642009-08-26 02:26:34 -0700313 }
Roland McGrathae1d7dc2009-08-26 01:27:59 -0700314
315 do
Roland McGrathbca43152009-01-05 23:59:32 -0800316 {
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800317 if (z.avail_in == 0 && state.input_buffer != NULL)
Roland McGrathae1d7dc2009-08-26 01:27:59 -0700318 {
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800319 ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE,
320 start_offset + state.input_pos);
Roland McGrathae1d7dc2009-08-26 01:27:59 -0700321 if (unlikely (n < 0))
Roland McGrath24169642009-08-26 02:26:34 -0700322 {
323 inflateEnd (&z);
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800324 return zlib_fail (&state, Z (ERRNO));
Roland McGrath24169642009-08-26 02:26:34 -0700325 }
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800326 z.next_in = state.input_buffer;
Roland McGrathae1d7dc2009-08-26 01:27:59 -0700327 z.avail_in = n;
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800328 state.input_pos += n;
Roland McGrathae1d7dc2009-08-26 01:27:59 -0700329 }
330 if (z.avail_out == 0)
Roland McGrathbca43152009-01-05 23:59:32 -0800331 {
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800332 ptrdiff_t pos = (void *) z.next_out - state.buffer;
333 if (!bigger_buffer (&state, z.avail_in))
Roland McGrathbca43152009-01-05 23:59:32 -0800334 {
335 result = Z (MEM_ERROR);
336 break;
337 }
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800338 z.next_out = state.buffer + pos;
339 z.avail_out = state.size - pos;
Roland McGrathbca43152009-01-05 23:59:32 -0800340 }
Roland McGrathae1d7dc2009-08-26 01:27:59 -0700341 }
Roland McGrath24169642009-08-26 02:26:34 -0700342 while ((result = do_inflate (&z)) == Z (OK));
Roland McGrathbca43152009-01-05 23:59:32 -0800343
344#ifdef BZLIB
Roland McGrathae1d7dc2009-08-26 01:27:59 -0700345 uint64_t total_out = (((uint64_t) z.total_out_hi32 << 32)
346 | z.total_out_lo32);
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800347 smaller_buffer (&state, total_out);
Roland McGrathbca43152009-01-05 23:59:32 -0800348#else
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800349 smaller_buffer (&state, z.total_out);
Roland McGrathbca43152009-01-05 23:59:32 -0800350#endif
351
Roland McGrathae1d7dc2009-08-26 01:27:59 -0700352 inflateEnd (&z);
Roland McGrathbca43152009-01-05 23:59:32 -0800353
Roland McGrathae1d7dc2009-08-26 01:27:59 -0700354 if (result != Z (STREAM_END))
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800355 return zlib_fail (&state, result);
Roland McGratha6ef1dc2009-01-06 00:54:49 -0800356
Roland McGrathae1d7dc2009-08-26 01:27:59 -0700357#else /* gzip only. */
358
359 /* Let the decompression library read the file directly. */
360
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800361 Dwfl_Error result = open_stream (fd, start_offset, &state);
Roland McGrathc4ac8612009-01-14 03:01:49 -0800362
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800363 if (result == DWFL_E_NOERROR && gzdirect (state.zf))
Roland McGrathae1d7dc2009-08-26 01:27:59 -0700364 {
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800365 gzclose (state.zf);
Mark Wielaard7ea82242019-08-12 00:43:22 +0200366 /* Not a compressed stream after all. */
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800367 return fail (&state, DWFL_E_BADELF);
Roland McGrathbca43152009-01-05 23:59:32 -0800368 }
369
Roland McGrathae1d7dc2009-08-26 01:27:59 -0700370 if (result != DWFL_E_NOERROR)
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800371 return fail (&state, result);
Roland McGrathae1d7dc2009-08-26 01:27:59 -0700372
373 ptrdiff_t pos = 0;
374 while (1)
375 {
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800376 if (!bigger_buffer (&state, 1024))
Roland McGrathae1d7dc2009-08-26 01:27:59 -0700377 {
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800378 gzclose (state.zf);
379 return zlib_fail (&state, Z (MEM_ERROR));
Roland McGrathae1d7dc2009-08-26 01:27:59 -0700380 }
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800381 int n = gzread (state.zf, state.buffer + pos, state.size - pos);
Roland McGrathae1d7dc2009-08-26 01:27:59 -0700382 if (n < 0)
383 {
384 int code;
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800385 gzerror (state.zf, &code);
386 gzclose (state.zf);
387 return zlib_fail (&state, code);
Roland McGrathae1d7dc2009-08-26 01:27:59 -0700388 }
389 if (n == 0)
390 break;
391 pos += n;
392 }
393
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800394 gzclose (state.zf);
395 smaller_buffer (&state, pos);
Roland McGrathae1d7dc2009-08-26 01:27:59 -0700396#endif
397
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800398 free (state.input_buffer);
Roland McGrath6bb90712009-08-27 12:36:47 -0700399
Chih-Hung Hsieh6bbea3d2015-11-13 11:48:07 -0800400 *state.whole = state.buffer;
401 *whole_size = state.size;
Roland McGrathbca43152009-01-05 23:59:32 -0800402
Roland McGratha6ef1dc2009-01-06 00:54:49 -0800403 return DWFL_E_NOERROR;
Roland McGrathbca43152009-01-05 23:59:32 -0800404}