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