1
2 /*--------------------------------------------------------------------*/
3 /*--- Read stabs debug info. readstabs.c ---*/
4 /*--------------------------------------------------------------------*/
5
6 /*
7 This file is part of Valgrind, a dynamic binary instrumentation
8 framework.
9
10 Copyright (C) 2000-2013 Julian Seward
11 jseward@acm.org
12
13 This program is free software; you can redistribute it and/or
14 modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation; either version 2 of the
16 License, or (at your option) any later version.
17
18 This program is distributed in the hope that it will be useful, but
19 WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
26 02111-1307, USA.
27
28 The GNU General Public License is contained in the file COPYING.
29 */
30
31 /*
32 Stabs reader greatly improved by Nick Nethercote, Apr 02.
33 This module was also extensively hacked on by Jeremy Fitzhardinge
34 and Tom Hughes.
35 */
36
37 /* "on Linux (except android), or on Darwin" */
38 #if (defined(VGO_linux) && \
39 !(defined(VGPV_arm_linux_android) || defined(VGPV_x86_linux_android) \
40 || defined(VGPV_mips32_linux_android)) \
41 || defined(VGO_darwin))
42
43 #include "pub_core_basics.h"
44 #include "pub_core_debuginfo.h"
45 #include "pub_core_libcbase.h"
46 #include "pub_core_libcassert.h"
47 #include "pub_core_libcprint.h"
48 #include "pub_core_xarray.h"
49 #include "priv_misc.h" /* dinfo_zalloc/free/strdup */
50 #include "priv_image.h"
51 #include "priv_tytypes.h"
52 #include "priv_d3basics.h"
53 #include "priv_storage.h"
54 #include "priv_readstabs.h" /* self */
55
56 /* --- !!! --- EXTERNAL HEADERS start --- !!! --- */
57 #if defined(VGO_linux)
58 # include <a.out.h> /* stabs defns */
59 #elif defined(VGO_darwin)
60 # include <mach-o/nlist.h>
61 # define n_other n_sect
62 # if VG_WORDSIZE == 8
63 # define nlist nlist_64
64 # endif
65 #else
66 # error "Unknown OS"
67 #endif
68 /* --- !!! --- EXTERNAL HEADERS end --- !!! --- */
69
70 /*------------------------------------------------------------*/
71 /*--- Read STABS format debug info. ---*/
72 /*------------------------------------------------------------*/
73
74 /* Stabs entry types, from:
75 * The "stabs" debug format
76 * Menapace, Kingdon and MacKenzie
77 * Cygnus Support
78 */
79 typedef enum { N_UNDEF = 0, /* undefined symbol, new stringtab */
80 N_GSYM = 32, /* Global symbol */
81 N_FUN = 36, /* Function start or end */
82 N_STSYM = 38, /* Data segment file-scope variable */
83 N_LCSYM = 40, /* BSS segment file-scope variable */
84 N_RSYM = 64, /* Register variable */
85 N_SLINE = 68, /* Source line number */
86 N_SO = 100, /* Source file path and name */
87 N_LSYM = 128, /* Stack variable or type */
88 N_BINCL = 130, /* Beginning of an include file */
89 N_SOL = 132, /* Include file name */
90 N_PSYM = 160, /* Function parameter */
91 N_EINCL = 162, /* End of an include file */
92 N_LBRAC = 192, /* Start of lexical block */
93 N_EXCL = 194, /* Placeholder for an include file */
94 N_RBRAC = 224 /* End of lexical block */
95 } stab_types;
96
97
98 /* Read stabs-format debug info. This is all rather horrible because
99 stabs is a underspecified, kludgy hack.
100 */
ML_(read_debuginfo_stabs)101 void ML_(read_debuginfo_stabs) ( DebugInfo* di,
102 UChar* stabC, Int stab_sz,
103 HChar* stabstr, Int stabstr_sz )
104 {
105 Int i;
106 Int n_stab_entries;
107 struct nlist* stab = (struct nlist*)stabC;
108 HChar *next_stabstr = NULL;
109 /* state for various things */
110 struct {
111 Addr start; /* start address */
112 Addr end; /* end address */
113 Int line; /* first line */
114 } func = { 0, 0, -1 };
115 struct {
116 HChar *name;
117 Bool same;
118 } file = { NULL, True };
119 struct {
120 Int prev; /* prev line */
121 Int no; /* current line */
122 Int ovf; /* line wrap */
123 Addr addr; /* start of this line */
124 Bool first; /* first line in function */
125 } line = { 0, 0, 0, 0, False };
126
127 /* Ok. It all looks plausible. Go on and read debug data.
128 stab kinds: 100 N_SO a source file name
129 68 N_SLINE a source line number
130 36 N_FUN start of a function
131
132 In this loop, we maintain a current file name, updated as
133 N_SO/N_SOLs appear, and a current function base address,
134 updated as N_FUNs appear. Based on that, address ranges for
135 N_SLINEs are calculated, and stuffed into the line info table.
136
137 Finding the instruction address range covered by an N_SLINE is
138 complicated; see the N_SLINE case below.
139 */
140 file.name = ML_(addStr)(di,"???", -1);
141
142 n_stab_entries = stab_sz/(int)sizeof(struct nlist);
143
144 TRACE_SYMTAB("\n--- Reading STABS (%d entries) ---\n", n_stab_entries);
145
146 for (i = 0; i < n_stab_entries; i++) {
147 const struct nlist *st = &stab[i];
148 HChar *string;
149
150 TRACE_SYMTAB("%2d type=%d othr=%d desc=%d "
151 "value=0x%x strx=%d %s\n", i,
152 st->n_type, st->n_other, st->n_desc,
153 (Int)st->n_value,
154 (Int)st->n_un.n_strx,
155 stabstr + st->n_un.n_strx );
156
157 /* handle continued string stabs */
158 {
159 Int qbuflen = 0;
160 Int qidx = 0;
161 HChar* qbuf = NULL;
162 Int qlen;
163 Bool qcontinuing = False;
164 UInt qstringidx;
165
166 qstringidx = st->n_un.n_strx;
167 string = stabstr + qstringidx;
168 qlen = VG_(strlen)(string);
169
170 while (string
171 && qlen > 0
172 && (qcontinuing || string[qlen-1] == '\\')) {
173 /* Gak, we have a continuation. Skip forward through
174 subsequent stabs to gather all the parts of the
175 continuation. Increment i, but keep st pointing at
176 current stab. */
177
178 qcontinuing = string[qlen-1] == '\\';
179
180 /* remove trailing \ */
181 while (string[qlen-1] == '\\' && qlen > 0)
182 qlen--;
183
184 TRACE_SYMTAB("cont: found extension string: \"%s\" "
185 "len=%d(%c) idx=%d buflen=%d\n",
186 string, qlen, string[qlen-1], qidx, qbuflen);
187
188 /* XXX this is silly. The si->strtab should have a way of
189 appending to the last added string... */
190 if ((qidx + qlen) >= qbuflen) {
191 HChar *n;
192
193 if (qbuflen == 0)
194 qbuflen = 16;
195 while ((qidx + qlen) >= qbuflen)
196 qbuflen *= 2;
197 n = ML_(dinfo_zalloc)("di.readstabs.rds.1", qbuflen);
198 VG_(memcpy)(n, qbuf, qidx);
199
200 if (qbuf != NULL)
201 ML_(dinfo_free)(qbuf);
202 qbuf = n;
203 }
204
205 VG_(memcpy)(&qbuf[qidx], string, qlen);
206 qidx += qlen;
207 if (di->trace_symtab) {
208 qbuf[qidx] = '\0';
209 TRACE_SYMTAB("cont: working buf=\"%s\"\n", qbuf);
210 }
211
212 i++;
213 if (i >= n_stab_entries)
214 break;
215
216 if (stab[i].n_un.n_strx) {
217 string = stabstr + stab[i].n_un.n_strx;
218 qlen = VG_(strlen)(string);
219 } else {
220 string = NULL;
221 qlen = 0;
222 }
223 }
224
225 if (qbuf != NULL) {
226 i--; /* overstepped */
227 string = ML_(addStr)(di, qbuf, qidx);
228 ML_(dinfo_free)(qbuf);
229 TRACE_SYMTAB("cont: made composite: \"%s\"\n", string);
230 }
231 }
232
233 switch(st->n_type) {
234 case N_UNDEF:
235 /* new string table base */
236 if (next_stabstr != NULL) {
237 stabstr_sz -= next_stabstr - stabstr;
238 stabstr = next_stabstr;
239 if (stabstr_sz <= 0) {
240 VG_(printf)(" @@ bad stabstr size %d\n", stabstr_sz);
241 return;
242 }
243 }
244 next_stabstr = stabstr + st->n_value;
245 break;
246
247 case N_BINCL: {
248 break;
249 }
250
251 case N_EINCL:
252 break;
253
254 case N_EXCL:
255 break;
256
257 case N_SOL: /* sub-source (include) file */
258 if (line.ovf != 0)
259 VG_(message)(Vg_UserMsg,
260 "Warning: file %s is very big (> 65535 lines) "
261 "Line numbers and annotation for this file might "
262 "be wrong. Sorry.\n",
263 file.name);
264 /* FALLTHROUGH */
265
266 case N_SO: { /* new source file */
267 HChar *nm = string;
268 UInt len = VG_(strlen)(nm);
269 Addr addr = func.start + st->n_value;
270
271 if (line.addr != 0) {
272 /* finish off previous line */
273 ML_(addLineInfo)(di, file.name, NULL, line.addr,
274 addr, line.no + line.ovf * LINENO_OVERFLOW, i);
275 }
276
277 /* reset line state */
278 line.ovf = 0;
279 line.addr = 0;
280 line.prev = 0;
281 line.no = 0;
282
283 if (len > 0 && nm[len-1] != '/') {
284 file.name = ML_(addStr)(di, nm, -1);
285 TRACE_SYMTAB("new source: %s\n", file.name);
286 } else if (len == 0)
287 file.name = ML_(addStr)(di, "?1\0", -1);
288
289 break;
290 }
291
292 case N_SLINE: { /* line info */
293 Addr addr = func.start + st->n_value;
294
295 if (line.addr != 0) {
296 /* there was a previous */
297 ML_(addLineInfo)(di, file.name, NULL, line.addr,
298 addr, line.no + line.ovf * LINENO_OVERFLOW, i);
299 }
300
301 line.addr = addr;
302 line.prev = line.no;
303 line.no = (Int)((UShort)st->n_desc);
304
305 if (line.prev > line.no + OVERFLOW_DIFFERENCE && file.same) {
306 VG_(message)(Vg_DebugMsg,
307 "Line number overflow detected (%d --> %d) in %s\n",
308 line.prev, line.no, file.name);
309 line.ovf++;
310 }
311 file.same = True;
312
313 /* This is pretty horrible. If this is the first line of
314 the function, then bind any unbound symbols to the arg
315 scope, since they're probably arguments. */
316 if (line.first) {
317 line.first = False;
318
319 /* remember first line of function */
320 if (func.start != 0) {
321 func.line = line.no;
322 }
323 }
324 break;
325 }
326
327 case N_FUN: { /* function start/end */
328 Addr addr = 0; /* end address for prev line/scope */
329
330 /* if this the end of the function or we haven't
331 previously finished the previous function... */
332 if (*string == '\0' || func.start != 0) {
333 /* end of function */
334 line.first = False;
335
336 /* end line at end of function */
337 addr = func.start + st->n_value;
338
339 /* now between functions */
340 func.start = 0;
341
342 // XXXX DEAD POINT XXXX
343 }
344
345 if (*string != '\0') {
346 /* new function */
347 line.first = True;
348
349 /* line ends at start of next function */
350 addr = di->text_debug_bias + st->n_value;
351
352 func.start = addr;
353 }
354
355 if (line.addr) {
356 ML_(addLineInfo)(di, file.name, NULL, line.addr,
357 addr, line.no + line.ovf * LINENO_OVERFLOW, i);
358 line.addr = 0;
359 }
360
361 //DEAD POINT
362 //DEAD POINT
363 break;
364 }
365
366 case N_LBRAC: {
367 /* open new scope */
368 // DEAD POINT
369 break;
370 }
371
372 case N_RBRAC: {
373 /* close scope */
374 // DEAD POINT
375 break;
376 }
377
378 case N_GSYM: /* global variable */
379 case N_STSYM: /* static in data segment */
380 case N_LCSYM: /* static in bss segment */
381 case N_PSYM: /* function parameter */
382 case N_LSYM: /* stack variable */
383 case N_RSYM: /* register variable */
384 break;
385 }
386 }
387 }
388
389 #endif /* (defined(VGO_linux) && !defined(VGPV_*_linux_android)) \
390 || defined(VGO_darwin) */
391
392 /*--------------------------------------------------------------------*/
393 /*--- end ---*/
394 /*--------------------------------------------------------------------*/
395