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