1 /* Find line information for given file/line/column triple.
2    Copyright (C) 2005-2009 Red Hat, Inc.
3    This file is part of elfutils.
4    Written by Ulrich Drepper <drepper@redhat.com>, 2005.
5 
6    This file is free software; you can redistribute it and/or modify
7    it under the terms of either
8 
9      * the GNU Lesser General Public License as published by the Free
10        Software Foundation; either version 3 of the License, or (at
11        your option) any later version
12 
13    or
14 
15      * the GNU General Public License as published by the Free
16        Software Foundation; either version 2 of the License, or (at
17        your option) any later version
18 
19    or both in parallel, as here.
20 
21    elfutils is distributed in the hope that it will be useful, but
22    WITHOUT ANY WARRANTY; without even the implied warranty of
23    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24    General Public License for more details.
25 
26    You should have received copies of the GNU General Public License and
27    the GNU Lesser General Public License along with this program.  If
28    not, see <http://www.gnu.org/licenses/>.  */
29 
30 #ifdef HAVE_CONFIG_H
31 # include <config.h>
32 #endif
33 
34 #include <assert.h>
35 #include <limits.h>
36 #include <stdlib.h>
37 #include <string.h>
38 
39 #include "libdwP.h"
40 
41 
42 int
dwarf_getsrc_file(Dwarf * dbg,const char * fname,int lineno,int column,Dwarf_Line *** srcsp,size_t * nsrcs)43 dwarf_getsrc_file (Dwarf *dbg, const char *fname, int lineno, int column,
44 		   Dwarf_Line ***srcsp, size_t *nsrcs)
45 {
46   if (dbg == NULL)
47     return -1;
48 
49   bool is_basename = strchr (fname, '/') == NULL;
50 
51   size_t max_match = *nsrcs ?: ~0u;
52   size_t act_match = *nsrcs;
53   size_t cur_match = 0;
54   Dwarf_Line **match = *nsrcs == 0 ? NULL : *srcsp;
55 
56   size_t cuhl;
57   Dwarf_Off noff;
58   for (Dwarf_Off off = 0;
59        INTUSE(dwarf_nextcu) (dbg, off, &noff, &cuhl, NULL, NULL, NULL) == 0;
60        off = noff)
61     {
62       Dwarf_Die cudie_mem;
63       Dwarf_Die *cudie = INTUSE(dwarf_offdie) (dbg, off + cuhl, &cudie_mem);
64       if (cudie == NULL)
65 	continue;
66 
67       /* Get the line number information for this file.  */
68       Dwarf_Lines *lines;
69       size_t nlines;
70       if (INTUSE(dwarf_getsrclines) (cudie, &lines, &nlines) != 0)
71 	{
72 	  /* Ignore a CU that just has no DW_AT_stmt_list at all.  */
73 	  int error = INTUSE(dwarf_errno) ();
74 	  if (error == 0)
75 	    continue;
76 	  __libdw_seterrno (error);
77 	  return -1;
78 	}
79 
80       /* Search through all the line number records for a matching
81 	 file and line/column number.  If any of the numbers is zero,
82 	 no match is performed.  */
83       unsigned int lastfile = UINT_MAX;
84       bool lastmatch = false;
85       for (size_t cnt = 0; cnt < nlines; ++cnt)
86 	{
87 	  Dwarf_Line *line = &lines->info[cnt];
88 
89 	  if (lastfile != line->file)
90 	    {
91 	      lastfile = line->file;
92 	      if (lastfile >= line->files->nfiles)
93 		{
94 		  __libdw_seterrno (DWARF_E_INVALID_DWARF);
95 		  return -1;
96 		}
97 
98 	      /* Match the name with the name the user provided.  */
99 	      const char *fname2 = line->files->info[lastfile].name;
100 	      if (is_basename)
101 		lastmatch = strcmp (basename (fname2), fname) == 0;
102 	      else
103 		lastmatch = strcmp (fname2, fname) == 0;
104 	    }
105 	  if (!lastmatch)
106 	    continue;
107 
108 	  /* See whether line and possibly column match.  */
109 	  if (lineno != 0
110 	      && (lineno > line->line
111 		  || (column != 0 && column > line->column)))
112 	    /* Cannot match.  */
113 	    continue;
114 
115 	  /* Determine whether this is the best match so far.  */
116 	  size_t inner;
117 	  for (inner = 0; inner < cur_match; ++inner)
118 	    if (match[inner]->files == line->files
119 		&& match[inner]->file == line->file)
120 	      break;
121 	  if (inner < cur_match
122 	      && (match[inner]->line != line->line
123 		  || match[inner]->line != lineno
124 		  || (column != 0
125 		      && (match[inner]->column != line->column
126 			  || match[inner]->column != column))))
127 	    {
128 	      /* We know about this file already.  If this is a better
129 		 match for the line number, use it.  */
130 	      if (match[inner]->line >= line->line
131 		  && (match[inner]->line != line->line
132 		      || match[inner]->column >= line->column))
133 		/*  Use the new line.  Otherwise the old one.  */
134 		match[inner] = line;
135 	      continue;
136 	    }
137 
138 	  if (cur_match < max_match)
139 	    {
140 	      if (cur_match == act_match)
141 		{
142 		  /* Enlarge the array for the results.  */
143 		  act_match += 10;
144 		  Dwarf_Line **newp = realloc (match,
145 					       act_match
146 					       * sizeof (Dwarf_Line *));
147 		  if (newp == NULL)
148 		    {
149 		      free (match);
150 		      __libdw_seterrno (DWARF_E_NOMEM);
151 		      return -1;
152 		    }
153 		  match = newp;
154 		}
155 
156 	      match[cur_match++] = line;
157 	    }
158 	}
159 
160       /* If we managed to find as many matches as the user requested
161 	 already, there is no need to go on to the next CU.  */
162       if (cur_match == max_match)
163 	break;
164     }
165 
166   if (cur_match > 0)
167     {
168       assert (*nsrcs == 0 || *srcsp == match);
169 
170       *nsrcs = cur_match;
171       *srcsp = match;
172 
173       return 0;
174     }
175 
176   __libdw_seterrno (DWARF_E_NO_MATCH);
177   return -1;
178 }
179