mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-04 02:18:37 +00:00
394 lines
13 KiB
C
394 lines
13 KiB
C
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- 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(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 };
|
|
|
|
/* 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, 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, 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, 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 ---*/
|
|
/*--------------------------------------------------------------------*/
|