1 /* Decompression support for libdwfl: zlib (gzip), bzlib (bzip2) or lzma (xz).
2 Copyright (C) 2009, 2016 Red Hat, Inc.
3 Copyright (C) 2022 Google LLC
4 This file is part of elfutils.
5
6 This file is free software; you can redistribute it and/or modify
7 it under the terms of either
8
9 * the GNU Lesser General Public License as published by the Free
10 Software Foundation; either version 3 of the License, or (at
11 your option) any later version
12
13 or
14
15 * the GNU General Public License as published by the Free
16 Software Foundation; either version 2 of the License, or (at
17 your option) any later version
18
19 or both in parallel, as here.
20
21 elfutils is distributed in the hope that it will be useful, but
22 WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 General Public License for more details.
25
26 You should have received copies of the GNU General Public License and
27 the GNU Lesser General Public License along with this program. If
28 not, see <http://www.gnu.org/licenses/>. */
29
30 #ifdef HAVE_CONFIG_H
31 # include <config.h>
32 #endif
33
34 #include "libelfP.h"
35 #include "libdwflP.h"
36
37 #if !USE_BZLIB
38 # define __libdw_bunzip2(...) DWFL_E_BADELF
39 #endif
40
41 #if !USE_LZMA
42 # define __libdw_unlzma(...) DWFL_E_BADELF
43 #endif
44
45 #if !USE_ZSTD
46 # define __libdw_unzstd(...) DWFL_E_BADELF
47 #endif
48
49 /* Consumes and replaces *ELF only on success. */
50 static Dwfl_Error
decompress(int fd,Elf ** elf)51 decompress (int fd __attribute__ ((unused)), Elf **elf)
52 {
53 Dwfl_Error error = DWFL_E_BADELF;
54 /* ELF cannot be decompressed, if there is no file descriptor. */
55 if (fd == -1)
56 return error;
57 void *buffer = NULL;
58 size_t size = 0;
59
60 const off_t offset = (*elf)->start_offset;
61 void *const mapped = ((*elf)->map_address == NULL ? NULL
62 : (*elf)->map_address + offset);
63 const size_t mapped_size = (*elf)->maximum_size;
64 if (mapped_size == 0)
65 return error;
66
67 error = __libdw_gunzip (fd, offset, mapped, mapped_size, &buffer, &size);
68 if (error == DWFL_E_BADELF)
69 error = __libdw_bunzip2 (fd, offset, mapped, mapped_size, &buffer, &size);
70 if (error == DWFL_E_BADELF)
71 error = __libdw_unlzma (fd, offset, mapped, mapped_size, &buffer, &size);
72 if (error == DWFL_E_BADELF)
73 error = __libdw_unzstd (fd, offset, mapped, mapped_size, &buffer, &size);
74
75 if (error == DWFL_E_NOERROR)
76 {
77 if (unlikely (size == 0))
78 {
79 error = DWFL_E_BADELF;
80 free (buffer);
81 }
82 else
83 {
84 Elf *memelf = elf_memory (buffer, size);
85 if (memelf == NULL)
86 {
87 error = DWFL_E_LIBELF;
88 free (buffer);
89 }
90 else
91 {
92 memelf->flags |= ELF_F_MALLOCED;
93 elf_end (*elf);
94 *elf = memelf;
95 }
96 }
97 }
98 else
99 free (buffer);
100
101 return error;
102 }
103
104 static Dwfl_Error
what_kind(int fd,Elf ** elfp,Elf_Kind * kind,bool * may_close_fd)105 what_kind (int fd, Elf **elfp, Elf_Kind *kind, bool *may_close_fd)
106 {
107 Dwfl_Error error = DWFL_E_NOERROR;
108 *kind = elf_kind (*elfp);
109 if (unlikely (*kind == ELF_K_NONE))
110 {
111 if (unlikely (*elfp == NULL))
112 error = DWFL_E_LIBELF;
113 else
114 {
115 error = decompress (fd, elfp);
116 if (error == DWFL_E_NOERROR)
117 {
118 *may_close_fd = true;
119 *kind = elf_kind (*elfp);
120 }
121 }
122 }
123 return error;
124 }
125
126 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,bool use_elfp)127 libdw_open_elf (int *fdp, Elf **elfp, bool close_on_fail, bool archive_ok,
128 bool never_close_fd, bool bad_elf_ok, bool use_elfp)
129 {
130 bool may_close_fd = false;
131
132 Elf *elf =
133 use_elfp ? *elfp : elf_begin (*fdp, ELF_C_READ_MMAP_PRIVATE, NULL);
134
135 Elf_Kind kind;
136 Dwfl_Error error = what_kind (*fdp, &elf, &kind, &may_close_fd);
137 if (error == DWFL_E_BADELF)
138 {
139 /* It's not an ELF file or a compressed file.
140 See if it's an image with a header preceding the real file. */
141
142 off_t offset = elf->start_offset;
143 error = __libdw_image_header (*fdp, &offset,
144 (elf->map_address == NULL ? NULL
145 : elf->map_address + offset),
146 elf->maximum_size);
147 if (error == DWFL_E_NOERROR)
148 {
149 /* Pure evil. libelf needs some better interfaces. */
150 elf->kind = ELF_K_AR;
151 elf->state.ar.elf_ar_hdr.ar_name = "libdwfl is faking you out";
152 elf->state.ar.elf_ar_hdr.ar_size = elf->maximum_size - offset;
153 elf->state.ar.offset = offset - sizeof (struct ar_hdr);
154 Elf *subelf = elf_begin (-1, elf->cmd, elf);
155 elf->kind = ELF_K_NONE;
156 if (unlikely (subelf == NULL))
157 error = DWFL_E_LIBELF;
158 else
159 {
160 subelf->parent = NULL;
161 subelf->flags |= elf->flags & (ELF_F_MMAPPED | ELF_F_MALLOCED);
162 elf->flags &= ~(ELF_F_MMAPPED | ELF_F_MALLOCED);
163 elf_end (elf);
164 elf = subelf;
165 error = what_kind (*fdp, &elf, &kind, &may_close_fd);
166 }
167 }
168 }
169
170 if (error == DWFL_E_NOERROR
171 && kind != ELF_K_ELF
172 && !(archive_ok && kind == ELF_K_AR))
173 error = DWFL_E_BADELF;
174
175 /* This basically means, we keep a ELF_K_NONE Elf handle and return it. */
176 if (bad_elf_ok && error == DWFL_E_BADELF)
177 error = DWFL_E_NOERROR;
178
179 if (error != DWFL_E_NOERROR)
180 {
181 elf_end (elf);
182 elf = NULL;
183 }
184
185 if (! never_close_fd
186 && error == DWFL_E_NOERROR ? may_close_fd : close_on_fail)
187 {
188 close (*fdp);
189 *fdp = -1;
190 }
191
192 *elfp = elf;
193 return error;
194 }
195
196 Dwfl_Error internal_function
__libdw_open_file(int * fdp,Elf ** elfp,bool close_on_fail,bool archive_ok)197 __libdw_open_file (int *fdp, Elf **elfp, bool close_on_fail, bool archive_ok)
198 {
199 return libdw_open_elf (fdp, elfp, close_on_fail, archive_ok, false, false,
200 false);
201 }
202
203 Dwfl_Error internal_function
__libdw_open_elf_memory(char * data,size_t size,Elf ** elfp,bool archive_ok)204 __libdw_open_elf_memory (char *data, size_t size, Elf **elfp, bool archive_ok)
205 {
206 /* It is ok to use `fd == -1` here, because libelf uses it as a value for
207 "no file opened" and code supports working with this value, and also
208 `never_close_fd == false` is passed to prevent closing non-existent file.
209 The only caveat is in `decompress` method, which doesn't support
210 decompressing from memory, so reading compressed zImage using this method
211 won't work. */
212 int fd = -1;
213 *elfp = elf_memory (data, size);
214 if (unlikely(*elfp == NULL))
215 {
216 return DWFL_E_LIBELF;
217 }
218 return libdw_open_elf (&fd, elfp, false, archive_ok, true, false, true);
219 }
220
221 Dwfl_Error internal_function
__libdw_open_elf(int fd,Elf ** elfp)222 __libdw_open_elf (int fd, Elf **elfp)
223 {
224 return libdw_open_elf (&fd, elfp, false, true, true, true, false);
225 }
226