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