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.  */
44 static Dwfl_Error
decompress(int fd,Elf ** elf)45 decompress (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 
93 static Dwfl_Error
what_kind(int fd,Elf ** elfp,Elf_Kind * kind,bool * close_fd)94 what_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 
115 Dwfl_Error internal_function
__libdw_open_file(int * fdp,Elf ** elfp,bool close_on_fail,bool archive_ok)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