1/* Decompression support for libdwfl: zlib (gzip) and/or bzlib (bzip2).
2   Copyright (C) 2009 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#include "../libelf/libelfP.h"
30#undef	_
31#include "libdwflP.h"
32
33#include <unistd.h>
34
35#if !USE_ZLIB
36# define __libdw_gunzip(...)	DWFL_E_BADELF
37#endif
38
39#if !USE_BZLIB
40# define __libdw_bunzip2(...)	DWFL_E_BADELF
41#endif
42
43#if !USE_LZMA
44# define __libdw_unlzma(...)	DWFL_E_BADELF
45#endif
46
47/* Consumes and replaces *ELF only on success.  */
48static Dwfl_Error
49decompress (int fd __attribute__ ((unused)), Elf **elf)
50{
51  Dwfl_Error error = DWFL_E_BADELF;
52  void *buffer = NULL;
53  size_t size = 0;
54
55#if USE_ZLIB || USE_BZLIB || USE_LZMA
56  const off64_t offset = (*elf)->start_offset;
57  void *const mapped = ((*elf)->map_address == NULL ? NULL
58			: (*elf)->map_address + offset);
59  const size_t mapped_size = (*elf)->maximum_size;
60  if (mapped_size == 0)
61    return error;
62
63  error = __libdw_gunzip (fd, offset, mapped, mapped_size, &buffer, &size);
64  if (error == DWFL_E_BADELF)
65    error = __libdw_bunzip2 (fd, offset, mapped, mapped_size, &buffer, &size);
66  if (error == DWFL_E_BADELF)
67    error = __libdw_unlzma (fd, offset, mapped, mapped_size, &buffer, &size);
68#endif
69
70  if (error == DWFL_E_NOERROR)
71    {
72      if (unlikely (size == 0))
73	{
74	  error = DWFL_E_BADELF;
75	  free (buffer);
76	}
77      else
78	{
79	  Elf *memelf = elf_memory (buffer, size);
80	  if (memelf == NULL)
81	    {
82	      error = DWFL_E_LIBELF;
83	      free (buffer);
84	    }
85	  else
86	    {
87	      memelf->flags |= ELF_F_MALLOCED;
88	      elf_end (*elf);
89	      *elf = memelf;
90	    }
91	}
92    }
93  else
94    free (buffer);
95
96  return error;
97}
98
99static Dwfl_Error
100what_kind (int fd, Elf **elfp, Elf_Kind *kind, bool *close_fd)
101{
102  Dwfl_Error error = DWFL_E_NOERROR;
103  *kind = elf_kind (*elfp);
104  if (unlikely (*kind == ELF_K_NONE))
105    {
106      if (unlikely (*elfp == NULL))
107	error = DWFL_E_LIBELF;
108      else
109	{
110	  error = decompress (fd, elfp);
111	  if (error == DWFL_E_NOERROR)
112	    {
113	      *close_fd = true;
114	      *kind = elf_kind (*elfp);
115	    }
116	}
117    }
118  return error;
119}
120
121Dwfl_Error internal_function
122__libdw_open_file (int *fdp, Elf **elfp, bool close_on_fail, bool archive_ok)
123{
124  bool close_fd = false;
125
126  Elf *elf = elf_begin (*fdp, ELF_C_READ_MMAP_PRIVATE, NULL);
127
128  Elf_Kind kind;
129  Dwfl_Error error = what_kind (*fdp, &elf, &kind, &close_fd);
130  if (error == DWFL_E_BADELF)
131    {
132      /* It's not an ELF file or a compressed file.
133	 See if it's an image with a header preceding the real file.  */
134
135      off64_t offset = elf->start_offset;
136      error = __libdw_image_header (*fdp, &offset,
137				    (elf->map_address == NULL ? NULL
138				     : elf->map_address + offset),
139				    elf->maximum_size);
140      if (error == DWFL_E_NOERROR)
141	{
142	  /* Pure evil.  libelf needs some better interfaces.  */
143	  elf->kind = ELF_K_AR;
144	  elf->state.ar.elf_ar_hdr.ar_name = "libdwfl is faking you out";
145	  elf->state.ar.elf_ar_hdr.ar_size = elf->maximum_size - offset;
146	  elf->state.ar.offset = offset - sizeof (struct ar_hdr);
147	  Elf *subelf = elf_begin (-1, ELF_C_READ_MMAP_PRIVATE, elf);
148	  elf->kind = ELF_K_NONE;
149	  if (unlikely (subelf == NULL))
150	    error = DWFL_E_LIBELF;
151	  else
152	    {
153	      subelf->parent = NULL;
154	      subelf->flags |= elf->flags & (ELF_F_MMAPPED | ELF_F_MALLOCED);
155	      elf->flags &= ~(ELF_F_MMAPPED | ELF_F_MALLOCED);
156	      elf_end (elf);
157	      elf = subelf;
158	      error = what_kind (*fdp, &elf, &kind, &close_fd);
159	    }
160	}
161    }
162
163  if (error == DWFL_E_NOERROR
164      && kind != ELF_K_ELF
165      && !(archive_ok && kind == ELF_K_AR))
166    error = DWFL_E_BADELF;
167
168  if (error != DWFL_E_NOERROR)
169    {
170      elf_end (elf);
171      elf = NULL;
172    }
173
174  if (error == DWFL_E_NOERROR ? close_fd : close_on_fail)
175    {
176      close (*fdp);
177      *fdp = -1;
178    }
179
180  *elfp = elf;
181  return error;
182}
183