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)120 libdw_open_elf (int *fdp, Elf **elfp, bool close_on_fail, bool archive_ok,
121 bool never_close_fd)
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 if (error != DWFL_E_NOERROR)
168 {
169 elf_end (elf);
170 elf = NULL;
171 }
172
173 if (! never_close_fd
174 && error == DWFL_E_NOERROR ? may_close_fd : close_on_fail)
175 {
176 close (*fdp);
177 *fdp = -1;
178 }
179
180 *elfp = elf;
181 return error;
182 }
183
184 Dwfl_Error internal_function
__libdw_open_file(int * fdp,Elf ** elfp,bool close_on_fail,bool archive_ok)185 __libdw_open_file (int *fdp, Elf **elfp, bool close_on_fail, bool archive_ok)
186 {
187 return libdw_open_elf (fdp, elfp, close_on_fail, archive_ok, false);
188 }
189
190 Dwfl_Error internal_function
__libdw_open_elf(int fd,Elf ** elfp)191 __libdw_open_elf (int fd, Elf **elfp)
192 {
193 return libdw_open_elf (&fd, elfp, false, true, true);
194 }
195