blob: e9892dc91b0051488df072688b27fa41e85cb088 [file] [log] [blame]
/*--------------------------------------------------------------------*/
/*--- Read stabs debug info. readstabs.c ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Valgrind, a dynamic binary instrumentation
framework.
Copyright (C) 2000-2013 Julian Seward
jseward@acm.org
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
The GNU General Public License is contained in the file COPYING.
*/
/*
Stabs reader greatly improved by Nick Nethercote, Apr 02.
This module was also extensively hacked on by Jeremy Fitzhardinge
and Tom Hughes.
*/
/* "on Linux (except android), or on Darwin" */
#if (defined(VGO_linux) && \
!(defined(VGPV_arm_linux_android) || defined(VGPV_x86_linux_android) \
|| defined(VGPV_mips32_linux_android)) \
|| defined(VGO_darwin))
#include "pub_core_basics.h"
#include "pub_core_debuginfo.h"
#include "pub_core_libcbase.h"
#include "pub_core_libcassert.h"
#include "pub_core_libcprint.h"
#include "pub_core_xarray.h"
#include "priv_misc.h" /* dinfo_zalloc/free/strdup */
#include "priv_image.h"
#include "priv_tytypes.h"
#include "priv_d3basics.h"
#include "priv_storage.h"
#include "priv_readstabs.h" /* self */
/* --- !!! --- EXTERNAL HEADERS start --- !!! --- */
#if defined(VGO_linux)
# include <a.out.h> /* stabs defns */
#elif defined(VGO_darwin)
# include <mach-o/nlist.h>
# define n_other n_sect
# if VG_WORDSIZE == 8
# define nlist nlist_64
# endif
#else
# error "Unknown OS"
#endif
/* --- !!! --- EXTERNAL HEADERS end --- !!! --- */
/*------------------------------------------------------------*/
/*--- Read STABS format debug info. ---*/
/*------------------------------------------------------------*/
/* Stabs entry types, from:
* The "stabs" debug format
* Menapace, Kingdon and MacKenzie
* Cygnus Support
*/
typedef enum { N_UNDEF = 0, /* undefined symbol, new stringtab */
N_GSYM = 32, /* Global symbol */
N_FUN = 36, /* Function start or end */
N_STSYM = 38, /* Data segment file-scope variable */
N_LCSYM = 40, /* BSS segment file-scope variable */
N_RSYM = 64, /* Register variable */
N_SLINE = 68, /* Source line number */
N_SO = 100, /* Source file path and name */
N_LSYM = 128, /* Stack variable or type */
N_BINCL = 130, /* Beginning of an include file */
N_SOL = 132, /* Include file name */
N_PSYM = 160, /* Function parameter */
N_EINCL = 162, /* End of an include file */
N_LBRAC = 192, /* Start of lexical block */
N_EXCL = 194, /* Placeholder for an include file */
N_RBRAC = 224 /* End of lexical block */
} stab_types;
/* Read stabs-format debug info. This is all rather horrible because
stabs is a underspecified, kludgy hack.
*/
void ML_(read_debuginfo_stabs) ( DebugInfo* di,
UChar* stabC, Int stab_sz,
HChar* stabstr, Int stabstr_sz )
{
Int i;
Int n_stab_entries;
struct nlist* stab = (struct nlist*)stabC;
HChar *next_stabstr = NULL;
/* state for various things */
struct {
Addr start; /* start address */
Addr end; /* end address */
Int line; /* first line */
} func = { 0, 0, -1 };
struct {
HChar *name;
Bool same;
} file = { NULL, True };
struct {
Int prev; /* prev line */
Int no; /* current line */
Int ovf; /* line wrap */
Addr addr; /* start of this line */
Bool first; /* first line in function */
} line = { 0, 0, 0, 0, False };
vg_assert (0);
/* Stab reader broken since debuginfo server (revision 13440)
See #if 0 for call to ML_(read_debuginfo_stabs) in readelf.c.
If ever it is repaired, file.name above should be replaced by a fndn_ix
for performance reasons. */
/* Ok. It all looks plausible. Go on and read debug data.
stab kinds: 100 N_SO a source file name
68 N_SLINE a source line number
36 N_FUN start of a function
In this loop, we maintain a current file name, updated as
N_SO/N_SOLs appear, and a current function base address,
updated as N_FUNs appear. Based on that, address ranges for
N_SLINEs are calculated, and stuffed into the line info table.
Finding the instruction address range covered by an N_SLINE is
complicated; see the N_SLINE case below.
*/
file.name = ML_(addStr)(di,"???", -1);
n_stab_entries = stab_sz/(int)sizeof(struct nlist);
TRACE_SYMTAB("\n--- Reading STABS (%d entries) ---\n", n_stab_entries);
for (i = 0; i < n_stab_entries; i++) {
const struct nlist *st = &stab[i];
HChar *string;
TRACE_SYMTAB("%2d type=%d othr=%d desc=%d "
"value=0x%x strx=%d %s\n", i,
st->n_type, st->n_other, st->n_desc,
(Int)st->n_value,
(Int)st->n_un.n_strx,
stabstr + st->n_un.n_strx );
/* handle continued string stabs */
{
Int qbuflen = 0;
Int qidx = 0;
HChar* qbuf = NULL;
Int qlen;
Bool qcontinuing = False;
UInt qstringidx;
qstringidx = st->n_un.n_strx;
string = stabstr + qstringidx;
qlen = VG_(strlen)(string);
while (string
&& qlen > 0
&& (qcontinuing || string[qlen-1] == '\\')) {
/* Gak, we have a continuation. Skip forward through
subsequent stabs to gather all the parts of the
continuation. Increment i, but keep st pointing at
current stab. */
qcontinuing = string[qlen-1] == '\\';
/* remove trailing \ */
while (string[qlen-1] == '\\' && qlen > 0)
qlen--;
TRACE_SYMTAB("cont: found extension string: \"%s\" "
"len=%d(%c) idx=%d buflen=%d\n",
string, qlen, string[qlen-1], qidx, qbuflen);
/* XXX this is silly. The si->strtab should have a way of
appending to the last added string... */
if ((qidx + qlen) >= qbuflen) {
HChar *n;
if (qbuflen == 0)
qbuflen = 16;
while ((qidx + qlen) >= qbuflen)
qbuflen *= 2;
n = ML_(dinfo_zalloc)("di.readstabs.rds.1", qbuflen);
VG_(memcpy)(n, qbuf, qidx);
if (qbuf != NULL)
ML_(dinfo_free)(qbuf);
qbuf = n;
}
VG_(memcpy)(&qbuf[qidx], string, qlen);
qidx += qlen;
if (di->trace_symtab) {
qbuf[qidx] = '\0';
TRACE_SYMTAB("cont: working buf=\"%s\"\n", qbuf);
}
i++;
if (i >= n_stab_entries)
break;
if (stab[i].n_un.n_strx) {
string = stabstr + stab[i].n_un.n_strx;
qlen = VG_(strlen)(string);
} else {
string = NULL;
qlen = 0;
}
}
if (qbuf != NULL) {
i--; /* overstepped */
string = ML_(addStr)(di, qbuf, qidx);
ML_(dinfo_free)(qbuf);
TRACE_SYMTAB("cont: made composite: \"%s\"\n", string);
}
}
switch(st->n_type) {
case N_UNDEF:
/* new string table base */
if (next_stabstr != NULL) {
stabstr_sz -= next_stabstr - stabstr;
stabstr = next_stabstr;
if (stabstr_sz <= 0) {
VG_(printf)(" @@ bad stabstr size %d\n", stabstr_sz);
return;
}
}
next_stabstr = stabstr + st->n_value;
break;
case N_BINCL: {
break;
}
case N_EINCL:
break;
case N_EXCL:
break;
case N_SOL: /* sub-source (include) file */
if (line.ovf != 0)
VG_(message)(Vg_UserMsg,
"Warning: file %s is very big (> 65535 lines) "
"Line numbers and annotation for this file might "
"be wrong. Sorry.\n",
file.name);
/* FALLTHROUGH */
case N_SO: { /* new source file */
HChar *nm = string;
UInt len = VG_(strlen)(nm);
Addr addr = func.start + st->n_value;
if (line.addr != 0) {
/* finish off previous line */
ML_(addLineInfo)(di,
ML_(addFnDn) (di,
file.name,
NULL),
line.addr,
addr, line.no + line.ovf * LINENO_OVERFLOW, i);
}
/* reset line state */
line.ovf = 0;
line.addr = 0;
line.prev = 0;
line.no = 0;
if (len > 0 && nm[len-1] != '/') {
file.name = ML_(addStr)(di, nm, -1);
TRACE_SYMTAB("new source: %s\n", file.name);
} else if (len == 0)
file.name = ML_(addStr)(di, "?1\0", -1);
break;
}
case N_SLINE: { /* line info */
Addr addr = func.start + st->n_value;
if (line.addr != 0) {
/* there was a previous */
ML_(addLineInfo)(di,
ML_(addFnDn)(di,
file.name,
NULL),
line.addr,
addr, line.no + line.ovf * LINENO_OVERFLOW, i);
}
line.addr = addr;
line.prev = line.no;
line.no = (Int)((UShort)st->n_desc);
if (line.prev > line.no + OVERFLOW_DIFFERENCE && file.same) {
VG_(message)(Vg_DebugMsg,
"Line number overflow detected (%d --> %d) in %s\n",
line.prev, line.no, file.name);
line.ovf++;
}
file.same = True;
/* This is pretty horrible. If this is the first line of
the function, then bind any unbound symbols to the arg
scope, since they're probably arguments. */
if (line.first) {
line.first = False;
/* remember first line of function */
if (func.start != 0) {
func.line = line.no;
}
}
break;
}
case N_FUN: { /* function start/end */
Addr addr = 0; /* end address for prev line/scope */
/* if this the end of the function or we haven't
previously finished the previous function... */
if (*string == '\0' || func.start != 0) {
/* end of function */
line.first = False;
/* end line at end of function */
addr = func.start + st->n_value;
/* now between functions */
func.start = 0;
// XXXX DEAD POINT XXXX
}
if (*string != '\0') {
/* new function */
line.first = True;
/* line ends at start of next function */
addr = di->text_debug_bias + st->n_value;
func.start = addr;
}
if (line.addr) {
ML_(addLineInfo)(di,
ML_(addFnDn) (di,
file.name,
NULL),
line.addr,
addr, line.no + line.ovf * LINENO_OVERFLOW, i);
line.addr = 0;
}
//DEAD POINT
//DEAD POINT
break;
}
case N_LBRAC: {
/* open new scope */
// DEAD POINT
break;
}
case N_RBRAC: {
/* close scope */
// DEAD POINT
break;
}
case N_GSYM: /* global variable */
case N_STSYM: /* static in data segment */
case N_LCSYM: /* static in bss segment */
case N_PSYM: /* function parameter */
case N_LSYM: /* stack variable */
case N_RSYM: /* register variable */
break;
}
}
}
#endif /* (defined(VGO_linux) && !defined(VGPV_*_linux_android)) \
|| defined(VGO_darwin) */
/*--------------------------------------------------------------------*/
/*--- end ---*/
/*--------------------------------------------------------------------*/