1 /* Iterate through the debug line table.
2    Copyright (C) 2018 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 <libdwP.h>
34 
35 
36 int
dwarf_next_lines(Dwarf * dbg,Dwarf_Off off,Dwarf_Off * next_off,Dwarf_CU ** cu,Dwarf_Files ** srcfiles,size_t * nfiles,Dwarf_Lines ** srclines,size_t * nlines)37 dwarf_next_lines (Dwarf *dbg, Dwarf_Off off,
38 		  Dwarf_Off *next_off, Dwarf_CU **cu,
39 		  Dwarf_Files **srcfiles, size_t *nfiles,
40 		  Dwarf_Lines **srclines, size_t *nlines)
41 {
42   /* Ignore existing errors.  */
43   if (dbg == NULL)
44     return -1;
45 
46   Elf_Data *lines = dbg->sectiondata[IDX_debug_line];
47   if (lines == NULL)
48     {
49       __libdw_seterrno (DWARF_E_NO_DEBUG_LINE);
50       return -1;
51     }
52 
53   if (off == (Dwarf_Off) -1
54       || lines->d_size < 4
55       || off >= lines->d_size)
56     {
57       *next_off = (Dwarf_Off) -1;
58       return 1;
59     }
60 
61   /* Read enough of the header to know where the next table is and
62      whether we need to lookup the CU (version < 5).  */
63   const unsigned char *linep = lines->d_buf + off;
64   const unsigned char *lineendp = lines->d_buf + lines->d_size;
65 
66   if ((size_t) (lineendp - linep) < 4)
67     {
68     invalid_data:
69       __libdw_seterrno (DWARF_E_INVALID_DEBUG_LINE);
70       return -1;
71     }
72 
73   *next_off = off + 4;
74   Dwarf_Word unit_length = read_4ubyte_unaligned_inc (dbg, linep);
75   if (unit_length == DWARF3_LENGTH_64_BIT)
76     {
77       if ((size_t) (lineendp - linep) < 8)
78 	goto invalid_data;
79       unit_length = read_8ubyte_unaligned_inc (dbg, linep);
80       *next_off += 8;
81     }
82 
83   if (unit_length > (size_t) (lineendp - linep))
84     goto invalid_data;
85 
86   *next_off += unit_length;
87   lineendp = linep + unit_length;
88 
89   if ((size_t) (lineendp - linep) < 2)
90     goto invalid_data;
91   uint_fast16_t version = read_2ubyte_unaligned_inc (dbg, linep);
92 
93   Dwarf_Die cudie;
94   if (version < 5)
95     {
96       /* We need to find the matching CU to get the comp_dir.  Use the
97 	 given CU as hint where to start searching.  Normally it will
98 	 be the next CU that has a statement list. */
99       Dwarf_CU *given_cu = *cu;
100       Dwarf_CU *next_cu = given_cu;
101       bool found = false;
102       while (dwarf_get_units (dbg, next_cu, &next_cu, NULL, NULL,
103 			      &cudie, NULL) == 0)
104 	{
105 	  if (dwarf_hasattr (&cudie, DW_AT_stmt_list))
106 	    {
107 	      Dwarf_Attribute attr;
108 	      Dwarf_Word stmt_off;
109 	      if (dwarf_formudata (dwarf_attr (&cudie, DW_AT_stmt_list, &attr),
110 				   &stmt_off) == 0
111 		  && stmt_off == off)
112 		{
113 		  found = true;
114 		  break;
115 		}
116 	    }
117 	  else if (off == 0
118 		   && (next_cu->unit_type == DW_UT_split_compile
119 		       || next_cu->unit_type == DW_UT_split_type))
120 	    {
121 	      /* For split units (in .dwo files) there is only one table
122 		 at offset zero (containing just the files, no lines).  */
123 	      found = true;
124 	      break;
125 	    }
126 	}
127 
128       if (!found && given_cu != NULL)
129 	{
130 	  /* The CUs might be in a different order from the line
131 	     tables. Need to do a linear search (but stop at the given
132 	     CU, since we already searched those.  */
133 	  next_cu = NULL;
134 	  while (dwarf_get_units (dbg, next_cu, &next_cu, NULL, NULL,
135 				  &cudie, NULL) == 0
136 		 && next_cu != given_cu)
137 	    {
138 	      Dwarf_Attribute attr;
139 	      Dwarf_Word stmt_off;
140 	      if (dwarf_formudata (dwarf_attr (&cudie, DW_AT_stmt_list, &attr),
141 				   &stmt_off) == 0
142 		  && stmt_off == off)
143 		{
144 		  found = true;
145 		  break;
146 		}
147 	    }
148 	}
149 
150       if (found)
151 	*cu = next_cu;
152       else
153 	*cu = NULL;
154     }
155   else
156     *cu = NULL;
157 
158   const char *comp_dir;
159   unsigned address_size;
160   if (*cu != NULL)
161     {
162       comp_dir = __libdw_getcompdir (&cudie);
163       address_size = (*cu)->address_size;
164     }
165   else
166     {
167       comp_dir = NULL;
168 
169       size_t esize;
170       char *ident = elf_getident (dbg->elf, &esize);
171       if (ident == NULL || esize < EI_NIDENT)
172 	goto invalid_data;
173       address_size = ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
174     }
175 
176   if (__libdw_getsrclines (dbg, off, comp_dir, address_size,
177 			   srclines, srcfiles) != 0)
178     return -1;
179 
180   if (nlines != NULL)
181     {
182       if (srclines != NULL && *srclines != NULL)
183 	*nlines = (*srclines)->nlines;
184       else
185 	*nlines = 0;
186     }
187 
188   if (nfiles != NULL)
189     {
190       if (srcfiles != NULL && *srcfiles != NULL)
191 	*nfiles = (*srcfiles)->nfiles;
192       else
193 	*nfiles = 0;
194     }
195 
196   return 0;
197 }
198