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