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 #ifdef HAVE_CONFIG_H
30 # include <config.h>
31 #endif
32 
33 #include "../libelf/libelfP.h"
34 #undef	_
35 #include "libdwflP.h"
36 
37 #include <unistd.h>
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.  */
48 static Dwfl_Error
decompress(int fd,Elf ** elf)49 decompress (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   const off_t offset = (*elf)->start_offset;
56   void *const mapped = ((*elf)->map_address == NULL ? NULL
57 			: (*elf)->map_address + offset);
58   const size_t mapped_size = (*elf)->maximum_size;
59   if (mapped_size == 0)
60     return error;
61 
62   error = __libdw_gunzip (fd, offset, mapped, mapped_size, &buffer, &size);
63   if (error == DWFL_E_BADELF)
64     error = __libdw_bunzip2 (fd, offset, mapped, mapped_size, &buffer, &size);
65   if (error == DWFL_E_BADELF)
66     error = __libdw_unlzma (fd, offset, mapped, mapped_size, &buffer, &size);
67 
68   if (error == DWFL_E_NOERROR)
69     {
70       if (unlikely (size == 0))
71 	{
72 	  error = DWFL_E_BADELF;
73 	  free (buffer);
74 	}
75       else
76 	{
77 	  Elf *memelf = elf_memory (buffer, size);
78 	  if (memelf == NULL)
79 	    {
80 	      error = DWFL_E_LIBELF;
81 	      free (buffer);
82 	    }
83 	  else
84 	    {
85 	      memelf->flags |= ELF_F_MALLOCED;
86 	      elf_end (*elf);
87 	      *elf = memelf;
88 	    }
89 	}
90     }
91   else
92     free (buffer);
93 
94   return error;
95 }
96 
97 static Dwfl_Error
what_kind(int fd,Elf ** elfp,Elf_Kind * kind,bool * may_close_fd)98 what_kind (int fd, Elf **elfp, Elf_Kind *kind, bool *may_close_fd)
99 {
100   Dwfl_Error error = DWFL_E_NOERROR;
101   *kind = elf_kind (*elfp);
102   if (unlikely (*kind == ELF_K_NONE))
103     {
104       if (unlikely (*elfp == NULL))
105 	error = DWFL_E_LIBELF;
106       else
107 	{
108 	  error = decompress (fd, elfp);
109 	  if (error == DWFL_E_NOERROR)
110 	    {
111 	      *may_close_fd = true;
112 	      *kind = elf_kind (*elfp);
113 	    }
114 	}
115     }
116   return error;
117 }
118 
119 static Dwfl_Error
libdw_open_elf(int * fdp,Elf ** elfp,bool close_on_fail,bool archive_ok,bool never_close_fd,bool bad_elf_ok)120 libdw_open_elf (int *fdp, Elf **elfp, bool close_on_fail, bool archive_ok,
121 		bool never_close_fd, bool bad_elf_ok)
122 {
123   bool may_close_fd = false;
124 
125   Elf *elf = elf_begin (*fdp, ELF_C_READ_MMAP_PRIVATE, NULL);
126 
127   Elf_Kind kind;
128   Dwfl_Error error = what_kind (*fdp, &elf, &kind, &may_close_fd);
129   if (error == DWFL_E_BADELF)
130     {
131       /* It's not an ELF file or a compressed file.
132 	 See if it's an image with a header preceding the real file.  */
133 
134       off_t offset = elf->start_offset;
135       error = __libdw_image_header (*fdp, &offset,
136 				    (elf->map_address == NULL ? NULL
137 				     : elf->map_address + offset),
138 				    elf->maximum_size);
139       if (error == DWFL_E_NOERROR)
140 	{
141 	  /* Pure evil.  libelf needs some better interfaces.  */
142 	  elf->kind = ELF_K_AR;
143 	  elf->state.ar.elf_ar_hdr.ar_name = "libdwfl is faking you out";
144 	  elf->state.ar.elf_ar_hdr.ar_size = elf->maximum_size - offset;
145 	  elf->state.ar.offset = offset - sizeof (struct ar_hdr);
146 	  Elf *subelf = elf_begin (-1, ELF_C_READ_MMAP_PRIVATE, elf);
147 	  elf->kind = ELF_K_NONE;
148 	  if (unlikely (subelf == NULL))
149 	    error = DWFL_E_LIBELF;
150 	  else
151 	    {
152 	      subelf->parent = NULL;
153 	      subelf->flags |= elf->flags & (ELF_F_MMAPPED | ELF_F_MALLOCED);
154 	      elf->flags &= ~(ELF_F_MMAPPED | ELF_F_MALLOCED);
155 	      elf_end (elf);
156 	      elf = subelf;
157 	      error = what_kind (*fdp, &elf, &kind, &may_close_fd);
158 	    }
159 	}
160     }
161 
162   if (error == DWFL_E_NOERROR
163       && kind != ELF_K_ELF
164       && !(archive_ok && kind == ELF_K_AR))
165     error = DWFL_E_BADELF;
166 
167   /* This basically means, we keep a ELF_K_NONE Elf handle and return it.  */
168   if (bad_elf_ok && error == DWFL_E_BADELF)
169     error = DWFL_E_NOERROR;
170 
171   if (error != DWFL_E_NOERROR)
172     {
173       elf_end (elf);
174       elf = NULL;
175     }
176 
177   if (! never_close_fd
178       && error == DWFL_E_NOERROR ? may_close_fd : close_on_fail)
179     {
180       close (*fdp);
181       *fdp = -1;
182     }
183 
184   *elfp = elf;
185   return error;
186 }
187 
188 Dwfl_Error internal_function
__libdw_open_file(int * fdp,Elf ** elfp,bool close_on_fail,bool archive_ok)189 __libdw_open_file (int *fdp, Elf **elfp, bool close_on_fail, bool archive_ok)
190 {
191   return libdw_open_elf (fdp, elfp, close_on_fail, archive_ok, false, false);
192 }
193 
194 Dwfl_Error internal_function
__libdw_open_elf(int fd,Elf ** elfp)195 __libdw_open_elf (int fd, Elf **elfp)
196 {
197   return libdw_open_elf (&fd, elfp, false, true, true, true);
198 }
199