readstabs.c revision 436e89c602e787e7a27dd6624b09beed41a0da8a
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 */
79typedef 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*/
101void 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