/src/elfutils/libdwfl/dwfl_segment_report_module.c
Line | Count | Source |
1 | | /* Sniff out modules from ELF headers visible in memory segments. |
2 | | Copyright (C) 2008-2012, 2014, 2015, 2018 Red Hat, Inc. |
3 | | Copyright (C) 2021 Mark J. Wielaard <mark@klomp.org> |
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 | | #include <config.h> |
31 | | #include "libelfP.h" /* For NOTE_ALIGN4 and NOTE_ALIGN8. */ |
32 | | #include "libdwflP.h" |
33 | | #include "common.h" |
34 | | |
35 | | #include <elf.h> |
36 | | #include <gelf.h> |
37 | | #include <inttypes.h> |
38 | | #include <fcntl.h> |
39 | | |
40 | | #ifdef HAVE_OPENAT2_RESOLVE_IN_ROOT |
41 | | #include <linux/openat2.h> |
42 | | #include <sys/syscall.h> |
43 | | #include <unistd.h> |
44 | | #endif |
45 | | |
46 | | #include <system.h> |
47 | | |
48 | | |
49 | | /* A good size for the initial read from memory, if it's not too costly. |
50 | | This more than covers the phdrs and note segment in the average 64-bit |
51 | | binary. */ |
52 | | |
53 | 373k | #define INITIAL_READ 1024 |
54 | | |
55 | | #if BYTE_ORDER == LITTLE_ENDIAN |
56 | 586k | # define MY_ELFDATA ELFDATA2LSB |
57 | | #else |
58 | | # define MY_ELFDATA ELFDATA2MSB |
59 | | #endif |
60 | | |
61 | | struct elf_build_id |
62 | | { |
63 | | void *memory; |
64 | | size_t len; |
65 | | GElf_Addr vaddr; |
66 | | }; |
67 | | |
68 | | struct read_state |
69 | | { |
70 | | Dwfl *dwfl; |
71 | | Dwfl_Memory_Callback *memory_callback; |
72 | | void *memory_callback_arg; |
73 | | void **buffer; |
74 | | size_t *buffer_available; |
75 | | }; |
76 | | |
77 | | /* Return user segment index closest to ADDR but not above it. |
78 | | If NEXT, return the closest to ADDR but not below it. */ |
79 | | static int |
80 | | addr_segndx (Dwfl *dwfl, size_t segment, GElf_Addr addr, bool next) |
81 | 197k | { |
82 | 197k | int ndx = -1; |
83 | 197k | do |
84 | 2.06M | { |
85 | 2.06M | if (dwfl->lookup_segndx[segment] >= 0) |
86 | 1.00M | ndx = dwfl->lookup_segndx[segment]; |
87 | 2.06M | if (++segment >= dwfl->lookup_elts - 1) |
88 | 85.2k | return next ? ndx + 1 : ndx; |
89 | 2.06M | } |
90 | 1.98M | while (dwfl->lookup_addr[segment] < addr); |
91 | | |
92 | 111k | if (next) |
93 | 0 | { |
94 | 0 | while (dwfl->lookup_segndx[segment] < 0) |
95 | 0 | if (++segment >= dwfl->lookup_elts - 1) |
96 | 0 | return ndx + 1; |
97 | 0 | ndx = dwfl->lookup_segndx[segment]; |
98 | 0 | } |
99 | | |
100 | 111k | return ndx; |
101 | 111k | } |
102 | | |
103 | | /* Return whether there is SZ bytes available at PTR till END. */ |
104 | | |
105 | | static bool |
106 | | buf_has_data (const void *ptr, const void *end, size_t sz) |
107 | 488k | { |
108 | 488k | return ptr < end && (size_t) (end - ptr) >= sz; |
109 | 488k | } |
110 | | |
111 | | /* Read SZ bytes into *RETP from *PTRP (limited by END) in format EI_DATA. |
112 | | Function comes from src/readelf.c . */ |
113 | | |
114 | | static bool |
115 | | buf_read_ulong (unsigned char ei_data, size_t sz, |
116 | | const void **ptrp, const void *end, uint64_t *retp) |
117 | 488k | { |
118 | 488k | if (! buf_has_data (*ptrp, end, sz)) |
119 | 449 | return false; |
120 | | |
121 | 487k | union |
122 | 487k | { |
123 | 487k | uint64_t u64; |
124 | 487k | uint32_t u32; |
125 | 487k | } u; |
126 | | |
127 | 487k | memcpy (&u, *ptrp, sz); |
128 | 487k | (*ptrp) += sz; |
129 | | |
130 | 487k | if (retp == NULL) |
131 | 4.43k | return true; |
132 | | |
133 | 483k | if (MY_ELFDATA != ei_data) |
134 | 472k | { |
135 | 472k | if (sz == 4) |
136 | 469k | CONVERT (u.u32); |
137 | 2.52k | else |
138 | 2.52k | CONVERT (u.u64); |
139 | 472k | } |
140 | 483k | if (sz == 4) |
141 | 470k | *retp = u.u32; |
142 | 12.7k | else |
143 | 12.7k | *retp = u.u64; |
144 | 483k | return true; |
145 | 487k | } |
146 | | |
147 | | /* Try to find matching entry for module from address MODULE_START to |
148 | | MODULE_END in NT_FILE note located at NOTE_FILE of NOTE_FILE_SIZE |
149 | | bytes in format EI_CLASS and EI_DATA. */ |
150 | | |
151 | | static const char * |
152 | | handle_file_note (GElf_Addr module_start, GElf_Addr module_end, |
153 | | unsigned char ei_class, unsigned char ei_data, |
154 | | const void *note_file, size_t note_file_size) |
155 | 41.9k | { |
156 | 41.9k | if (note_file == NULL) |
157 | 37.0k | return NULL; |
158 | | |
159 | 4.88k | size_t sz; |
160 | 4.88k | switch (ei_class) |
161 | 4.88k | { |
162 | 4.01k | case ELFCLASS32: |
163 | 4.01k | sz = 4; |
164 | 4.01k | break; |
165 | 870 | case ELFCLASS64: |
166 | 870 | sz = 8; |
167 | 870 | break; |
168 | 0 | default: |
169 | 0 | return NULL; |
170 | 4.88k | } |
171 | | |
172 | 4.88k | const void *ptr = note_file; |
173 | 4.88k | const void *end = note_file + note_file_size; |
174 | 4.88k | uint64_t count; |
175 | 4.88k | if (! buf_read_ulong (ei_data, sz, &ptr, end, &count)) |
176 | 195 | return NULL; |
177 | 4.68k | if (! buf_read_ulong (ei_data, sz, &ptr, end, NULL)) // page_size |
178 | 254 | return NULL; |
179 | | |
180 | 4.43k | uint64_t maxcount = (size_t) (end - ptr) / (3 * sz); |
181 | 4.43k | if (count > maxcount) |
182 | 646 | return NULL; |
183 | | |
184 | | /* Where file names are stored. */ |
185 | 3.78k | const char *fptr = ptr + 3 * count * sz; |
186 | | |
187 | 3.78k | ssize_t firstix = -1; |
188 | 3.78k | ssize_t lastix = -1; |
189 | 162k | for (size_t mix = 0; mix < count; mix++) |
190 | 159k | { |
191 | 159k | uint64_t mstart, mend, moffset; |
192 | 159k | if (! buf_read_ulong (ei_data, sz, &ptr, fptr, &mstart) |
193 | 159k | || ! buf_read_ulong (ei_data, sz, &ptr, fptr, &mend) |
194 | 159k | || ! buf_read_ulong (ei_data, sz, &ptr, fptr, &moffset)) |
195 | 0 | return NULL; |
196 | 159k | if (mstart == module_start && moffset == 0) |
197 | 3.77k | firstix = lastix = mix; |
198 | 159k | if (firstix != -1 && mstart < module_end) |
199 | 140k | lastix = mix; |
200 | 159k | if (mend >= module_end) |
201 | 876 | break; |
202 | 159k | } |
203 | 3.78k | if (firstix == -1) |
204 | 1.26k | return NULL; |
205 | | |
206 | 2.52k | const char *retval = NULL; |
207 | 83.5k | for (ssize_t mix = 0; mix <= lastix; mix++) |
208 | 81.9k | { |
209 | 81.9k | const char *fnext = memchr (fptr, 0, (const char *) end - fptr); |
210 | 81.9k | if (fnext == NULL) |
211 | 395 | return NULL; |
212 | 81.5k | if (mix == firstix) |
213 | 2.14k | retval = fptr; |
214 | 81.5k | if (firstix < mix && mix <= lastix && strcmp (fptr, retval) != 0) |
215 | 460 | return NULL; |
216 | 81.0k | fptr = fnext + 1; |
217 | 81.0k | } |
218 | 1.66k | return retval; |
219 | 2.52k | } |
220 | | |
221 | | /* Return true iff we are certain ELF cannot match BUILD_ID of |
222 | | BUILD_ID_LEN bytes. Pass DISK_FILE_HAS_BUILD_ID as false if it is |
223 | | certain ELF does not contain build-id (it is only a performance hit |
224 | | to pass it always as true). */ |
225 | | |
226 | | static bool |
227 | | invalid_elf (Elf *elf, bool disk_file_has_build_id, |
228 | | struct elf_build_id *build_id) |
229 | 2.33k | { |
230 | 2.33k | if (! disk_file_has_build_id && build_id->len > 0) |
231 | 0 | { |
232 | | /* Module found in segments with build-id is more reliable |
233 | | than a module found via DT_DEBUG on disk without any |
234 | | build-id. */ |
235 | 0 | return true; |
236 | 0 | } |
237 | 2.33k | if (disk_file_has_build_id && build_id->len > 0) |
238 | 1.99k | { |
239 | 1.99k | const void *elf_build_id; |
240 | 1.99k | ssize_t elf_build_id_len; |
241 | | |
242 | | /* If there is a build id in the elf file, check it. */ |
243 | 1.99k | elf_build_id_len = INTUSE(dwelf_elf_gnu_build_id) (elf, &elf_build_id); |
244 | 1.99k | if (elf_build_id_len > 0) |
245 | 1.99k | { |
246 | 1.99k | if (build_id->len != (size_t) elf_build_id_len |
247 | 1.20k | || memcmp (build_id->memory, elf_build_id, build_id->len) != 0) |
248 | 1.99k | return true; |
249 | 1.99k | } |
250 | 1.99k | } |
251 | 340 | return false; |
252 | 2.33k | } |
253 | | |
254 | | static void |
255 | | finish_portion (struct read_state *read_state, |
256 | | void **data, size_t *data_size) |
257 | 232k | { |
258 | 232k | if (*data_size != 0 && *data != NULL) |
259 | 4.11k | (*read_state->memory_callback) (read_state->dwfl, -1, data, data_size, |
260 | 4.11k | 0, 0, read_state->memory_callback_arg); |
261 | 232k | } |
262 | | |
263 | | static inline bool |
264 | | read_portion (struct read_state *read_state, |
265 | | void **data, size_t *data_size, |
266 | | GElf_Addr start, size_t segment, |
267 | | GElf_Addr vaddr, size_t filesz) |
268 | 308k | { |
269 | | /* Check whether we will have to read the segment data, or if it |
270 | | can be returned from the existing buffer. */ |
271 | 308k | if (filesz > *read_state->buffer_available |
272 | 180k | || vaddr - start > *read_state->buffer_available - filesz |
273 | | /* If we're in string mode, then don't consider the buffer we have |
274 | | sufficient unless it contains the terminator of the string. */ |
275 | 162k | || (filesz == 0 && memchr (vaddr - start + *read_state->buffer, '\0', |
276 | 1.72k | (*read_state->buffer_available |
277 | 1.72k | - (vaddr - start))) == NULL)) |
278 | 146k | { |
279 | 146k | *data = NULL; |
280 | 146k | *data_size = filesz; |
281 | 146k | return !(*read_state->memory_callback) (read_state->dwfl, |
282 | 146k | addr_segndx (read_state->dwfl, |
283 | 146k | segment, vaddr, |
284 | 146k | false), |
285 | 146k | data, data_size, vaddr, filesz, |
286 | 146k | read_state->memory_callback_arg); |
287 | 146k | } |
288 | | |
289 | | /* We already have this whole note segment from our initial read. */ |
290 | 162k | *data = vaddr - start + (*read_state->buffer); |
291 | 162k | *data_size = 0; |
292 | 162k | return false; |
293 | 308k | } |
294 | | |
295 | | int |
296 | | dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name, |
297 | | const char *executable, |
298 | | Dwfl_Memory_Callback *memory_callback, |
299 | | void *memory_callback_arg, |
300 | | Dwfl_Module_Callback *read_eagerly, |
301 | | void *read_eagerly_arg, |
302 | | size_t maxread, |
303 | | const void *note_file, size_t note_file_size, |
304 | | const struct r_debug_info *r_debug_info) |
305 | 495k | { |
306 | 495k | size_t segment = ndx; |
307 | 495k | struct read_state read_state; |
308 | | |
309 | 495k | if (segment >= dwfl->lookup_elts) |
310 | 421k | segment = dwfl->lookup_elts - 1; |
311 | | |
312 | 22.6M | while (segment > 0 |
313 | 22.5M | && (dwfl->lookup_segndx[segment] > ndx |
314 | 12.3M | || dwfl->lookup_segndx[segment] == -1)) |
315 | 22.1M | --segment; |
316 | | |
317 | 19.5M | while (dwfl->lookup_segndx[segment] < ndx) |
318 | 19.1M | if (++segment == dwfl->lookup_elts) |
319 | 121k | return 0; |
320 | | |
321 | 373k | GElf_Addr start = dwfl->lookup_addr[segment]; |
322 | | |
323 | | /* First read in the file header and check its sanity. */ |
324 | | |
325 | 373k | void *buffer = NULL; |
326 | 373k | size_t buffer_available = INITIAL_READ; |
327 | 373k | Elf *elf = NULL; |
328 | 373k | int fd = -1; |
329 | | |
330 | 373k | read_state.dwfl = dwfl; |
331 | 373k | read_state.memory_callback = memory_callback; |
332 | 373k | read_state.memory_callback_arg = memory_callback_arg; |
333 | 373k | read_state.buffer = &buffer; |
334 | 373k | read_state.buffer_available = &buffer_available; |
335 | | |
336 | | /* We might have to reserve some memory for the phdrs. Set to NULL |
337 | | here so we can always safely free it. */ |
338 | 373k | void *phdrsp = NULL; |
339 | | |
340 | | /* Collect the build ID bits here. */ |
341 | 373k | struct elf_build_id build_id; |
342 | 373k | build_id.memory = NULL; |
343 | 373k | build_id.len = 0; |
344 | 373k | build_id.vaddr = 0; |
345 | | |
346 | 373k | if (! (*memory_callback) (dwfl, ndx, &buffer, &buffer_available, |
347 | 373k | start, sizeof (Elf64_Ehdr), memory_callback_arg) |
348 | 72.2k | || memcmp (buffer, ELFMAG, SELFMAG) != 0) |
349 | 323k | goto out; |
350 | | |
351 | | /* Extract the information we need from the file header. */ |
352 | 49.9k | const unsigned char *e_ident; |
353 | 49.9k | unsigned char ei_class; |
354 | 49.9k | unsigned char ei_data; |
355 | 49.9k | uint16_t e_type; |
356 | 49.9k | union |
357 | 49.9k | { |
358 | 49.9k | Elf32_Ehdr e32; |
359 | 49.9k | Elf64_Ehdr e64; |
360 | 49.9k | } ehdr; |
361 | 49.9k | GElf_Off phoff; |
362 | 49.9k | uint_fast16_t phnum; |
363 | 49.9k | uint_fast16_t phentsize; |
364 | 49.9k | GElf_Off shdrs_end; |
365 | 49.9k | Elf_Data xlatefrom = |
366 | 49.9k | { |
367 | 49.9k | .d_type = ELF_T_EHDR, |
368 | 49.9k | .d_buf = (void *) buffer, |
369 | 49.9k | .d_version = EV_CURRENT, |
370 | 49.9k | }; |
371 | 49.9k | Elf_Data xlateto = |
372 | 49.9k | { |
373 | 49.9k | .d_type = ELF_T_EHDR, |
374 | 49.9k | .d_buf = &ehdr, |
375 | 49.9k | .d_size = sizeof ehdr, |
376 | 49.9k | .d_version = EV_CURRENT, |
377 | 49.9k | }; |
378 | 49.9k | e_ident = ((const unsigned char *) buffer); |
379 | 49.9k | ei_class = e_ident[EI_CLASS]; |
380 | 49.9k | ei_data = e_ident[EI_DATA]; |
381 | | /* buffer may be unaligned, in which case xlatetom would not work. |
382 | | xlatetom does work when the in and out d_buf are equal (but not |
383 | | for any other overlap). */ |
384 | 49.9k | size_t ehdr_align = (ei_class == ELFCLASS32 |
385 | 49.9k | ? __alignof__ (Elf32_Ehdr) |
386 | 49.9k | : __alignof__ (Elf64_Ehdr)); |
387 | 49.9k | if (((uintptr_t) buffer & (ehdr_align - 1)) != 0) |
388 | 7.75k | { |
389 | 7.75k | memcpy (&ehdr, buffer, |
390 | 7.75k | (ei_class == ELFCLASS32 |
391 | 7.75k | ? sizeof (Elf32_Ehdr) |
392 | 7.75k | : sizeof (Elf64_Ehdr))); |
393 | 7.75k | xlatefrom.d_buf = &ehdr; |
394 | 7.75k | } |
395 | 49.9k | switch (ei_class) |
396 | 49.9k | { |
397 | 34.7k | case ELFCLASS32: |
398 | 34.7k | xlatefrom.d_size = sizeof (Elf32_Ehdr); |
399 | 34.7k | if (elf32_xlatetom (&xlateto, &xlatefrom, ei_data) == NULL) |
400 | 210 | goto out; |
401 | 34.5k | e_type = ehdr.e32.e_type; |
402 | 34.5k | phoff = ehdr.e32.e_phoff; |
403 | 34.5k | phnum = ehdr.e32.e_phnum; |
404 | 34.5k | phentsize = ehdr.e32.e_phentsize; |
405 | 34.5k | if (phentsize != sizeof (Elf32_Phdr)) |
406 | 1.86k | goto out; |
407 | | /* NOTE if the number of sections is > 0xff00 then e_shnum |
408 | | is zero and the actual number would come from the section |
409 | | zero sh_size field. We ignore this here because getting shdrs |
410 | | is just a nice bonus (see below in the type == PT_LOAD case |
411 | | where we trim the last segment). */ |
412 | 32.7k | shdrs_end = ehdr.e32.e_shoff + ehdr.e32.e_shnum * sizeof (Elf32_Shdr); |
413 | 32.7k | break; |
414 | | |
415 | 14.9k | case ELFCLASS64: |
416 | 14.9k | xlatefrom.d_size = sizeof (Elf64_Ehdr); |
417 | 14.9k | if (elf64_xlatetom (&xlateto, &xlatefrom, ei_data) == NULL) |
418 | 207 | goto out; |
419 | 14.7k | e_type = ehdr.e64.e_type; |
420 | 14.7k | phoff = ehdr.e64.e_phoff; |
421 | 14.7k | phnum = ehdr.e64.e_phnum; |
422 | 14.7k | phentsize = ehdr.e64.e_phentsize; |
423 | 14.7k | if (phentsize != sizeof (Elf64_Phdr)) |
424 | 390 | goto out; |
425 | | /* See the NOTE above for shdrs_end and ehdr.e32.e_shnum. */ |
426 | 14.3k | shdrs_end = ehdr.e64.e_shoff + ehdr.e64.e_shnum * sizeof (Elf64_Shdr); |
427 | 14.3k | break; |
428 | | |
429 | 205 | default: |
430 | 205 | goto out; |
431 | 49.9k | } |
432 | | |
433 | | /* The file header tells where to find the program headers. |
434 | | These are what we need to find the boundaries of the module. |
435 | | Without them, we don't have a module to report. */ |
436 | | |
437 | 47.0k | if (phnum == 0) |
438 | 194 | goto out; |
439 | | |
440 | 46.8k | xlatefrom.d_type = xlateto.d_type = ELF_T_PHDR; |
441 | 46.8k | xlatefrom.d_size = phnum * phentsize; |
442 | | |
443 | 46.8k | void *ph_buffer = NULL; |
444 | 46.8k | size_t ph_buffer_size = 0; |
445 | 46.8k | if (read_portion (&read_state, &ph_buffer, &ph_buffer_size, |
446 | 46.8k | start, segment, |
447 | 46.8k | start + phoff, xlatefrom.d_size)) |
448 | 1.67k | goto out; |
449 | | |
450 | 45.1k | xlatefrom.d_buf = ph_buffer; |
451 | | |
452 | 45.1k | bool class32 = ei_class == ELFCLASS32; |
453 | 45.1k | size_t phdr_size = class32 ? sizeof (Elf32_Phdr) : sizeof (Elf64_Phdr); |
454 | 45.1k | if (unlikely (phnum > SIZE_MAX / phdr_size)) |
455 | 0 | goto out; |
456 | 45.1k | const size_t phdrsp_bytes = phnum * phdr_size; |
457 | 45.1k | phdrsp = malloc (phdrsp_bytes); |
458 | 45.1k | if (unlikely (phdrsp == NULL)) |
459 | 0 | goto out; |
460 | | |
461 | 45.1k | xlateto.d_buf = phdrsp; |
462 | 45.1k | xlateto.d_size = phdrsp_bytes; |
463 | | |
464 | | /* ph_ buffer may be unaligned, in which case xlatetom would not work. |
465 | | xlatetom does work when the in and out d_buf are equal (but not |
466 | | for any other overlap). */ |
467 | 45.1k | size_t phdr_align = (class32 |
468 | 45.1k | ? __alignof__ (Elf32_Phdr) |
469 | 45.1k | : __alignof__ (Elf64_Phdr)); |
470 | 45.1k | if (((uintptr_t) ph_buffer & (phdr_align - 1)) != 0) |
471 | 34.2k | { |
472 | 34.2k | memcpy (phdrsp, ph_buffer, phdrsp_bytes); |
473 | 34.2k | xlatefrom.d_buf = phdrsp; |
474 | 34.2k | } |
475 | | |
476 | | /* Track the bounds of the file visible in memory. */ |
477 | 45.1k | GElf_Off file_trimmed_end = 0; /* Proper p_vaddr + p_filesz end. */ |
478 | 45.1k | GElf_Off file_end = 0; /* Rounded up to effective page size. */ |
479 | 45.1k | GElf_Off contiguous = 0; /* Visible as contiguous file from START. */ |
480 | 45.1k | GElf_Off total_filesz = 0; /* Total size of data to read. */ |
481 | | |
482 | | /* Collect the bias between START and the containing PT_LOAD's p_vaddr. */ |
483 | 45.1k | GElf_Addr bias = 0; |
484 | 45.1k | bool found_bias = false; |
485 | | |
486 | | /* Collect the unbiased bounds of the module here. */ |
487 | 45.1k | GElf_Addr module_start = -1l; |
488 | 45.1k | GElf_Addr module_end = 0; |
489 | 45.1k | GElf_Addr module_address_sync = 0; |
490 | | |
491 | | /* If we see PT_DYNAMIC, record it here. */ |
492 | 45.1k | GElf_Addr dyn_vaddr = 0; |
493 | 45.1k | GElf_Xword dyn_filesz = 0; |
494 | | |
495 | 45.1k | Elf32_Phdr *p32 = phdrsp; |
496 | 45.1k | Elf64_Phdr *p64 = phdrsp; |
497 | 45.1k | if ((ei_class == ELFCLASS32 |
498 | 31.2k | && elf32_xlatetom (&xlateto, &xlatefrom, ei_data) == NULL) |
499 | 45.1k | || (ei_class == ELFCLASS64 |
500 | 13.9k | && elf64_xlatetom (&xlateto, &xlatefrom, ei_data) == NULL)) |
501 | 0 | { |
502 | 0 | found_bias = false; /* Trigger error check */ |
503 | 0 | } |
504 | 45.1k | else |
505 | 45.1k | { |
506 | | /* Consider each of the program headers we've read from the image. */ |
507 | 64.5M | for (uint_fast16_t i = 0; i < phnum; ++i) |
508 | 64.5M | { |
509 | 64.5M | bool is32 = (ei_class == ELFCLASS32); |
510 | 64.5M | GElf_Word type = is32 ? p32[i].p_type : p64[i].p_type; |
511 | 64.5M | GElf_Addr vaddr = is32 ? p32[i].p_vaddr : p64[i].p_vaddr; |
512 | 64.5M | GElf_Xword memsz = is32 ? p32[i].p_memsz : p64[i].p_memsz; |
513 | 64.5M | GElf_Off offset = is32 ? p32[i].p_offset : p64[i].p_offset; |
514 | 64.5M | GElf_Xword filesz = is32 ? p32[i].p_filesz : p64[i].p_filesz; |
515 | 64.5M | GElf_Xword align = is32 ? p32[i].p_align : p64[i].p_align; |
516 | | |
517 | 64.5M | if (type == PT_DYNAMIC) |
518 | 210k | { |
519 | 210k | dyn_vaddr = vaddr; |
520 | 210k | dyn_filesz = filesz; |
521 | 210k | } |
522 | 64.3M | else if (type == PT_NOTE) |
523 | 297k | { |
524 | | /* If we have already seen a build ID, we don't care any more. */ |
525 | 297k | if (build_id.memory != NULL || filesz == 0) |
526 | 62.0k | continue; /* Next header */ |
527 | | |
528 | | /* We calculate from the p_offset of the note segment, |
529 | | because we don't yet know the bias for its p_vaddr. */ |
530 | 235k | const GElf_Addr note_vaddr = start + offset; |
531 | 235k | void *data = NULL; |
532 | 235k | size_t data_size = 0; |
533 | 235k | if (read_portion (&read_state, &data, &data_size, |
534 | 235k | start, segment, note_vaddr, filesz)) |
535 | 132k | continue; /* Next header */ |
536 | | |
537 | 103k | if (filesz > SIZE_MAX / sizeof (Elf32_Nhdr)) |
538 | 0 | continue; |
539 | | |
540 | 103k | assert (sizeof (Elf32_Nhdr) == sizeof (Elf64_Nhdr)); |
541 | | |
542 | 103k | void *notes; |
543 | 103k | if (ei_data == MY_ELFDATA |
544 | 47.9k | && (uintptr_t) data == (align == 8 |
545 | 47.9k | ? NOTE_ALIGN8 ((uintptr_t) data) |
546 | 47.9k | : NOTE_ALIGN4 ((uintptr_t) data))) |
547 | 34.5k | notes = data; |
548 | 68.9k | else |
549 | 68.9k | { |
550 | 68.9k | const unsigned int xencoding = ehdr.e32.e_ident[EI_DATA]; |
551 | | |
552 | 68.9k | if (filesz > SIZE_MAX / sizeof (Elf32_Nhdr)) |
553 | 0 | continue; |
554 | 68.9k | notes = malloc (filesz); |
555 | 68.9k | if (unlikely (notes == NULL)) |
556 | 0 | continue; /* Next header */ |
557 | 68.9k | xlatefrom.d_type = xlateto.d_type = (align == 8 |
558 | 68.9k | ? ELF_T_NHDR8 |
559 | 68.9k | : ELF_T_NHDR); |
560 | 68.9k | xlatefrom.d_buf = (void *) data; |
561 | 68.9k | xlatefrom.d_size = filesz; |
562 | 68.9k | xlateto.d_buf = notes; |
563 | 68.9k | xlateto.d_size = filesz; |
564 | | |
565 | | /* data may be unaligned, in which case xlatetom would not work. |
566 | | xlatetom does work when the in and out d_buf are equal (but not |
567 | | for any other overlap). */ |
568 | 68.9k | if ((uintptr_t) data != (align == 8 |
569 | 68.9k | ? NOTE_ALIGN8 ((uintptr_t) data) |
570 | 68.9k | : NOTE_ALIGN4 ((uintptr_t) data))) |
571 | 18.1k | { |
572 | 18.1k | memcpy (notes, data, filesz); |
573 | 18.1k | xlatefrom.d_buf = notes; |
574 | 18.1k | } |
575 | | |
576 | 68.9k | if (elf32_xlatetom (&xlateto, &xlatefrom, xencoding) == NULL) |
577 | 0 | { |
578 | 0 | free (notes); |
579 | 0 | finish_portion (&read_state, &data, &data_size); |
580 | 0 | continue; |
581 | 0 | } |
582 | 68.9k | } |
583 | | |
584 | 103k | const GElf_Nhdr *nh = notes; |
585 | 103k | size_t len = 0; |
586 | 152k | while (filesz - len > sizeof (*nh)) |
587 | 97.5k | { |
588 | 97.5k | len += sizeof (*nh); |
589 | | |
590 | 97.5k | size_t namesz = nh->n_namesz; |
591 | 97.5k | namesz = align == 8 ? NOTE_ALIGN8 (namesz) : NOTE_ALIGN4 (namesz); |
592 | 97.5k | if (namesz > filesz - len || len + namesz < namesz) |
593 | 38.5k | break; |
594 | | |
595 | 58.9k | void *note_name = notes + len; |
596 | 58.9k | len += namesz; |
597 | | |
598 | 58.9k | size_t descsz = nh->n_descsz; |
599 | 58.9k | descsz = align == 8 ? NOTE_ALIGN8 (descsz) : NOTE_ALIGN4 (descsz); |
600 | 58.9k | if (descsz > filesz - len || len + descsz < descsz) |
601 | 6.64k | break; |
602 | | |
603 | 52.3k | void *note_desc = notes + len; |
604 | 52.3k | len += descsz; |
605 | | |
606 | | /* We don't handle very short or really large build-ids. We need at |
607 | | at least 3 and allow for up to 64 (normally ids are 20 long). */ |
608 | 57.1k | #define MIN_BUILD_ID_BYTES 3 |
609 | 56.7k | #define MAX_BUILD_ID_BYTES 64 |
610 | 52.3k | if (nh->n_type == NT_GNU_BUILD_ID |
611 | 4.86k | && nh->n_descsz >= MIN_BUILD_ID_BYTES |
612 | 4.44k | && nh->n_descsz <= MAX_BUILD_ID_BYTES |
613 | 3.94k | && nh->n_namesz == sizeof "GNU" |
614 | 3.49k | && !memcmp (note_name, "GNU", sizeof "GNU")) |
615 | 3.02k | { |
616 | 3.02k | build_id.vaddr = (note_desc |
617 | 3.02k | - (const void *) notes |
618 | 3.02k | + note_vaddr); |
619 | 3.02k | build_id.len = nh->n_descsz; |
620 | 3.02k | build_id.memory = malloc (build_id.len); |
621 | 3.02k | if (likely (build_id.memory != NULL)) |
622 | 3.02k | memcpy (build_id.memory, note_desc, build_id.len); |
623 | 3.02k | break; |
624 | 3.02k | } |
625 | | |
626 | 49.3k | nh = (void *) notes + len; |
627 | 49.3k | } |
628 | | |
629 | 103k | if (notes != data) |
630 | 68.9k | free (notes); |
631 | 103k | finish_portion (&read_state, &data, &data_size); |
632 | 103k | } |
633 | 64.0M | else if (type == PT_LOAD) |
634 | 1.97M | { |
635 | 1.97M | align = (dwfl->segment_align > 1 |
636 | 1.97M | ? dwfl->segment_align : (align ?: 1)); |
637 | | |
638 | 1.97M | GElf_Addr vaddr_end = (vaddr + memsz + align - 1) & -align; |
639 | 1.97M | GElf_Addr filesz_vaddr = (filesz < memsz |
640 | 1.97M | ? vaddr + filesz : vaddr_end); |
641 | 1.97M | GElf_Off filesz_offset = filesz_vaddr - vaddr + offset; |
642 | | |
643 | 1.97M | if (file_trimmed_end < offset + filesz) |
644 | 91.3k | { |
645 | 91.3k | file_trimmed_end = offset + filesz; |
646 | | |
647 | | /* Trim the last segment so we don't bother with zeros |
648 | | in the last page that are off the end of the file. |
649 | | However, if the extra bit in that page includes the |
650 | | section headers, keep them. */ |
651 | 91.3k | if (shdrs_end <= filesz_offset |
652 | 45.8k | && shdrs_end > file_trimmed_end) |
653 | 1.14k | { |
654 | 1.14k | filesz += shdrs_end - file_trimmed_end; |
655 | 1.14k | file_trimmed_end = shdrs_end; |
656 | 1.14k | } |
657 | 91.3k | } |
658 | | |
659 | 1.97M | total_filesz += filesz; |
660 | | |
661 | 1.97M | if (file_end < filesz_offset) |
662 | 100k | { |
663 | 100k | file_end = filesz_offset; |
664 | 100k | if (filesz_vaddr - start == filesz_offset) |
665 | 26.8k | contiguous = file_end; |
666 | 100k | } |
667 | | |
668 | 1.97M | if (!found_bias && (offset & -align) == 0 |
669 | 55.8k | && likely (filesz_offset >= phoff + phnum * phentsize)) |
670 | 43.4k | { |
671 | 43.4k | bias = start - vaddr; |
672 | 43.4k | found_bias = true; |
673 | 43.4k | } |
674 | | |
675 | 1.97M | if ((vaddr & -align) < module_start) |
676 | 57.8k | { |
677 | 57.8k | module_start = vaddr & -align; |
678 | 57.8k | module_address_sync = vaddr + memsz; |
679 | 57.8k | } |
680 | | |
681 | 1.97M | if (module_end < vaddr_end) |
682 | 99.4k | module_end = vaddr_end; |
683 | 1.97M | } |
684 | 64.5M | } |
685 | 45.1k | } |
686 | | |
687 | 45.1k | finish_portion (&read_state, &ph_buffer, &ph_buffer_size); |
688 | | |
689 | | /* We must have seen the segment covering offset 0, or else the ELF |
690 | | header we read at START was not produced by these program headers. */ |
691 | 45.1k | if (unlikely (!found_bias)) |
692 | 1.73k | goto out; |
693 | | |
694 | | /* Now we know enough to report a module for sure: its bounds. */ |
695 | 43.4k | module_start += bias; |
696 | 43.4k | module_end += bias; |
697 | | |
698 | 43.4k | dyn_vaddr += bias; |
699 | | |
700 | | /* NAME found from link map has precedence over DT_SONAME possibly read |
701 | | below. */ |
702 | 43.4k | bool name_is_final = false; |
703 | | |
704 | | /* Try to match up DYN_VADDR against L_LD as found in link map. |
705 | | Segments sniffing may guess invalid address as the first read-only memory |
706 | | mapping may not be dumped to the core file (if ELF headers are not dumped) |
707 | | and the ELF header is dumped first with the read/write mapping of the same |
708 | | file at higher addresses. */ |
709 | 43.4k | if (r_debug_info != NULL) |
710 | 43.4k | for (const struct r_debug_info_module *module = r_debug_info->module; |
711 | 146k | module != NULL; module = module->next) |
712 | 105k | if (module_start <= module->l_ld && module->l_ld < module_end) |
713 | 8.53k | { |
714 | | /* L_LD read from link map must be right while DYN_VADDR is unsafe. |
715 | | Therefore subtract DYN_VADDR and add L_LD to get a possibly |
716 | | corrective displacement for all addresses computed so far. */ |
717 | 8.53k | GElf_Addr fixup = module->l_ld - dyn_vaddr; |
718 | 8.53k | if ((fixup & (dwfl->segment_align - 1)) == 0 |
719 | 3.73k | && module_start + fixup <= module->l_ld |
720 | 2.34k | && module->l_ld < module_end + fixup) |
721 | 1.99k | { |
722 | 1.99k | module_start += fixup; |
723 | 1.99k | module_end += fixup; |
724 | 1.99k | dyn_vaddr += fixup; |
725 | 1.99k | bias += fixup; |
726 | 1.99k | if (module->name[0] != '\0') |
727 | 1.06k | { |
728 | 1.06k | name = xbasename (module->name); |
729 | 1.06k | name_is_final = true; |
730 | 1.06k | } |
731 | 1.99k | break; |
732 | 1.99k | } |
733 | 8.53k | } |
734 | | |
735 | 43.4k | if (r_debug_info != NULL) |
736 | 43.4k | { |
737 | 43.4k | bool skip_this_module = false; |
738 | 43.4k | for (struct r_debug_info_module *module = r_debug_info->module; |
739 | 161k | module != NULL; module = module->next) |
740 | 118k | if ((module_end > module->start && module_start < module->end) |
741 | 117k | || dyn_vaddr == module->l_ld) |
742 | 14.5k | { |
743 | 14.5k | if (module->elf != NULL |
744 | 1.25k | && invalid_elf (module->elf, module->disk_file_has_build_id, |
745 | 1.25k | &build_id)) |
746 | 1.24k | { |
747 | | /* If MODULE's build-id doesn't match the disk file's |
748 | | build-id, close ELF only if MODULE and ELF refer to |
749 | | different builds of files with the same name. This |
750 | | prevents premature closure of the correct ELF in cases |
751 | | where segments of a module are non-contiguous in memory. */ |
752 | 1.24k | if (name != NULL && module->name[0] != '\0' |
753 | 523 | && strcmp (xbasename (module->name), xbasename (name)) == 0) |
754 | 94 | { |
755 | 94 | elf_end (module->elf); |
756 | 94 | close (module->fd); |
757 | 94 | module->elf = NULL; |
758 | 94 | module->fd = -1; |
759 | 94 | } |
760 | 1.24k | } |
761 | 13.2k | else if (module->elf != NULL) |
762 | 5 | { |
763 | | /* This module has already been reported. */ |
764 | 5 | skip_this_module = true; |
765 | 5 | } |
766 | 13.2k | else |
767 | 13.2k | { |
768 | | /* Only report this module if we haven't already done so. */ |
769 | 26.6k | for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; |
770 | 13.3k | mod = mod->next) |
771 | 13.3k | if (mod->low_addr == module_start |
772 | 12.4k | && mod->high_addr == module_end) |
773 | 10.3k | skip_this_module = true; |
774 | 13.2k | } |
775 | 14.5k | } |
776 | 43.4k | if (skip_this_module) |
777 | 1.46k | goto out; |
778 | 43.4k | } |
779 | | |
780 | 41.9k | const char *file_note_name = handle_file_note (module_start, module_end, |
781 | 41.9k | ei_class, ei_data, |
782 | 41.9k | note_file, note_file_size); |
783 | 41.9k | if (file_note_name) |
784 | 1.66k | { |
785 | 1.66k | name = file_note_name; |
786 | 1.66k | name_is_final = true; |
787 | 1.66k | bool invalid = false; |
788 | | |
789 | | /* We were not handed specific executable hence try to look for it in |
790 | | sysroot if it is set. */ |
791 | 1.66k | if (dwfl->sysroot && !executable) |
792 | 0 | { |
793 | | #ifdef HAVE_OPENAT2_RESOLVE_IN_ROOT |
794 | | int sysrootfd, err; |
795 | | |
796 | | struct open_how how = { |
797 | | .flags = O_RDONLY, |
798 | | .resolve = RESOLVE_IN_ROOT, |
799 | | }; |
800 | | |
801 | | sysrootfd = open (dwfl->sysroot, O_DIRECTORY|O_PATH); |
802 | | if (sysrootfd < 0) |
803 | | return -1; |
804 | | |
805 | | fd = syscall (SYS_openat2, sysrootfd, name, &how, sizeof(how)); |
806 | | err = fd < 0 ? -errno : 0; |
807 | | |
808 | | close (sysrootfd); |
809 | | |
810 | | /* Fallback to regular open() if openat2 is not available. */ |
811 | | if (fd < 0 && err == -ENOSYS) |
812 | | #endif |
813 | 0 | { |
814 | 0 | int r; |
815 | 0 | char *n; |
816 | |
|
817 | 0 | r = asprintf (&n, "%s%s", dwfl->sysroot, name); |
818 | 0 | if (r > 0) |
819 | 0 | { |
820 | 0 | fd = open (n, O_RDONLY); |
821 | 0 | free (n); |
822 | 0 | } |
823 | 0 | } |
824 | 0 | } |
825 | 1.66k | else |
826 | 1.66k | fd = open (name, O_RDONLY); |
827 | | |
828 | 1.66k | if (fd >= 0) |
829 | 1.27k | { |
830 | 1.27k | Dwfl_Error error = __libdw_open_file (&fd, &elf, true, false); |
831 | 1.27k | if (error == DWFL_E_NOERROR) |
832 | 1.07k | invalid = invalid_elf (elf, true /* disk_file_has_build_id */, |
833 | 1.07k | &build_id); |
834 | 1.27k | } |
835 | 1.66k | if (invalid) |
836 | 744 | { |
837 | | /* The file was there, but the build_id didn't match. We |
838 | | still want to report the module, but need to get the ELF |
839 | | some other way if possible. */ |
840 | 744 | close (fd); |
841 | 744 | fd = -1; |
842 | 744 | elf_end (elf); |
843 | 744 | elf = NULL; |
844 | 744 | } |
845 | 1.66k | } |
846 | | |
847 | | /* Examine its .dynamic section to get more interesting details. |
848 | | If it has DT_SONAME, we'll use that as the module name. |
849 | | If it has a DT_DEBUG, then it's actually a PIE rather than a DSO. |
850 | | We need its DT_STRTAB and DT_STRSZ to decipher DT_SONAME, |
851 | | and they also tell us the essential portion of the file |
852 | | for fetching symbols. */ |
853 | 41.9k | GElf_Addr soname_stroff = 0; |
854 | 41.9k | GElf_Addr dynstr_vaddr = 0; |
855 | 41.9k | GElf_Xword dynstrsz = 0; |
856 | 41.9k | bool execlike = false; |
857 | 41.9k | const size_t dyn_entsize = (ei_class == ELFCLASS32 |
858 | 41.9k | ? sizeof (Elf32_Dyn) : sizeof (Elf64_Dyn)); |
859 | 41.9k | void *dyn_data = NULL; |
860 | 41.9k | size_t dyn_data_size = 0; |
861 | 41.9k | if (dyn_filesz != 0 && dyn_filesz % dyn_entsize == 0 |
862 | 22.0k | && ! read_portion (&read_state, &dyn_data, &dyn_data_size, |
863 | 22.0k | start, segment, dyn_vaddr, dyn_filesz)) |
864 | 15.6k | { |
865 | 15.6k | if ((dyn_filesz / dyn_entsize) == 0 |
866 | 15.6k | || dyn_filesz > (SIZE_MAX / dyn_entsize)) |
867 | 0 | goto out; |
868 | 15.6k | void *dyns = malloc (dyn_filesz); |
869 | 15.6k | Elf32_Dyn *d32 = dyns; |
870 | 15.6k | Elf64_Dyn *d64 = dyns; |
871 | 15.6k | if (unlikely (dyns == NULL)) |
872 | 0 | goto out; |
873 | | |
874 | 15.6k | xlatefrom.d_type = xlateto.d_type = ELF_T_DYN; |
875 | 15.6k | xlatefrom.d_buf = (void *) dyn_data; |
876 | 15.6k | xlatefrom.d_size = dyn_filesz; |
877 | 15.6k | xlateto.d_buf = dyns; |
878 | 15.6k | xlateto.d_size = dyn_filesz; |
879 | | |
880 | | /* dyn_data may be unaligned, in which case xlatetom would not work. |
881 | | xlatetom does work when the in and out d_buf are equal (but not |
882 | | for any other overlap). */ |
883 | 15.6k | bool is32 = (ei_class == ELFCLASS32); |
884 | 15.6k | size_t dyn_align = (is32 |
885 | 15.6k | ? __alignof__ (Elf32_Dyn) |
886 | 15.6k | : __alignof__ (Elf64_Dyn)); |
887 | 15.6k | if (((uintptr_t) dyn_data & (dyn_align - 1)) != 0) |
888 | 9.83k | { |
889 | 9.83k | memcpy (dyns, dyn_data, dyn_filesz); |
890 | 9.83k | xlatefrom.d_buf = dyns; |
891 | 9.83k | } |
892 | | |
893 | 15.6k | if ((is32 && elf32_xlatetom (&xlateto, &xlatefrom, ei_data) != NULL) |
894 | 2.33k | || (!is32 && elf64_xlatetom (&xlateto, &xlatefrom, ei_data) != NULL)) |
895 | 15.6k | { |
896 | 15.6k | size_t n = (is32 |
897 | 15.6k | ? (dyn_filesz / sizeof (Elf32_Dyn)) |
898 | 15.6k | : (dyn_filesz / sizeof (Elf64_Dyn))); |
899 | 5.85M | for (size_t i = 0; i < n; ++i) |
900 | 5.84M | { |
901 | 5.84M | GElf_Sxword tag = is32 ? d32[i].d_tag : d64[i].d_tag; |
902 | 5.84M | GElf_Xword val = is32 ? d32[i].d_un.d_val : d64[i].d_un.d_val; |
903 | | |
904 | 5.84M | if (tag == DT_DEBUG) |
905 | 6.49k | execlike = true; |
906 | 5.83M | else if (tag == DT_SONAME) |
907 | 5.57k | soname_stroff = val; |
908 | 5.83M | else if (tag == DT_STRTAB) |
909 | 27.8k | dynstr_vaddr = val; |
910 | 5.80M | else if (tag == DT_STRSZ) |
911 | 18.2k | dynstrsz = val; |
912 | 5.78M | else |
913 | 5.78M | continue; |
914 | | |
915 | 58.1k | if (soname_stroff != 0 && dynstr_vaddr != 0 && dynstrsz != 0) |
916 | 4.56k | break; |
917 | 58.1k | } |
918 | 15.6k | } |
919 | 15.6k | free (dyns); |
920 | 15.6k | } |
921 | 41.9k | finish_portion (&read_state, &dyn_data, &dyn_data_size); |
922 | | |
923 | | /* We'll use the name passed in or a stupid default if not DT_SONAME. */ |
924 | 41.9k | if (name == NULL) |
925 | 40.0k | name = e_type == ET_EXEC ? "[exe]" : execlike ? "[pie]" : "[dso]"; |
926 | | |
927 | 41.9k | void *soname = NULL; |
928 | 41.9k | size_t soname_size = 0; |
929 | 41.9k | if (! name_is_final && dynstrsz != 0 && dynstr_vaddr != 0) |
930 | 11.4k | { |
931 | | /* We know the bounds of the .dynstr section. |
932 | | |
933 | | The DYNSTR_VADDR pointer comes from the .dynamic section |
934 | | (DT_STRTAB, detected above). Ordinarily the dynamic linker |
935 | | will have adjusted this pointer in place so it's now an |
936 | | absolute address. But sometimes .dynamic is read-only (in |
937 | | vDSOs and odd architectures), and sometimes the adjustment |
938 | | just hasn't happened yet in the memory image we looked at. |
939 | | So treat DYNSTR_VADDR as an absolute address if it falls |
940 | | within the module bounds, or try applying the phdr bias |
941 | | when that adjusts it to fall within the module bounds. */ |
942 | | |
943 | 11.4k | if ((dynstr_vaddr < module_start || dynstr_vaddr >= module_end) |
944 | 5.65k | && dynstr_vaddr + bias >= module_start |
945 | 4.46k | && dynstr_vaddr + bias < module_end) |
946 | 2.97k | dynstr_vaddr += bias; |
947 | | |
948 | 11.4k | if (unlikely (dynstr_vaddr + dynstrsz > module_end)) |
949 | 721 | dynstrsz = 0; |
950 | | |
951 | | /* Try to get the DT_SONAME string. */ |
952 | 11.4k | if (soname_stroff != 0 && soname_stroff + 1 < dynstrsz |
953 | 3.46k | && ! read_portion (&read_state, &soname, &soname_size, |
954 | 3.46k | start, segment, |
955 | 3.46k | dynstr_vaddr + soname_stroff, 0)) |
956 | 1.79k | name = soname; |
957 | 11.4k | } |
958 | | |
959 | | /* Now that we have chosen the module's name and bounds, report it. |
960 | | If we found a build ID, report that too. */ |
961 | | |
962 | 41.9k | Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, name, |
963 | 41.9k | module_start, module_end); |
964 | | |
965 | | // !execlike && ET_EXEC is PIE. |
966 | | // execlike && !ET_EXEC is a static executable. |
967 | 41.9k | if (mod != NULL && (execlike || ehdr.e32.e_type == ET_EXEC)) |
968 | 4.64k | mod->is_executable = true; |
969 | | |
970 | 41.9k | if (likely (mod != NULL) && build_id.memory != NULL |
971 | 2.78k | && unlikely (INTUSE(dwfl_module_report_build_id) (mod, |
972 | 41.9k | build_id.memory, |
973 | 41.9k | build_id.len, |
974 | 41.9k | build_id.vaddr))) |
975 | 271 | { |
976 | 271 | mod->gc = true; |
977 | 271 | mod = NULL; |
978 | 271 | } |
979 | | |
980 | | /* At this point we do not need BUILD_ID or NAME any more. |
981 | | They have been copied. */ |
982 | 41.9k | free (build_id.memory); |
983 | 41.9k | build_id.memory = NULL; |
984 | 41.9k | finish_portion (&read_state, &soname, &soname_size); |
985 | | |
986 | 41.9k | if (unlikely (mod == NULL)) |
987 | 271 | { |
988 | 271 | ndx = -1; |
989 | 271 | goto out; |
990 | 271 | } |
991 | 41.6k | else |
992 | 41.6k | ndx++; |
993 | | |
994 | | /* We have reported the module. Now let the caller decide whether we |
995 | | should read the whole thing in right now. */ |
996 | | |
997 | 41.6k | const GElf_Off cost = (contiguous < file_trimmed_end ? total_filesz |
998 | 41.6k | : buffer_available >= contiguous ? 0 |
999 | 10.8k | : contiguous - buffer_available); |
1000 | 41.6k | const GElf_Off worthwhile = ((dynstr_vaddr == 0 || dynstrsz == 0) ? 0 |
1001 | 41.6k | : dynstr_vaddr + dynstrsz - start); |
1002 | 41.6k | const GElf_Off whole = MAX (file_trimmed_end, shdrs_end); |
1003 | | |
1004 | 41.6k | if (elf == NULL |
1005 | 41.3k | && (*read_eagerly) (MODCB_ARGS (mod), &buffer, &buffer_available, |
1006 | 41.3k | cost, worthwhile, whole, contiguous, |
1007 | 41.3k | read_eagerly_arg, &elf) |
1008 | 12.5k | && elf == NULL) |
1009 | 10.4k | { |
1010 | | /* The caller wants to read the whole file in right now, but hasn't |
1011 | | done it for us. Fill in a local image of the virtual file. */ |
1012 | | |
1013 | 10.4k | if (file_trimmed_end > maxread) |
1014 | 9.77k | file_trimmed_end = maxread; |
1015 | | |
1016 | 10.4k | void *contents = calloc (1, file_trimmed_end); |
1017 | 10.4k | if (unlikely (contents == NULL)) |
1018 | 0 | goto out; |
1019 | | |
1020 | 10.4k | if (contiguous < file_trimmed_end) |
1021 | 5.97k | { |
1022 | | /* We can't use the memory image verbatim as the file image. |
1023 | | So we'll be reading into a local image of the virtual file. */ |
1024 | 4.78M | for (uint_fast16_t i = 0; i < phnum; ++i) |
1025 | 4.77M | { |
1026 | 4.77M | bool is32 = (ei_class == ELFCLASS32); |
1027 | 4.77M | GElf_Word type = is32 ? p32[i].p_type : p64[i].p_type; |
1028 | | |
1029 | 4.77M | if (type != PT_LOAD) |
1030 | 4.68M | continue; |
1031 | | |
1032 | 93.4k | GElf_Addr vaddr = is32 ? p32[i].p_vaddr : p64[i].p_vaddr; |
1033 | 93.4k | GElf_Off offset = is32 ? p32[i].p_offset : p64[i].p_offset; |
1034 | 93.4k | GElf_Xword filesz = is32 ? p32[i].p_filesz : p64[i].p_filesz; |
1035 | | |
1036 | | /* Don't try to read beyond the actual end of file. */ |
1037 | 93.4k | if (offset >= file_trimmed_end) |
1038 | 43.5k | continue; |
1039 | | |
1040 | 49.8k | void *into = contents + offset; |
1041 | 49.8k | size_t read_size = MIN (filesz, file_trimmed_end - offset); |
1042 | 49.8k | (*memory_callback) (dwfl, addr_segndx (dwfl, segment, |
1043 | 49.8k | vaddr + bias, false), |
1044 | 49.8k | &into, &read_size, vaddr + bias, read_size, |
1045 | 49.8k | memory_callback_arg); |
1046 | 49.8k | } |
1047 | 5.97k | } |
1048 | 4.48k | else |
1049 | 4.48k | { |
1050 | | /* The whole file sits contiguous in memory, |
1051 | | but the caller didn't want to just do it. */ |
1052 | | |
1053 | 4.48k | const size_t have = MIN (buffer_available, file_trimmed_end); |
1054 | 4.48k | memcpy (contents, buffer, have); |
1055 | | |
1056 | 4.48k | if (have < file_trimmed_end) |
1057 | 1.20k | { |
1058 | 1.20k | void *into = contents + have; |
1059 | 1.20k | size_t read_size = file_trimmed_end - have; |
1060 | 1.20k | (*memory_callback) (dwfl, |
1061 | 1.20k | addr_segndx (dwfl, segment, |
1062 | 1.20k | start + have, false), |
1063 | 1.20k | &into, &read_size, start + have, |
1064 | 1.20k | read_size, memory_callback_arg); |
1065 | 1.20k | } |
1066 | 4.48k | } |
1067 | | |
1068 | 10.4k | elf = elf_memory (contents, file_trimmed_end); |
1069 | 10.4k | if (unlikely (elf == NULL)) |
1070 | 517 | free (contents); |
1071 | 9.94k | else |
1072 | 9.94k | elf->flags |= ELF_F_MALLOCED; |
1073 | 10.4k | } |
1074 | | |
1075 | 41.6k | if (elf != NULL && mod->main.elf == NULL) |
1076 | 1.89k | { |
1077 | | /* Install the file in the module. */ |
1078 | 1.89k | mod->main.elf = elf; |
1079 | 1.89k | mod->main.fd = fd; |
1080 | 1.89k | elf = NULL; |
1081 | 1.89k | fd = -1; |
1082 | 1.89k | mod->main.vaddr = module_start - bias; |
1083 | 1.89k | mod->main.address_sync = module_address_sync; |
1084 | 1.89k | mod->main_bias = bias; |
1085 | 1.89k | } |
1086 | | |
1087 | 373k | out: |
1088 | 373k | if (build_id.memory != NULL) |
1089 | 244 | free (build_id.memory); |
1090 | 373k | free (phdrsp); |
1091 | 373k | if (buffer != NULL) |
1092 | 69.5k | (*memory_callback) (dwfl, -1, &buffer, &buffer_available, 0, 0, |
1093 | 69.5k | memory_callback_arg); |
1094 | | |
1095 | 373k | if (elf != NULL) |
1096 | 10.4k | elf_end (elf); |
1097 | 373k | if (fd != -1) |
1098 | 325 | close (fd); |
1099 | 373k | return ndx; |
1100 | 41.6k | } |