Branch data Line data Source code
1 : : /* Find debugging and symbol information for a module in libdwfl.
2 : : Copyright (C) 2005-2011 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 : : #include "libdwflP.h"
30 : : #include <fcntl.h>
31 : : #include <string.h>
32 : : #include <unistd.h>
33 : : #include "../libdw/libdwP.h" /* DWARF_E_* values are here. */
34 : :
35 : :
36 : : /* Open libelf FILE->fd and compute the load base of ELF as loaded in MOD.
37 : : When we return success, FILE->elf and FILE->vaddr are set up. */
38 : : static inline Dwfl_Error
39 : 5092 : open_elf (Dwfl_Module *mod, struct dwfl_file *file)
40 : : {
41 [ + + ]: 5092 : if (file->elf == NULL)
42 : : {
43 : : /* CBFAIL uses errno if it's set, so clear it first in case we don't
44 : : set it with an open failure below. */
45 : 5063 : errno = 0;
46 : :
47 : : /* If there was a pre-primed file name left that the callback left
48 : : behind, try to open that file name. */
49 [ + + ][ + + ]: 5063 : if (file->fd < 0 && file->name != NULL)
50 [ - + ][ # # ]: 6 : file->fd = TEMP_FAILURE_RETRY (open64 (file->name, O_RDONLY));
51 : :
52 [ + + ]: 5063 : if (file->fd < 0)
53 [ - + ]: 32 : return CBFAIL;
54 : :
55 : 5031 : Dwfl_Error error = __libdw_open_file (&file->fd, &file->elf, true, false);
56 [ + - ]: 5031 : if (error != DWFL_E_NOERROR)
57 : : return error;
58 : : }
59 [ - + ]: 29 : else if (unlikely (elf_kind (file->elf) != ELF_K_ELF))
60 : : {
61 : 0 : elf_end (file->elf);
62 : 0 : file->elf = NULL;
63 : 0 : close (file->fd);
64 : 0 : file->fd = -1;
65 : : return DWFL_E_BADELF;
66 : : }
67 : :
68 : 5060 : GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (file->elf, &ehdr_mem);
69 [ - + ]: 5060 : if (ehdr == NULL)
70 : : {
71 : : elf_error:
72 : 0 : elf_end (file->elf);
73 : 0 : file->elf = NULL;
74 : 0 : close (file->fd);
75 : 0 : file->fd = -1;
76 : 0 : return DWFL_E (LIBELF, elf_errno ());
77 : : }
78 : :
79 [ + + ]: 5060 : if (mod->e_type != ET_REL)
80 : : {
81 : : /* In any non-ET_REL file, we compute the "synchronization address".
82 : :
83 : : We start with the address at the end of the first PT_LOAD
84 : : segment. When prelink converts REL to RELA in an ET_DYN
85 : : file, it expands the space between the beginning of the
86 : : segment and the actual code/data addresses. Since that
87 : : change wasn't made in the debug file, the distance from
88 : : p_vaddr to an address of interest (in an st_value or DWARF
89 : : data) now differs between the main and debug files. The
90 : : distance from address_sync to an address of interest remains
91 : : consistent.
92 : :
93 : : If there are no section headers at all (full stripping), then
94 : : the end of the first segment is a valid synchronization address.
95 : : This cannot happen in a prelinked file, since prelink itself
96 : : relies on section headers for prelinking and for undoing it.
97 : : (If you do full stripping on a prelinked file, then you get what
98 : : you deserve--you can neither undo the prelinking, nor expect to
99 : : line it up with a debug file separated before prelinking.)
100 : :
101 : : However, when prelink processes an ET_EXEC file, it can do
102 : : something different. There it juggles the "special" sections
103 : : (SHT_DYNSYM et al) to make space for the additional prelink
104 : : special sections. Sometimes it will do this by moving a special
105 : : section like .dynstr after the real program sections in the first
106 : : PT_LOAD segment--i.e. to the end. That changes the end address of
107 : : the segment, so it no longer lines up correctly and is not a valid
108 : : synchronization address to use. Because of this, we need to apply
109 : : a different prelink-savvy means to discover the synchronization
110 : : address when there is a separate debug file and a prelinked main
111 : : file. That is done in find_debuginfo, below. */
112 : :
113 : : size_t phnum;
114 [ + - ]: 5059 : if (unlikely (elf_getphdrnum (file->elf, &phnum) != 0))
115 : : goto elf_error;
116 : :
117 : 5059 : file->vaddr = file->address_sync = 0;
118 [ + - ]: 20168 : for (size_t i = 0; i < phnum; ++i)
119 : : {
120 : : GElf_Phdr ph_mem;
121 : 15109 : GElf_Phdr *ph = gelf_getphdr (file->elf, i, &ph_mem);
122 [ + - ]: 15109 : if (unlikely (ph == NULL))
123 : : goto elf_error;
124 [ + + ]: 15109 : if (ph->p_type == PT_LOAD)
125 : : {
126 : 5059 : file->vaddr = ph->p_vaddr & -ph->p_align;
127 : 5059 : file->address_sync = ph->p_vaddr + ph->p_memsz;
128 : : break;
129 : : }
130 : : }
131 : : }
132 : :
133 : 5060 : mod->e_type = ehdr->e_type;
134 : :
135 : : /* Relocatable Linux kernels are ET_EXEC but act like ET_DYN. */
136 [ + + ][ + + ]: 5060 : if (mod->e_type == ET_EXEC && file->vaddr != mod->low_addr)
137 : 5092 : mod->e_type = ET_DYN;
138 : :
139 : : return DWFL_E_NOERROR;
140 : : }
141 : :
142 : : /* We have an authoritative build ID for this module MOD, so don't use
143 : : a file by name that doesn't match that ID. */
144 : : static void
145 : 6 : mod_verify_build_id (Dwfl_Module *mod)
146 : : {
147 [ - + ]: 6 : assert (mod->build_id_len > 0);
148 : :
149 [ - + - + ]: 6 : switch (__builtin_expect (__libdwfl_find_build_id (mod, false,
150 : : mod->main.elf), 2))
151 : : {
152 : : case 2:
153 : : /* Build ID matches as it should. */
154 : 6 : return;
155 : :
156 : : case -1: /* ELF error. */
157 : 0 : mod->elferr = INTUSE(dwfl_errno) ();
158 : 0 : break;
159 : :
160 : : case 0: /* File has no build ID note. */
161 : : case 1: /* FIle has a build ID that does not match. */
162 : 5 : mod->elferr = DWFL_E_WRONG_ID_ELF;
163 : 5 : break;
164 : :
165 : : default:
166 : 0 : abort ();
167 : : }
168 : :
169 : : /* We get here when it was the right ELF file. Clear it out. */
170 : 5 : elf_end (mod->main.elf);
171 : 5 : mod->main.elf = NULL;
172 [ + - ]: 5 : if (mod->main.fd >= 0)
173 : : {
174 : 5 : close (mod->main.fd);
175 : 5 : mod->main.fd = -1;
176 : : }
177 : : }
178 : :
179 : : /* Find the main ELF file for this module and open libelf on it.
180 : : When we return success, MOD->main.elf and MOD->main.bias are set up. */
181 : : void
182 : : internal_function
183 : 5371 : __libdwfl_getelf (Dwfl_Module *mod)
184 : : {
185 [ + + ]: 5371 : if (mod->main.elf != NULL /* Already done. */
186 [ + + ]: 5043 : || mod->elferr != DWFL_E_NOERROR) /* Cached failure. */
187 : : return;
188 : :
189 : 5034 : mod->main.fd = (*mod->dwfl->callbacks->find_elf) (MODCB_ARGS (mod),
190 : : &mod->main.name,
191 : : &mod->main.elf);
192 [ + + ][ + + ]: 5034 : const bool fallback = mod->main.elf == NULL && mod->main.fd < 0;
193 : 5034 : mod->elferr = open_elf (mod, &mod->main);
194 [ + + ]: 5034 : if (mod->elferr != DWFL_E_NOERROR)
195 : : return;
196 : :
197 [ + + ]: 5032 : if (!mod->main.valid)
198 : : {
199 : : /* Clear any explicitly reported build ID, just in case it was wrong.
200 : : We'll fetch it from the file when asked. */
201 : 5026 : free (mod->build_id_bits);
202 : 5026 : mod->build_id_bits = NULL;
203 : 5026 : mod->build_id_len = 0;
204 : : }
205 [ + - ]: 6 : else if (fallback)
206 : 6 : mod_verify_build_id (mod);
207 : :
208 [ + - ]: 5371 : mod->main_bias = mod->e_type == ET_REL ? 0 : mod->low_addr - mod->main.vaddr;
209 : : }
210 : :
211 : : /* Search an ELF file for a ".gnu_debuglink" section. */
212 : : static const char *
213 : 58 : find_debuglink (Elf *elf, GElf_Word *crc)
214 : : {
215 : : size_t shstrndx;
216 [ + - ]: 58 : if (elf_getshdrstrndx (elf, &shstrndx) < 0)
217 : : return NULL;
218 : :
219 : : Elf_Scn *scn = NULL;
220 [ + + ]: 1068 : while ((scn = elf_nextscn (elf, scn)) != NULL)
221 : : {
222 : : GElf_Shdr shdr_mem;
223 : 1046 : GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
224 [ + - ]: 1046 : if (shdr == NULL)
225 : : return NULL;
226 : :
227 : 1046 : const char *name = elf_strptr (elf, shstrndx, shdr->sh_name);
228 [ + - ]: 1046 : if (name == NULL)
229 : : return NULL;
230 : :
231 [ + + ]: 1046 : if (!strcmp (name, ".gnu_debuglink"))
232 : : break;
233 : : }
234 : :
235 [ + + ]: 58 : if (scn == NULL)
236 : : return NULL;
237 : :
238 : : /* Found the .gnu_debuglink section. Extract its contents. */
239 : 36 : Elf_Data *rawdata = elf_rawdata (scn, NULL);
240 [ + - ]: 36 : if (rawdata == NULL)
241 : : return NULL;
242 : :
243 : 36 : Elf_Data crcdata =
244 : : {
245 : : .d_type = ELF_T_WORD,
246 : : .d_buf = crc,
247 : : .d_size = sizeof *crc,
248 : : .d_version = EV_CURRENT,
249 : : };
250 : 72 : Elf_Data conv =
251 : : {
252 : : .d_type = ELF_T_WORD,
253 : 36 : .d_buf = rawdata->d_buf + rawdata->d_size - sizeof *crc,
254 : : .d_size = sizeof *crc,
255 : : .d_version = EV_CURRENT,
256 : : };
257 : :
258 : : GElf_Ehdr ehdr_mem;
259 : 36 : GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
260 [ + - ]: 36 : if (ehdr == NULL)
261 : : return NULL;
262 : :
263 : 36 : Elf_Data *d = gelf_xlatetom (elf, &crcdata, &conv, ehdr->e_ident[EI_DATA]);
264 [ + - ]: 36 : if (d == NULL)
265 : : return NULL;
266 [ - + ]: 36 : assert (d == &crcdata);
267 : :
268 : 58 : return rawdata->d_buf;
269 : : }
270 : :
271 : : /* If the main file might have been prelinked, then we need to
272 : : discover the correct synchronization address between the main and
273 : : debug files. Because of prelink's section juggling, we cannot rely
274 : : on the address_sync computed from PT_LOAD segments (see open_elf).
275 : :
276 : : We will attempt to discover a synchronization address based on the
277 : : section headers instead. But finding a section address that is
278 : : safe to use requires identifying which sections are SHT_PROGBITS.
279 : : We can do that in the main file, but in the debug file all the
280 : : allocated sections have been transformed into SHT_NOBITS so we have
281 : : lost the means to match them up correctly.
282 : :
283 : : The only method left to us is to decode the .gnu.prelink_undo
284 : : section in the prelinked main file. This shows what the sections
285 : : looked like before prelink juggled them--when they still had a
286 : : direct correspondence to the debug file. */
287 : : static Dwfl_Error
288 : 27 : find_prelink_address_sync (Dwfl_Module *mod)
289 : : {
290 : : /* The magic section is only identified by name. */
291 : : size_t shstrndx;
292 [ + - ]: 27 : if (elf_getshdrstrndx (mod->main.elf, &shstrndx) < 0)
293 : : return DWFL_E_LIBELF;
294 : :
295 : : Elf_Scn *scn = NULL;
296 [ + + ]: 662 : while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
297 : : {
298 : : GElf_Shdr shdr_mem;
299 : 643 : GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
300 [ + - ]: 643 : if (unlikely (shdr == NULL))
301 : : return DWFL_E_LIBELF;
302 [ + + ]: 643 : if (shdr->sh_type == SHT_PROGBITS
303 [ + + ]: 330 : && !(shdr->sh_flags & SHF_ALLOC)
304 [ + - ]: 54 : && shdr->sh_name != 0)
305 : : {
306 : 54 : const char *secname = elf_strptr (mod->main.elf, shstrndx,
307 : : shdr->sh_name);
308 [ + - ]: 54 : if (unlikely (secname == NULL))
309 : : return DWFL_E_LIBELF;
310 [ + + ]: 643 : if (!strcmp (secname, ".gnu.prelink_undo"))
311 : : break;
312 : : }
313 : : }
314 : :
315 [ + + ]: 27 : if (scn == NULL)
316 : : /* There was no .gnu.prelink_undo section. */
317 : : return DWFL_E_NOERROR;
318 : :
319 : 8 : Elf_Data *undodata = elf_rawdata (scn, NULL);
320 [ + - ]: 8 : if (unlikely (undodata == NULL))
321 : : return DWFL_E_LIBELF;
322 : :
323 : : /* Decode the section. It consists of the original ehdr, phdrs,
324 : : and shdrs (but omits section 0). */
325 : :
326 : : union
327 : : {
328 : : Elf32_Ehdr e32;
329 : : Elf64_Ehdr e64;
330 : : } ehdr;
331 : 8 : Elf_Data dst =
332 : : {
333 : : .d_buf = &ehdr,
334 : : .d_size = sizeof ehdr,
335 : : .d_type = ELF_T_EHDR,
336 : : .d_version = EV_CURRENT
337 : : };
338 : 8 : Elf_Data src = *undodata;
339 : 8 : src.d_size = gelf_fsize (mod->main.elf, ELF_T_EHDR, 1, EV_CURRENT);
340 : 8 : src.d_type = ELF_T_EHDR;
341 [ + - ]: 8 : if (unlikely (gelf_xlatetom (mod->main.elf, &dst, &src,
342 : : elf_getident (mod->main.elf, NULL)[EI_DATA])
343 : : == NULL))
344 : : return DWFL_E_LIBELF;
345 : :
346 : 8 : size_t shentsize = gelf_fsize (mod->main.elf, ELF_T_SHDR, 1, EV_CURRENT);
347 : 8 : size_t phentsize = gelf_fsize (mod->main.elf, ELF_T_PHDR, 1, EV_CURRENT);
348 : :
349 : : uint_fast16_t phnum;
350 : : uint_fast16_t shnum;
351 [ + + ]: 8 : if (ehdr.e32.e_ident[EI_CLASS] == ELFCLASS32)
352 : : {
353 [ + - ]: 4 : if (ehdr.e32.e_shentsize != shentsize
354 [ + - ]: 4 : || ehdr.e32.e_phentsize != phentsize)
355 : : return DWFL_E_BAD_PRELINK;
356 : 4 : phnum = ehdr.e32.e_phnum;
357 : 4 : shnum = ehdr.e32.e_shnum;
358 : : }
359 : : else
360 : : {
361 [ + - ]: 4 : if (ehdr.e64.e_shentsize != shentsize
362 [ + - ]: 4 : || ehdr.e64.e_phentsize != phentsize)
363 : : return DWFL_E_BAD_PRELINK;
364 : 4 : phnum = ehdr.e64.e_phnum;
365 : 4 : shnum = ehdr.e64.e_shnum;
366 : : }
367 : :
368 : : /* Since prelink does not store the zeroth section header in the undo
369 : : section, it cannot support SHN_XINDEX encoding. */
370 [ + - ]: 8 : if (unlikely (shnum >= SHN_LORESERVE)
371 [ + - ]: 8 : || unlikely (undodata->d_size != (src.d_size
372 : : + phnum * phentsize
373 : : + (shnum - 1) * shentsize)))
374 : : return DWFL_E_BAD_PRELINK;
375 : :
376 : : /* We look at the allocated SHT_PROGBITS (or SHT_NOBITS) sections. (Most
377 : : every file will have some SHT_PROGBITS sections, but it's possible to
378 : : have one with nothing but .bss, i.e. SHT_NOBITS.) The special sections
379 : : that can be moved around have different sh_type values--except for
380 : : .interp, the section that became the PT_INTERP segment. So we exclude
381 : : the SHT_PROGBITS section whose address matches the PT_INTERP p_vaddr.
382 : : For this reason, we must examine the phdrs first to find PT_INTERP. */
383 : :
384 : 8 : GElf_Addr main_interp = 0;
385 : : {
386 : : size_t main_phnum;
387 [ + - ]: 8 : if (unlikely (elf_getphdrnum (mod->main.elf, &main_phnum)))
388 : : return DWFL_E_LIBELF;
389 [ + + ]: 33 : for (size_t i = 0; i < main_phnum; ++i)
390 : : {
391 : : GElf_Phdr phdr;
392 [ - + ]: 29 : if (unlikely (gelf_getphdr (mod->main.elf, i, &phdr) == NULL))
393 : : return DWFL_E_LIBELF;
394 [ + + ]: 29 : if (phdr.p_type == PT_INTERP)
395 : : {
396 : 4 : main_interp = phdr.p_vaddr;
397 : : break;
398 : : }
399 : : }
400 : : }
401 : :
402 : 8 : src.d_buf += src.d_size;
403 : 8 : src.d_type = ELF_T_PHDR;
404 : 8 : src.d_size = phnum * phentsize;
405 : :
406 : 8 : GElf_Addr undo_interp = 0;
407 : : {
408 : 16 : union
409 : : {
410 : 8 : Elf32_Phdr p32[phnum];
411 : 8 : Elf64_Phdr p64[phnum];
412 : 8 : } phdr;
413 : 8 : dst.d_buf = &phdr;
414 : 8 : dst.d_size = sizeof phdr;
415 [ - + ]: 8 : if (unlikely (gelf_xlatetom (mod->main.elf, &dst, &src,
416 : : ehdr.e32.e_ident[EI_DATA]) == NULL))
417 : : return DWFL_E_LIBELF;
418 [ + + ]: 8 : if (ehdr.e32.e_ident[EI_CLASS] == ELFCLASS32)
419 : : {
420 [ + + ]: 16 : for (uint_fast16_t i = 0; i < phnum; ++i)
421 [ + + ]: 14 : if (phdr.p32[i].p_type == PT_INTERP)
422 : : {
423 : 2 : undo_interp = phdr.p32[i].p_vaddr;
424 : 2 : break;
425 : : }
426 : : }
427 : : else
428 : : {
429 [ + + ]: 17 : for (uint_fast16_t i = 0; i < phnum; ++i)
430 [ + + ]: 15 : if (phdr.p64[i].p_type == PT_INTERP)
431 : : {
432 : 2 : undo_interp = phdr.p64[i].p_vaddr;
433 : 2 : break;
434 : : }
435 : : }
436 : : }
437 : :
438 [ + - ]: 8 : if (unlikely ((main_interp == 0) != (undo_interp == 0)))
439 : : return DWFL_E_BAD_PRELINK;
440 : :
441 : 8 : src.d_buf += src.d_size;
442 : 8 : src.d_type = ELF_T_SHDR;
443 : 8 : src.d_size = gelf_fsize (mod->main.elf, ELF_T_SHDR, shnum - 1, EV_CURRENT);
444 : :
445 : 16 : union
446 : : {
447 : 8 : Elf32_Shdr s32[shnum - 1];
448 : 8 : Elf64_Shdr s64[shnum - 1];
449 : 8 : } shdr;
450 : 8 : dst.d_buf = &shdr;
451 : 8 : dst.d_size = sizeof shdr;
452 [ + - ]: 8 : if (unlikely (gelf_xlatetom (mod->main.elf, &dst, &src,
453 : : ehdr.e32.e_ident[EI_DATA]) == NULL))
454 : : return DWFL_E_LIBELF;
455 : :
456 : : /* Now we can look at the original section headers of the main file
457 : : before it was prelinked. First we'll apply our method to the main
458 : : file sections as they are after prelinking, to calculate the
459 : : synchronization address of the main file. Then we'll apply that
460 : : same method to the saved section headers, to calculate the matching
461 : : synchronization address of the debug file.
462 : :
463 : : The method is to consider SHF_ALLOC sections that are either
464 : : SHT_PROGBITS or SHT_NOBITS, excluding the section whose sh_addr
465 : : matches the PT_INTERP p_vaddr. The special sections that can be
466 : : moved by prelink have other types, except for .interp (which
467 : : becomes PT_INTERP). The "real" sections cannot move as such, but
468 : : .bss can be split into .dynbss and .bss, with the total memory
469 : : image remaining the same but being spread across the two sections.
470 : : So we consider the highest section end, which still matches up. */
471 : :
472 : : GElf_Addr highest;
473 : :
474 : : inline void consider_shdr (GElf_Addr interp,
475 : : GElf_Word sh_type,
476 : : GElf_Xword sh_flags,
477 : : GElf_Addr sh_addr,
478 : : GElf_Xword sh_size)
479 : : {
480 [ + + ][ + + ]: 404 : if ((sh_flags & SHF_ALLOC)
[ + + ]
481 [ + + ][ + + ]: 340 : && ((sh_type == SHT_PROGBITS && sh_addr != interp)
[ + + ]
482 [ + + ][ + + ]: 174 : || sh_type == SHT_NOBITS))
[ + + ]
483 : : {
484 : 176 : const GElf_Addr sh_end = sh_addr + sh_size;
485 [ + - ][ + - ]: 302 : if (sh_end > highest)
[ + - ]
486 : : highest = sh_end;
487 : : }
488 : : }
489 : :
490 : : highest = 0;
491 : : scn = NULL;
492 [ + + ]: 223 : while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
493 : : {
494 : : GElf_Shdr sh_mem;
495 : 215 : GElf_Shdr *sh = gelf_getshdr (scn, &sh_mem);
496 [ - + ]: 215 : if (unlikely (sh == NULL))
497 : : return DWFL_E_LIBELF;
498 : 215 : consider_shdr (main_interp, sh->sh_type, sh->sh_flags,
499 : : sh->sh_addr, sh->sh_size);
500 : : }
501 [ + - ]: 8 : if (highest > mod->main.vaddr)
502 : : {
503 : 8 : mod->main.address_sync = highest;
504 : :
505 : : highest = 0;
506 [ + + ]: 8 : if (ehdr.e32.e_ident[EI_CLASS] == ELFCLASS32)
507 [ + + ]: 98 : for (size_t i = 0; i < shnum - 1; ++i)
508 : 188 : consider_shdr (undo_interp, shdr.s32[i].sh_type, shdr.s32[i].sh_flags,
509 : 188 : shdr.s32[i].sh_addr, shdr.s32[i].sh_size);
510 : : else
511 [ + + ]: 99 : for (size_t i = 0; i < shnum - 1; ++i)
512 : 95 : consider_shdr (undo_interp, shdr.s64[i].sh_type, shdr.s64[i].sh_flags,
513 : : shdr.s64[i].sh_addr, shdr.s64[i].sh_size);
514 : :
515 [ + - ]: 8 : if (highest > mod->debug.vaddr)
516 : 27 : mod->debug.address_sync = highest;
517 : : else
518 : : return DWFL_E_BAD_PRELINK;
519 : : }
520 : :
521 : : return DWFL_E_NOERROR;
522 : : }
523 : :
524 : : /* Find the separate debuginfo file for this module and open libelf on it.
525 : : When we return success, MOD->debug is set up. */
526 : : static Dwfl_Error
527 : 76 : find_debuginfo (Dwfl_Module *mod)
528 : : {
529 [ + + ]: 76 : if (mod->debug.elf != NULL)
530 : : return DWFL_E_NOERROR;
531 : :
532 : 58 : GElf_Word debuglink_crc = 0;
533 : 58 : const char *debuglink_file = find_debuglink (mod->main.elf, &debuglink_crc);
534 : :
535 : 58 : mod->debug.fd = (*mod->dwfl->callbacks->find_debuginfo) (MODCB_ARGS (mod),
536 : 58 : mod->main.name,
537 : : debuglink_file,
538 : : debuglink_crc,
539 : : &mod->debug.name);
540 : 58 : Dwfl_Error result = open_elf (mod, &mod->debug);
541 [ + + ][ + + ]: 58 : if (result == DWFL_E_NOERROR && mod->debug.address_sync != 0)
542 : 76 : result = find_prelink_address_sync (mod);
543 : : return result;
544 : : }
545 : :
546 : :
547 : : /* Try to find a symbol table in FILE.
548 : : Returns DWFL_E_NOERROR if a proper one is found.
549 : : Returns DWFL_E_NO_SYMTAB if not, but still sets results for SHT_DYNSYM. */
550 : : static Dwfl_Error
551 : 97 : load_symtab (struct dwfl_file *file, struct dwfl_file **symfile,
552 : : Elf_Scn **symscn, Elf_Scn **xndxscn,
553 : : size_t *syments, int *first_global, GElf_Word *strshndx)
554 : : {
555 : 97 : bool symtab = false;
556 : 97 : Elf_Scn *scn = NULL;
557 [ + + ]: 2866 : while ((scn = elf_nextscn (file->elf, scn)) != NULL)
558 : : {
559 : 2769 : GElf_Shdr shdr_mem, *shdr = gelf_getshdr (scn, &shdr_mem);
560 [ + - ]: 2769 : if (shdr != NULL)
561 [ + + - + ]: 2769 : switch (shdr->sh_type)
562 : : {
563 : : case SHT_SYMTAB:
564 : 76 : symtab = true;
565 : 76 : *symscn = scn;
566 : 76 : *symfile = file;
567 : 76 : *strshndx = shdr->sh_link;
568 : 76 : *syments = shdr->sh_size / shdr->sh_entsize;
569 : 76 : *first_global = shdr->sh_info;
570 [ + - ]: 76 : if (*xndxscn != NULL)
571 : : return DWFL_E_NOERROR;
572 : : break;
573 : :
574 : : case SHT_DYNSYM:
575 [ + - ]: 52 : if (symtab)
576 : : break;
577 : : /* Use this if need be, but keep looking for SHT_SYMTAB. */
578 : 52 : *symscn = scn;
579 : 52 : *symfile = file;
580 : 52 : *strshndx = shdr->sh_link;
581 : 52 : *syments = shdr->sh_size / shdr->sh_entsize;
582 : 52 : break;
583 : :
584 : : case SHT_SYMTAB_SHNDX:
585 : 0 : *xndxscn = scn;
586 [ # # ]: 2769 : if (symtab)
587 : : return DWFL_E_NOERROR;
588 : : break;
589 : :
590 : : default:
591 : : break;
592 : : }
593 : : }
594 : :
595 [ + + ]: 97 : if (symtab)
596 : : /* We found one, though no SHT_SYMTAB_SHNDX to go with it. */
597 : : return DWFL_E_NOERROR;
598 : :
599 : : /* We found no SHT_SYMTAB, so any SHT_SYMTAB_SHNDX was bogus.
600 : : We might have found an SHT_DYNSYM and set *SYMSCN et al though. */
601 : 21 : *xndxscn = NULL;
602 : 97 : return DWFL_E_NO_SYMTAB;
603 : : }
604 : :
605 : :
606 : : /* Translate addresses into file offsets.
607 : : OFFS[*] start out zero and remain zero if unresolved. */
608 : : static void
609 : 2 : find_offsets (Elf *elf, size_t phnum, size_t n,
610 : : GElf_Addr addrs[n], GElf_Off offs[n])
611 : : {
612 : 2 : size_t unsolved = n;
613 [ + + ]: 12 : for (size_t i = 0; i < phnum; ++i)
614 : : {
615 : : GElf_Phdr phdr_mem;
616 : 10 : GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
617 [ + - ][ + + ]: 10 : if (phdr != NULL && phdr->p_type == PT_LOAD && phdr->p_memsz > 0)
[ + - ]
618 [ + + ]: 20 : for (size_t j = 0; j < n; ++j)
619 [ + + ]: 16 : if (offs[j] == 0
620 [ + + ]: 10 : && addrs[j] >= phdr->p_vaddr
621 [ + - ]: 6 : && addrs[j] - phdr->p_vaddr < phdr->p_filesz)
622 : : {
623 : 6 : offs[j] = addrs[j] - phdr->p_vaddr + phdr->p_offset;
624 [ + - ]: 6 : if (--unsolved == 0)
625 : : break;
626 : : }
627 : : }
628 : 2 : }
629 : :
630 : : /* Try to find a dynamic symbol table via phdrs. */
631 : : static void
632 : 2 : find_dynsym (Dwfl_Module *mod)
633 : : {
634 : : GElf_Ehdr ehdr_mem;
635 : 2 : GElf_Ehdr *ehdr = gelf_getehdr (mod->main.elf, &ehdr_mem);
636 : :
637 : : size_t phnum;
638 [ + - ]: 2 : if (unlikely (elf_getphdrnum (mod->main.elf, &phnum) != 0))
639 : : return;
640 : :
641 [ + - ]: 8 : for (size_t i = 0; i < phnum; ++i)
642 : : {
643 : : GElf_Phdr phdr_mem;
644 : 6 : GElf_Phdr *phdr = gelf_getphdr (mod->main.elf, i, &phdr_mem);
645 [ + - ]: 6 : if (phdr == NULL)
646 : : break;
647 : :
648 [ + + ]: 6 : if (phdr->p_type == PT_DYNAMIC)
649 : : {
650 : : /* Examine the dynamic section for the pointers we need. */
651 : :
652 : 2 : Elf_Data *data = elf_getdata_rawchunk (mod->main.elf,
653 : 2 : phdr->p_offset, phdr->p_filesz,
654 : : ELF_T_DYN);
655 [ - + ]: 2 : if (data == NULL)
656 : 0 : continue;
657 : :
658 : : enum
659 : : {
660 : : i_symtab,
661 : : i_strtab,
662 : : i_hash,
663 : : i_gnu_hash,
664 : : i_max
665 : : };
666 : 2 : GElf_Addr addrs[i_max] = { 0, };
667 : 2 : GElf_Xword strsz = 0;
668 : 2 : size_t n = data->d_size / gelf_fsize (mod->main.elf,
669 : : ELF_T_DYN, 1, EV_CURRENT);
670 [ + - ]: 32 : for (size_t j = 0; j < n; ++j)
671 : : {
672 : : GElf_Dyn dyn_mem;
673 : 30 : GElf_Dyn *dyn = gelf_getdyn (data, j, &dyn_mem);
674 [ + - ]: 30 : if (dyn != NULL)
675 [ + - + + : 30 : switch (dyn->d_tag)
+ + + ]
676 : : {
677 : : case DT_SYMTAB:
678 : 2 : addrs[i_symtab] = dyn->d_un.d_ptr;
679 : 2 : continue;
680 : :
681 : : case DT_HASH:
682 : 0 : addrs[i_hash] = dyn->d_un.d_ptr;
683 : 0 : continue;
684 : :
685 : : case DT_GNU_HASH:
686 : 2 : addrs[i_gnu_hash] = dyn->d_un.d_ptr;
687 : 2 : continue;
688 : :
689 : : case DT_STRTAB:
690 : 2 : addrs[i_strtab] = dyn->d_un.d_ptr;
691 : 2 : continue;
692 : :
693 : : case DT_STRSZ:
694 : 2 : strsz = dyn->d_un.d_val;
695 : 2 : continue;
696 : :
697 : : default:
698 : 20 : continue;
699 : :
700 : : case DT_NULL:
701 : : break;
702 : : }
703 : : break;
704 : : }
705 : :
706 : : /* Translate pointers into file offsets. */
707 : 2 : GElf_Off offs[i_max] = { 0, };
708 : 2 : find_offsets (mod->main.elf, phnum, i_max, addrs, offs);
709 : :
710 : : /* Figure out the size of the symbol table. */
711 [ - + ]: 2 : if (offs[i_hash] != 0)
712 : : {
713 : : /* In the original format, .hash says the size of .dynsym. */
714 : :
715 [ # # ][ # # ]: 0 : size_t entsz = SH_ENTSIZE_HASH (ehdr);
[ # # ]
716 [ # # ]: 0 : data = elf_getdata_rawchunk (mod->main.elf,
717 : 0 : offs[i_hash] + entsz, entsz,
718 : : entsz == 4 ? ELF_T_WORD
719 : : : ELF_T_XWORD);
720 [ # # ]: 0 : if (data != NULL)
721 : 0 : mod->syments = (entsz == 4
722 : 0 : ? *(const GElf_Word *) data->d_buf
723 [ # # ]: 0 : : *(const GElf_Xword *) data->d_buf);
724 : : }
725 [ + - ][ + - ]: 2 : if (offs[i_gnu_hash] != 0 && mod->syments == 0)
726 : : {
727 : : /* In the new format, we can derive it with some work. */
728 : :
729 : : const struct
730 : : {
731 : : Elf32_Word nbuckets;
732 : : Elf32_Word symndx;
733 : : Elf32_Word maskwords;
734 : : Elf32_Word shift2;
735 : : } *header;
736 : :
737 : 2 : data = elf_getdata_rawchunk (mod->main.elf, offs[i_gnu_hash],
738 : : sizeof *header, ELF_T_WORD);
739 [ + - ]: 2 : if (data != NULL)
740 : : {
741 : 2 : header = data->d_buf;
742 : 2 : Elf32_Word nbuckets = header->nbuckets;
743 : 2 : Elf32_Word symndx = header->symndx;
744 : 4 : GElf_Off buckets_at = (offs[i_gnu_hash] + sizeof *header
745 : 2 : + (gelf_getclass (mod->main.elf)
746 : : * sizeof (Elf32_Word)
747 : 2 : * header->maskwords));
748 : :
749 : 2 : data = elf_getdata_rawchunk (mod->main.elf, buckets_at,
750 : : nbuckets * sizeof (Elf32_Word),
751 : : ELF_T_WORD);
752 [ + + ]: 2 : if (data != NULL && symndx < nbuckets)
753 : : {
754 : 1 : const Elf32_Word *const buckets = data->d_buf;
755 : 1 : Elf32_Word maxndx = symndx;
756 [ + + ]: 4 : for (Elf32_Word bucket = 0; bucket < nbuckets; ++bucket)
757 [ + + ]: 3 : if (buckets[bucket] > maxndx)
758 : 1 : maxndx = buckets[bucket];
759 : :
760 : 1 : GElf_Off hasharr_at = (buckets_at
761 : : + nbuckets * sizeof (Elf32_Word));
762 : 1 : hasharr_at += (maxndx - symndx) * sizeof (Elf32_Word);
763 : : do
764 : : {
765 : 1 : data = elf_getdata_rawchunk (mod->main.elf,
766 : : hasharr_at,
767 : : sizeof (Elf32_Word),
768 : : ELF_T_WORD);
769 [ + - ]: 1 : if (data != NULL
770 [ + - ]: 1 : && (*(const Elf32_Word *) data->d_buf & 1u))
771 : : {
772 : 1 : mod->syments = maxndx + 1;
773 : 1 : break;
774 : : }
775 : 0 : ++maxndx;
776 : 0 : hasharr_at += sizeof (Elf32_Word);
777 [ # # ]: 0 : } while (data != NULL);
778 : : }
779 : : }
780 : : }
781 [ + - ][ + + ]: 2 : if (offs[i_strtab] > offs[i_symtab] && mod->syments == 0)
782 : 1 : mod->syments = ((offs[i_strtab] - offs[i_symtab])
783 : 1 : / gelf_fsize (mod->main.elf,
784 : : ELF_T_SYM, 1, EV_CURRENT));
785 : :
786 [ + - ]: 2 : if (mod->syments > 0)
787 : : {
788 : 2 : mod->symdata = elf_getdata_rawchunk (mod->main.elf,
789 : 2 : offs[i_symtab],
790 : : gelf_fsize (mod->main.elf,
791 : : ELF_T_SYM,
792 : : mod->syments,
793 : : EV_CURRENT),
794 : : ELF_T_SYM);
795 [ + - ]: 2 : if (mod->symdata != NULL)
796 : : {
797 : 2 : mod->symstrdata = elf_getdata_rawchunk (mod->main.elf,
798 : 2 : offs[i_strtab],
799 : : strsz,
800 : : ELF_T_BYTE);
801 [ - + ]: 2 : if (mod->symstrdata == NULL)
802 : 0 : mod->symdata = NULL;
803 : : }
804 [ - + ]: 2 : if (mod->symdata == NULL)
805 : 0 : mod->symerr = DWFL_E (LIBELF, elf_errno ());
806 : : else
807 : : {
808 : 2 : mod->symfile = &mod->main;
809 : 6 : mod->symerr = DWFL_E_NOERROR;
810 : : }
811 : : return;
812 : : }
813 : : }
814 : : }
815 : : }
816 : :
817 : : /* Try to find a symbol table in either MOD->main.elf or MOD->debug.elf. */
818 : : static void
819 : 642059 : find_symtab (Dwfl_Module *mod)
820 : : {
821 [ + + ]: 642059 : if (mod->symdata != NULL /* Already done. */
822 [ + + ]: 83 : || mod->symerr != DWFL_E_NOERROR) /* Cached previous failure. */
823 : : return;
824 : :
825 : 81 : __libdwfl_getelf (mod);
826 : 81 : mod->symerr = mod->elferr;
827 [ + + ]: 81 : if (mod->symerr != DWFL_E_NOERROR)
828 : : return;
829 : :
830 : 79 : mod->first_global = -1; /* Unknown, unless explicitly set by load_symtab. */
831 : :
832 : : /* First see if the main ELF file has the debugging information. */
833 : 79 : Elf_Scn *symscn = NULL, *xndxscn = NULL;
834 : : GElf_Word strshndx;
835 : 79 : mod->symerr = load_symtab (&mod->main, &mod->symfile, &symscn,
836 : : &xndxscn, &mod->syments, &mod->first_global,
837 : : &strshndx);
838 [ + + - ]: 79 : switch (mod->symerr)
839 : : {
840 : : default:
841 : : return;
842 : :
843 : : case DWFL_E_NOERROR:
844 : : break;
845 : :
846 : : case DWFL_E_NO_SYMTAB:
847 : : /* Now we have to look for a separate debuginfo file. */
848 : 21 : mod->symerr = find_debuginfo (mod);
849 [ + + - ]: 21 : switch (mod->symerr)
850 : : {
851 : : default:
852 : : return;
853 : :
854 : : case DWFL_E_NOERROR:
855 : 18 : mod->symerr = load_symtab (&mod->debug, &mod->symfile, &symscn,
856 : : &xndxscn, &mod->syments,
857 : : &mod->first_global, &strshndx);
858 : 18 : break;
859 : :
860 : : case DWFL_E_CB: /* The find_debuginfo hook failed. */
861 : 3 : mod->symerr = DWFL_E_NO_SYMTAB;
862 : 3 : break;
863 : : }
864 : :
865 [ + + - ]: 21 : switch (mod->symerr)
866 : : {
867 : : default:
868 : : return;
869 : :
870 : : case DWFL_E_NOERROR:
871 : : break;
872 : :
873 : : case DWFL_E_NO_SYMTAB:
874 [ + + ]: 3 : if (symscn != NULL)
875 : : {
876 : : /* We still have the dynamic symbol table. */
877 : 1 : mod->symerr = DWFL_E_NOERROR;
878 : 1 : break;
879 : : }
880 : :
881 : : /* Last ditch, look for dynamic symbols without section headers. */
882 : 2 : find_dynsym (mod);
883 : : return;
884 : : }
885 : : break;
886 : : }
887 : :
888 : : /* This does some sanity checks on the string table section. */
889 [ - + ]: 77 : if (elf_strptr (mod->symfile->elf, strshndx, 0) == NULL)
890 : : {
891 : : elferr:
892 : 0 : mod->symerr = DWFL_E (LIBELF, elf_errno ());
893 : : return;
894 : : }
895 : :
896 : : /* Cache the data; MOD->syments and MOD->first_global were set above. */
897 : :
898 : 77 : mod->symstrdata = elf_getdata (elf_getscn (mod->symfile->elf, strshndx),
899 : : NULL);
900 [ - + ]: 77 : if (mod->symstrdata == NULL)
901 : : goto elferr;
902 : :
903 [ + - ]: 77 : if (xndxscn == NULL)
904 : 77 : mod->symxndxdata = NULL;
905 : : else
906 : : {
907 : 0 : mod->symxndxdata = elf_getdata (xndxscn, NULL);
908 [ # # ]: 0 : if (mod->symxndxdata == NULL)
909 : : goto elferr;
910 : : }
911 : :
912 : 77 : mod->symdata = elf_getdata (symscn, NULL);
913 [ - + ]: 642059 : if (mod->symdata == NULL)
914 : : goto elferr;
915 : : }
916 : :
917 : :
918 : : /* Try to open a libebl backend for MOD. */
919 : : Dwfl_Error
920 : : internal_function
921 : 61 : __libdwfl_module_getebl (Dwfl_Module *mod)
922 : : {
923 [ + + ]: 61 : if (mod->ebl == NULL)
924 : : {
925 : 58 : __libdwfl_getelf (mod);
926 [ + - ]: 58 : if (mod->elferr != DWFL_E_NOERROR)
927 : : return mod->elferr;
928 : :
929 : 58 : mod->ebl = ebl_openbackend (mod->main.elf);
930 [ + - ]: 61 : if (mod->ebl == NULL)
931 : : return DWFL_E_LIBEBL;
932 : : }
933 : : return DWFL_E_NOERROR;
934 : : }
935 : :
936 : : /* Try to start up libdw on DEBUGFILE. */
937 : : static Dwfl_Error
938 : 5155 : load_dw (Dwfl_Module *mod, struct dwfl_file *debugfile)
939 : : {
940 [ + + ][ + + ]: 5155 : if (mod->e_type == ET_REL && !debugfile->relocated)
941 : : {
942 : 10 : const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
943 : :
944 : : /* The debugging sections have to be relocated. */
945 [ + - ]: 10 : if (cb->section_address == NULL)
946 : : return DWFL_E_NOREL;
947 : :
948 : 10 : Dwfl_Error error = __libdwfl_module_getebl (mod);
949 [ + - ]: 10 : if (error != DWFL_E_NOERROR)
950 : : return error;
951 : :
952 : 10 : find_symtab (mod);
953 : 10 : Dwfl_Error result = mod->symerr;
954 [ + - ]: 10 : if (result == DWFL_E_NOERROR)
955 : 10 : result = __libdwfl_relocate (mod, debugfile->elf, true);
956 [ + - ]: 10 : if (result != DWFL_E_NOERROR)
957 : : return result;
958 : :
959 : : /* Don't keep the file descriptors around. */
960 [ - + ][ # # ]: 10 : if (mod->main.fd != -1 && elf_cntl (mod->main.elf, ELF_C_FDREAD) == 0)
961 : : {
962 : 0 : close (mod->main.fd);
963 : 0 : mod->main.fd = -1;
964 : : }
965 [ + + ][ + - ]: 10 : if (debugfile->fd != -1 && elf_cntl (debugfile->elf, ELF_C_FDREAD) == 0)
966 : : {
967 : 1 : close (debugfile->fd);
968 : 1 : debugfile->fd = -1;
969 : : }
970 : : }
971 : :
972 : 5155 : mod->dw = INTUSE(dwarf_begin_elf) (debugfile->elf, DWARF_C_READ, NULL);
973 [ + + ]: 5155 : if (mod->dw == NULL)
974 : : {
975 : 56 : int err = INTUSE(dwarf_errno) ();
976 [ + + ]: 56 : return err == DWARF_E_NO_DWARF ? DWFL_E_NO_DWARF : DWFL_E (LIBDW, err);
977 : : }
978 : :
979 : : /* Until we have iterated through all CU's, we might do lazy lookups. */
980 : 5099 : mod->lazycu = 1;
981 : :
982 : 5155 : return DWFL_E_NOERROR;
983 : : }
984 : :
985 : : /* Try to start up libdw on either the main file or the debuginfo file. */
986 : : static void
987 : 5226 : find_dw (Dwfl_Module *mod)
988 : : {
989 [ + + ]: 5226 : if (mod->dw != NULL /* Already done. */
990 [ + + ]: 5197 : || mod->dwerr != DWFL_E_NOERROR) /* Cached previous failure. */
991 : : return;
992 : :
993 : 5134 : __libdwfl_getelf (mod);
994 : 5134 : mod->dwerr = mod->elferr;
995 [ + + ]: 5134 : if (mod->dwerr != DWFL_E_NOERROR)
996 : : return;
997 : :
998 : : /* First see if the main ELF file has the debugging information. */
999 : 5127 : mod->dwerr = load_dw (mod, &mod->main);
1000 [ + + + ]: 5127 : switch (mod->dwerr)
1001 : : {
1002 : : case DWFL_E_NOERROR:
1003 : 5071 : mod->debug.elf = mod->main.elf;
1004 : 5071 : mod->debug.address_sync = mod->main.address_sync;
1005 : 5071 : return;
1006 : :
1007 : : case DWFL_E_NO_DWARF:
1008 : : break;
1009 : :
1010 : : default:
1011 : : goto canonicalize;
1012 : : }
1013 : :
1014 : : /* Now we have to look for a separate debuginfo file. */
1015 : 55 : mod->dwerr = find_debuginfo (mod);
1016 [ + + - ]: 55 : switch (mod->dwerr)
1017 : : {
1018 : : case DWFL_E_NOERROR:
1019 : 28 : mod->dwerr = load_dw (mod, &mod->debug);
1020 : 28 : break;
1021 : :
1022 : : case DWFL_E_CB: /* The find_debuginfo hook failed. */
1023 : 27 : mod->dwerr = DWFL_E_NO_DWARF;
1024 : 27 : return;
1025 : :
1026 : : default:
1027 : : break;
1028 : : }
1029 : :
1030 : : canonicalize:
1031 : 5226 : mod->dwerr = __libdwfl_canon_error (mod->dwerr);
1032 : : }
1033 : :
1034 : : Dwarf *
1035 : 5226 : dwfl_module_getdwarf (Dwfl_Module *mod, Dwarf_Addr *bias)
1036 : : {
1037 [ + - ]: 5226 : if (mod == NULL)
1038 : : return NULL;
1039 : :
1040 : 5226 : find_dw (mod);
1041 [ + + ]: 5226 : if (mod->dwerr == DWFL_E_NOERROR)
1042 : : {
1043 : : /* If dwfl_module_getelf was used previously, then partial apply
1044 : : relocation to miscellaneous sections in the debug file too. */
1045 [ + + ]: 5128 : if (mod->e_type == ET_REL
1046 [ + + ][ + - ]: 16 : && mod->main.relocated && ! mod->debug.relocated)
1047 : : {
1048 : 10 : mod->debug.relocated = true;
1049 [ - + ]: 10 : if (mod->debug.elf != mod->main.elf)
1050 : 0 : (void) __libdwfl_relocate (mod, mod->debug.elf, false);
1051 : : }
1052 : :
1053 : 5128 : *bias = dwfl_adjusted_dwarf_addr (mod, 0);
1054 : 5128 : return mod->dw;
1055 : : }
1056 : :
1057 : 98 : __libdwfl_seterrno (mod->dwerr);
1058 : 5226 : return NULL;
1059 : : }
1060 : : INTDEF (dwfl_module_getdwarf)
1061 : :
1062 : : int
1063 : 642049 : dwfl_module_getsymtab (Dwfl_Module *mod)
1064 : : {
1065 [ + - ]: 642049 : if (mod == NULL)
1066 : : return -1;
1067 : :
1068 : 642049 : find_symtab (mod);
1069 [ + + ]: 642049 : if (mod->symerr == DWFL_E_NOERROR)
1070 : 642045 : return mod->syments;
1071 : :
1072 : 4 : __libdwfl_seterrno (mod->symerr);
1073 : 642049 : return -1;
1074 : : }
1075 : 15312 : INTDEF (dwfl_module_getsymtab)
|