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