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