1/* Maintenance of module list in libdwfl.
2   Copyright (C) 2005, 2006, 2007, 2008, 2014, 2015 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#ifdef HAVE_CONFIG_H
30# include <config.h>
31#endif
32
33#include "libdwflP.h"
34#include "../libdw/cfi.h"
35#include <search.h>
36
37static void
38free_cu (struct dwfl_cu *cu)
39{
40  if (cu->lines != NULL)
41    free (cu->lines);
42  free (cu);
43}
44
45static void
46nofree (void *arg __attribute__ ((unused)))
47{
48}
49
50static void
51free_file (struct dwfl_file *file)
52{
53  free (file->name);
54
55  /* Close the fd only on the last reference.  */
56  if (file->elf != NULL && elf_end (file->elf) == 0 && file->fd != -1)
57    close (file->fd);
58}
59
60void
61internal_function
62__libdwfl_module_free (Dwfl_Module *mod)
63{
64  if (mod->lazy_cu_root != NULL)
65    tdestroy (mod->lazy_cu_root, nofree);
66
67  if (mod->aranges != NULL)
68    free (mod->aranges);
69
70  if (mod->cu != NULL)
71    {
72      for (size_t i = 0; i < mod->ncu; ++i)
73	free_cu (mod->cu[i]);
74      free (mod->cu);
75    }
76
77  /* We might have primed the Dwarf_CFI ebl cache with our own ebl
78     in __libdwfl_set_cfi. Make sure we don't free it twice.  */
79  if (mod->eh_cfi != NULL)
80    {
81      if (mod->eh_cfi->ebl != NULL && mod->eh_cfi->ebl == mod->ebl)
82	mod->eh_cfi->ebl = NULL;
83      dwarf_cfi_end (mod->eh_cfi);
84    }
85
86  if (mod->dwarf_cfi != NULL)
87    {
88      if (mod->dwarf_cfi->ebl != NULL && mod->dwarf_cfi->ebl == mod->ebl)
89	mod->dwarf_cfi->ebl = NULL;
90      /* We don't need to explicitly destroy the dwarf_cfi.
91	 That will be done by dwarf_end.  */
92    }
93
94  if (mod->dw != NULL)
95    {
96      INTUSE(dwarf_end) (mod->dw);
97      if (mod->alt != NULL)
98	{
99	  INTUSE(dwarf_end) (mod->alt);
100	  if (mod->alt_elf != NULL)
101	    elf_end (mod->alt_elf);
102	  if (mod->alt_fd != -1)
103	    close (mod->alt_fd);
104	}
105    }
106
107  if (mod->ebl != NULL)
108    ebl_closebackend (mod->ebl);
109
110  if (mod->debug.elf != mod->main.elf)
111    free_file (&mod->debug);
112  free_file (&mod->main);
113  free_file (&mod->aux_sym);
114
115  if (mod->build_id_bits != NULL)
116    free (mod->build_id_bits);
117
118  if (mod->reloc_info != NULL)
119    free (mod->reloc_info);
120
121  free (mod->name);
122  free (mod->elfdir);
123  free (mod);
124}
125
126void
127dwfl_report_begin_add (Dwfl *dwfl __attribute__ ((unused)))
128{
129  /* The lookup table will be cleared on demand, there is nothing we need
130     to do here.  */
131}
132INTDEF (dwfl_report_begin_add)
133
134void
135dwfl_report_begin (Dwfl *dwfl)
136{
137  /* Clear the segment lookup table.  */
138  dwfl->lookup_elts = 0;
139
140  for (Dwfl_Module *m = dwfl->modulelist; m != NULL; m = m->next)
141    m->gc = true;
142
143  dwfl->offline_next_address = OFFLINE_REDZONE;
144}
145INTDEF (dwfl_report_begin)
146
147static inline Dwfl_Module *
148use (Dwfl_Module *mod, Dwfl_Module **tailp, Dwfl *dwfl)
149{
150  mod->next = *tailp;
151  *tailp = mod;
152
153  if (unlikely (dwfl->lookup_module != NULL))
154    {
155      free (dwfl->lookup_module);
156      dwfl->lookup_module = NULL;
157    }
158
159  return mod;
160}
161
162/* Report that a module called NAME spans addresses [START, END).
163   Returns the module handle, either existing or newly allocated,
164   or returns a null pointer for an allocation error.  */
165Dwfl_Module *
166dwfl_report_module (Dwfl *dwfl, const char *name,
167		    GElf_Addr start, GElf_Addr end)
168{
169  Dwfl_Module **tailp = &dwfl->modulelist, **prevp = tailp;
170
171  for (Dwfl_Module *m = *prevp; m != NULL; m = *(prevp = &m->next))
172    {
173      if (m->low_addr == start && m->high_addr == end
174	  && !strcmp (m->name, name))
175	{
176	  /* This module is still here.  Move it to the place in the list
177	     after the last module already reported.  */
178	  *prevp = m->next;
179	  m->gc = false;
180	  return use (m, tailp, dwfl);
181	}
182
183      if (! m->gc)
184	tailp = &m->next;
185    }
186
187  Dwfl_Module *mod = calloc (1, sizeof *mod);
188  if (mod == NULL)
189    goto nomem;
190
191  mod->name = strdup (name);
192  if (mod->name == NULL)
193    {
194      free (mod);
195    nomem:
196      __libdwfl_seterrno (DWFL_E_NOMEM);
197      return NULL;
198    }
199
200  mod->low_addr = start;
201  mod->high_addr = end;
202  mod->dwfl = dwfl;
203
204  return use (mod, tailp, dwfl);
205}
206INTDEF (dwfl_report_module)
207
208
209/* Finish reporting the current set of modules to the library.
210   If REMOVED is not null, it's called for each module that
211   existed before but was not included in the current report.
212   Returns a nonzero return value from the callback.
213   DWFL cannot be used until this function has returned zero.  */
214int
215dwfl_report_end (Dwfl *dwfl,
216		 int (*removed) (Dwfl_Module *, void *,
217				 const char *, Dwarf_Addr,
218				 void *arg),
219		 void *arg)
220{
221  Dwfl_Module **tailp = &dwfl->modulelist;
222  while (*tailp != NULL)
223    {
224      Dwfl_Module *m = *tailp;
225      if (m->gc && removed != NULL)
226	{
227	  int result = (*removed) (MODCB_ARGS (m), arg);
228	  if (result != 0)
229	    return result;
230	}
231      if (m->gc)
232	{
233	  *tailp = m->next;
234	  __libdwfl_module_free (m);
235	}
236      else
237	tailp = &m->next;
238    }
239
240  return 0;
241}
242INTDEF (dwfl_report_end)
243