Branch data Line data Source code
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
51 : 9 : decompress (int fd __attribute__ ((unused)), Elf **elf)
52 : : {
53 : 9 : Dwfl_Error error = DWFL_E_BADELF;
54 : : /* ELF cannot be decompressed, if there is no file descriptor. */
55 [ - + ]: 9 : if (fd == -1)
56 : : return error;
57 : 9 : void *buffer = NULL;
58 : 9 : size_t size = 0;
59 : :
60 : 9 : const off_t offset = (*elf)->start_offset;
61 : 18 : void *const mapped = ((*elf)->map_address == NULL ? NULL
62 [ + - ]: 9 : : (*elf)->map_address + offset);
63 : 9 : const size_t mapped_size = (*elf)->maximum_size;
64 [ - + ]: 9 : if (mapped_size == 0)
65 : : return error;
66 : :
67 : 9 : error = __libdw_gunzip (fd, offset, mapped, mapped_size, &buffer, &size);
68 [ + - ]: 9 : if (error == DWFL_E_BADELF)
69 : 9 : error = __libdw_bunzip2 (fd, offset, mapped, mapped_size, &buffer, &size);
70 [ + + ]: 9 : if (error == DWFL_E_BADELF)
71 : 7 : error = __libdw_unlzma (fd, offset, mapped, mapped_size, &buffer, &size);
72 [ + + ]: 7 : if (error == DWFL_E_BADELF)
73 : 2 : error = __libdw_unzstd (fd, offset, mapped, mapped_size, &buffer, &size);
74 : :
75 [ + - ]: 9 : if (error == DWFL_E_NOERROR)
76 : : {
77 [ - + ]: 9 : if (unlikely (size == 0))
78 : : {
79 : 0 : error = DWFL_E_BADELF;
80 : 0 : free (buffer);
81 : : }
82 : : else
83 : : {
84 : 9 : Elf *memelf = elf_memory (buffer, size);
85 [ - + ]: 9 : if (memelf == NULL)
86 : : {
87 : 0 : error = DWFL_E_LIBELF;
88 : 0 : free (buffer);
89 : : }
90 : : else
91 : : {
92 : 9 : memelf->flags |= ELF_F_MALLOCED;
93 : 9 : elf_end (*elf);
94 : 9 : *elf = memelf;
95 : : }
96 : : }
97 : : }
98 : : else
99 : 0 : free (buffer);
100 : :
101 : : return error;
102 : : }
103 : :
104 : : static Dwfl_Error
105 : 5966 : what_kind (int fd, Elf **elfp, Elf_Kind *kind, bool *may_close_fd)
106 : : {
107 : 5966 : Dwfl_Error error = DWFL_E_NOERROR;
108 : 5966 : *kind = elf_kind (*elfp);
109 [ + + ]: 5966 : if (unlikely (*kind == ELF_K_NONE))
110 : : {
111 [ + - ]: 9 : if (unlikely (*elfp == NULL))
112 : : error = DWFL_E_LIBELF;
113 : : else
114 : : {
115 : 9 : error = decompress (fd, elfp);
116 [ + - ]: 9 : if (error == DWFL_E_NOERROR)
117 : : {
118 : 9 : *may_close_fd = true;
119 : 9 : *kind = elf_kind (*elfp);
120 : : }
121 : : }
122 : : }
123 : 5966 : return error;
124 : : }
125 : :
126 : : static Dwfl_Error
127 : 5966 : 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 : 5966 : bool may_close_fd = false;
131 : :
132 : 11932 : Elf *elf =
133 [ + + ]: 5966 : use_elfp ? *elfp : elf_begin (*fdp, ELF_C_READ_MMAP_PRIVATE, NULL);
134 : :
135 : 5966 : Elf_Kind kind;
136 : 5966 : Dwfl_Error error = what_kind (*fdp, &elf, &kind, &may_close_fd);
137 [ - + ]: 5966 : 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 : 0 : off_t offset = elf->start_offset;
143 : 0 : error = __libdw_image_header (*fdp, &offset,
144 [ # # ]: 0 : (elf->map_address == NULL ? NULL
145 : 0 : : elf->map_address + offset),
146 : : elf->maximum_size);
147 [ # # ]: 0 : if (error == DWFL_E_NOERROR)
148 : : {
149 : : /* Pure evil. libelf needs some better interfaces. */
150 : 0 : elf->kind = ELF_K_AR;
151 : 0 : elf->state.ar.elf_ar_hdr.ar_name = "libdwfl is faking you out";
152 : 0 : elf->state.ar.elf_ar_hdr.ar_size = elf->maximum_size - offset;
153 : 0 : elf->state.ar.offset = offset - sizeof (struct ar_hdr);
154 : 0 : Elf *subelf = elf_begin (-1, ELF_C_READ_MMAP_PRIVATE, elf);
155 : 0 : elf->kind = ELF_K_NONE;
156 [ # # ]: 0 : if (unlikely (subelf == NULL))
157 : : error = DWFL_E_LIBELF;
158 : : else
159 : : {
160 : 0 : subelf->parent = NULL;
161 : 0 : subelf->flags |= elf->flags & (ELF_F_MMAPPED | ELF_F_MALLOCED);
162 : 0 : elf->flags &= ~(ELF_F_MMAPPED | ELF_F_MALLOCED);
163 : 0 : elf_end (elf);
164 : 0 : elf = subelf;
165 : 0 : error = what_kind (*fdp, &elf, &kind, &may_close_fd);
166 : : }
167 : : }
168 : : }
169 : :
170 [ + - ]: 5966 : if (error == DWFL_E_NOERROR
171 [ + + ]: 5966 : && kind != ELF_K_ELF
172 [ + - + - ]: 1 : && !(archive_ok && kind == ELF_K_AR))
173 : 5966 : error = DWFL_E_BADELF;
174 : :
175 : : /* This basically means, we keep a ELF_K_NONE Elf handle and return it. */
176 [ + - ]: 5966 : if (bad_elf_ok && error == DWFL_E_BADELF)
177 : : error = DWFL_E_NOERROR;
178 : :
179 [ - + ]: 5966 : if (error != DWFL_E_NOERROR)
180 : : {
181 : 0 : elf_end (elf);
182 : 0 : elf = NULL;
183 : : }
184 : :
185 [ + + ]: 5966 : if (! never_close_fd
186 [ + + ]: 5966 : && error == DWFL_E_NOERROR ? may_close_fd : close_on_fail)
187 : : {
188 : 7 : close (*fdp);
189 : 7 : *fdp = -1;
190 : : }
191 : :
192 : 5966 : *elfp = elf;
193 : 5966 : return error;
194 : : }
195 : :
196 : : Dwfl_Error internal_function
197 : 5864 : __libdw_open_file (int *fdp, Elf **elfp, bool close_on_fail, bool archive_ok)
198 : : {
199 : 5864 : return libdw_open_elf (fdp, elfp, close_on_fail, archive_ok, false, false,
200 : : false);
201 : : }
202 : :
203 : : Dwfl_Error internal_function
204 : 4 : __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 : 4 : int fd = -1;
213 : 4 : *elfp = elf_memory (data, size);
214 [ + + ]: 4 : if (unlikely(*elfp == NULL))
215 : : {
216 : : return DWFL_E_LIBELF;
217 : : }
218 : : /* Allow using this ELF as reference for subsequent elf_begin calls. */
219 : 2 : (*elfp)->cmd = ELF_C_READ_MMAP_PRIVATE;
220 : 2 : return libdw_open_elf (&fd, elfp, false, archive_ok, true, false, true);
221 : : }
222 : :
223 : : Dwfl_Error internal_function
224 : 100 : __libdw_open_elf (int fd, Elf **elfp)
225 : : {
226 : 100 : return libdw_open_elf (&fd, elfp, false, true, true, true, false);
227 : : }
|