blob: c8cf9b39a48d1d63adff394c29d9aa54d46d34b6 [file] [log] [blame]
/*
* Stabs debugging format
*
* Copyright (C) 2003-2007 Michael Urman
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <util.h>
#include <libyasm.h>
typedef enum {
N_UNDF = 0x00, /* Undefined */
N_GSYM = 0x20, /* Global symbol */
N_FNAME = 0x22, /* Function name (BSD Fortran) */
N_FUN = 0x24, /* Function name or Text segment variable */
N_STSYM = 0x26, /* Data segment file-scope variable */
N_LCSYM = 0x28, /* BSS segment file-scope variable */
N_MAIN = 0x2a, /* Name of main routine */
N_ROSYM = 0x2c, /* Variable in .rodata section */
N_PC = 0x30, /* Global symbol (Pascal) */
N_SYMS = 0x32, /* Number of symbols (Ultrix V4.0) */
N_NOMAP = 0x34, /* No DST map */
N_OBJ = 0x38, /* Object file (Solaris2) */
N_OPT = 0x3c, /* Debugger options (Solaris2) */
N_RSYM = 0x40, /* Register variable */
N_M2C = 0x42, /* Modula-2 compilation unit */
N_SLINE = 0x44, /* Line numbers in .text segment */
N_DSLINE = 0x46, /* Line numbers in .data segment */
N_BSLINE = 0x48, /* Line numbers in .bss segment */
N_BROWS = 0x48, /* Source code .cb file's path */
N_DEFD = 0x4a, /* GNU Modula-2 definition module dependency */
N_FLINE = 0x4c, /* Function start/body/end line numbers (Solaris2) */
N_EHDECL = 0x50, /* GNU C++ exception variable */
N_MOD2 = 0x50, /* Modula2 info for imc (Ultrix V4.0) */
N_CATCH = 0x54, /* GNU C++ catch clause */
N_SSYM = 0x60, /* Structure or union element */
N_ENDM = 0x62, /* Last stab for module (Solaris2) */
N_SO = 0x64, /* Path and name of source files */
N_LSYM = 0x80, /* Stack variable */
N_BINCL = 0x84, /* Beginning of include file */
N_SOL = 0x84, /* Name of include file */
N_PSYM = 0xa0, /* Parameter variable */
N_EINCL = 0xa2, /* End of include file */
N_ENTRY = 0xa4, /* Alternate entry point */
N_LBRAC = 0xc0, /* Beginning of lexical block */
N_EXCL = 0xc2, /* Placeholder for a deleted include file */
N_SCOPE = 0xc4, /* Modula 2 scope info (Sun) */
N_RBRAC = 0xe0, /* End of lexical block */
N_BCOMM = 0xe2, /* Begin named common block */
N_ECOMM = 0xe4, /* End named common block */
N_ECOML = 0xe8, /* Member of common block */
N_WITH = 0xea, /* Pascal with statement: type,,0,0,offset (Solaris2) */
N_NBTEXT = 0xf0, /* Gould non-base registers */
N_NBDATA = 0xf2, /* Gould non-base registers */
N_NBBSS = 0xf4, /* Gould non-base registers */
N_NBSTS = 0xf6, /* Gould non-base registers */
N_NBLCS = 0xf8 /* Gould non-base registers */
} stabs_stab_type;
typedef struct yasm_dbgfmt_stabs {
yasm_dbgfmt_base dbgfmt; /* base structure */
} yasm_dbgfmt_stabs;
typedef struct {
unsigned long lastline; /* track line and file of bytecodes */
unsigned long curline;
const char *lastfile;
const char *curfile;
unsigned int stablen; /* size of a stab for current machine */
unsigned long stabcount; /* count stored stabs; doesn't include first */
yasm_section *stab; /* sections to which stabs, stabstrs appended */
yasm_section *stabstr;
yasm_bytecode *basebc; /* base bytecode from which to track SLINEs */
yasm_object *object;
yasm_linemap *linemap;
yasm_errwarns *errwarns;
} stabs_info;
typedef struct {
/*@null@*/ yasm_bytecode *bcstr; /* bytecode in stabstr for string */
stabs_stab_type type; /* stab type: N_* */
unsigned char other; /* unused, but stored here anyway */
unsigned short desc; /* description element of a stab */
/*@null@*/ yasm_symrec *symvalue; /* value element needing relocation */
/*@null@*/yasm_bytecode *bcvalue; /* relocated stab's bytecode */
unsigned long value; /* fallthrough value if above NULL */
} stabs_stab;
/* Bytecode callback function prototypes */
static void stabs_bc_str_destroy(void *contents);
static void stabs_bc_str_print(const void *contents, FILE *f, int
indent_level);
static int stabs_bc_str_calc_len
(yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data);
static int stabs_bc_str_tobytes
(yasm_bytecode *bc, unsigned char **bufp, unsigned char *bufstart, void *d,
yasm_output_value_func output_value,
/*@null@*/ yasm_output_reloc_func output_reloc);
static void stabs_bc_stab_destroy(void *contents);
static void stabs_bc_stab_print(const void *contents, FILE *f, int
indent_level);
static int stabs_bc_stab_calc_len
(yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data);
static int stabs_bc_stab_tobytes
(yasm_bytecode *bc, unsigned char **bufp, unsigned char *bufstart, void *d,
yasm_output_value_func output_value,
/*@null@*/ yasm_output_reloc_func output_reloc);
/* Bytecode callback structures */
static const yasm_bytecode_callback stabs_bc_str_callback = {
stabs_bc_str_destroy,
stabs_bc_str_print,
yasm_bc_finalize_common,
NULL,
stabs_bc_str_calc_len,
yasm_bc_expand_common,
stabs_bc_str_tobytes,
0
};
static const yasm_bytecode_callback stabs_bc_stab_callback = {
stabs_bc_stab_destroy,
stabs_bc_stab_print,
yasm_bc_finalize_common,
NULL,
stabs_bc_stab_calc_len,
yasm_bc_expand_common,
stabs_bc_stab_tobytes,
0
};
yasm_dbgfmt_module yasm_stabs_LTX_dbgfmt;
static /*@null@*/ /*@only@*/ yasm_dbgfmt *
stabs_dbgfmt_create(yasm_object *object)
{
yasm_dbgfmt_stabs *dbgfmt_stabs = yasm_xmalloc(sizeof(yasm_dbgfmt_stabs));
dbgfmt_stabs->dbgfmt.module = &yasm_stabs_LTX_dbgfmt;
return (yasm_dbgfmt *)dbgfmt_stabs;
}
static void
stabs_dbgfmt_destroy(/*@only@*/ yasm_dbgfmt *dbgfmt)
{
yasm_xfree(dbgfmt);
}
/* Create and add a new strtab-style string bytecode to a section, updating
* offset on insertion; no optimization necessary */
/* Copies the string, so you must still free yours as normal */
static yasm_bytecode *
stabs_dbgfmt_append_bcstr(yasm_section *sect, const char *str)
{
yasm_bytecode *bc;
bc = yasm_bc_create_common(&stabs_bc_str_callback, yasm__xstrdup(str), 0);
bc->len = (unsigned long)(strlen(str)+1);
bc->offset = yasm_bc_next_offset(yasm_section_bcs_last(sect));
yasm_section_bcs_append(sect, bc);
return bc;
}
/* Create and add a new stab bytecode to a section, updating offset on
* insertion; no optimization necessary. */
/* Requires a string bytecode, or NULL, for its string entry */
static stabs_stab *
stabs_dbgfmt_append_stab(stabs_info *info, yasm_section *sect,
/*@null@*/ yasm_bytecode *bcstr, stabs_stab_type type,
unsigned long desc, /*@null@*/ yasm_symrec *symvalue,
/*@null@*/ yasm_bytecode *bcvalue, unsigned long value)
{
yasm_bytecode *bc;
stabs_stab *stab = yasm_xmalloc(sizeof(stabs_stab));
stab->other = 0;
stab->bcstr = bcstr;
stab->type = type;
stab->desc = (unsigned short)desc;
stab->symvalue = symvalue;
stab->bcvalue = bcvalue;
stab->value = value;
bc = yasm_bc_create_common(&stabs_bc_stab_callback, stab,
bcvalue ? bcvalue->line : 0);
bc->len = info->stablen;
bc->offset = yasm_bc_next_offset(yasm_section_bcs_last(sect));
yasm_section_bcs_append(sect, bc);
info->stabcount++;
return stab;
}
static void
stabs_dbgfmt_generate_n_fun(stabs_info *info, yasm_bytecode *bc)
{
/* check all syms at this bc for potential function syms */
int bcsym;
for (bcsym=0; bc->symrecs && bc->symrecs[bcsym]; bcsym++)
{
char *str;
yasm_symrec *sym = bc->symrecs[bcsym];
const char *name = yasm_symrec_get_name(sym);
/* best guess algorithm - ignore labels containing a . or $ */
if (strchr(name, '.') || strchr(name, '$'))
continue;
/* if a function, update basebc, and output a funcname:F1 stab */
info->basebc = bc;
str = yasm_xmalloc(strlen(name)+4);
strcpy(str, name);
strcat(str, ":F1");
stabs_dbgfmt_append_stab(info, info->stab,
stabs_dbgfmt_append_bcstr(info->stabstr, str),
N_FUN, 0, sym, info->basebc, 0);
yasm_xfree(str);
break;
}
}
static int
stabs_dbgfmt_generate_bcs(yasm_bytecode *bc, void *d)
{
stabs_info *info = (stabs_info *)d;
yasm_linemap_lookup(info->linemap, bc->line, &info->curfile,
&info->curline);
/* check for new function */
stabs_dbgfmt_generate_n_fun(info, bc);
if (info->lastfile != info->curfile) {
info->lastline = 0; /* new file, so line changes */
/*stabs_dbgfmt_append_stab(info, info->stab,
stabs_dbgfmt_append_bcstr(info->stabstr, info->curfile),
N_SOL, 0, NULL, bc, 0);*/
}
/* output new line stabs if there's a basebc (known function) */
if (info->basebc != NULL && info->curline != info->lastline) {
info->lastline = bc->line;
stabs_dbgfmt_append_stab(info, info->stab, NULL, N_SLINE,
info->curline, NULL, NULL,
bc->offset - info->basebc->offset);
}
info->lastline = info->curline;
info->lastfile = info->curfile;
return 0;
}
static int
stabs_dbgfmt_generate_sections(yasm_section *sect, /*@null@*/ void *d)
{
stabs_info *info = (stabs_info *)d;
const char *sectname=yasm_section_get_name(sect);
/* each section has a different base symbol */
info->basebc = NULL;
/* handle first (pseudo) bc separately */
stabs_dbgfmt_generate_n_fun(d, yasm_section_bcs_first(sect));
yasm_section_bcs_traverse(sect, info->errwarns, d,
stabs_dbgfmt_generate_bcs);
if (yasm__strcasecmp(sectname, ".text")==0) {
/* Close out last function by appending a null SO stab after last bc */
yasm_bytecode *bc = yasm_section_bcs_last(sect);
yasm_symrec *sym =
yasm_symtab_define_label(info->object->symtab, ".n_so", bc, 1,
bc->line);
stabs_dbgfmt_append_stab(info, info->stab, 0, N_SO, 0, sym, bc, 0);
}
return 1;
}
static void
stabs_dbgfmt_generate(yasm_object *object, yasm_linemap *linemap,
yasm_errwarns *errwarns)
{
stabs_info info;
int new;
yasm_bytecode *dbgbc;
stabs_stab *stab;
yasm_bytecode *filebc, *nullbc, *laststr, *firstbc;
yasm_symrec *firstsym;
yasm_section *stext;
/* Stablen is determined by arch/machine */
if (yasm__strcasecmp(yasm_arch_keyword(object->arch), "x86") == 0) {
info.stablen = 12;
}
else /* unknown machine; generate nothing */
return;
info.object = object;
info.linemap = linemap;
info.errwarns = errwarns;
info.lastline = 0;
info.stabcount = 0;
info.stab = yasm_object_get_general(object, ".stab", 4, 0, 0, &new, 0);
if (!new) {
yasm_bytecode *last = yasm_section_bcs_last(info.stab);
if (last == NULL) {
yasm_error_set(YASM_ERROR_GENERAL,
N_("stabs debugging conflicts with user-defined section .stab"));
yasm_errwarn_propagate(errwarns,
yasm_section_bcs_first(info.stab)->line);
} else {
yasm_warn_set(YASM_WARN_GENERAL,
N_("stabs debugging overrides empty section .stab"));
yasm_errwarn_propagate(errwarns, 0);
}
}
info.stabstr =
yasm_object_get_general(object, ".stabstr", 1, 0, 0, &new, 0);
if (!new) {
yasm_bytecode *last = yasm_section_bcs_last(info.stabstr);
if (last == NULL) {
yasm_error_set(YASM_ERROR_GENERAL,
N_("stabs debugging conflicts with user-defined section .stabstr"));
yasm_errwarn_propagate(errwarns,
yasm_section_bcs_first(info.stab)->line);
} else {
yasm_warn_set(YASM_WARN_GENERAL,
N_("stabs debugging overrides empty section .stabstr"));
yasm_errwarn_propagate(errwarns, 0);
}
}
/* initial pseudo-stab */
stab = yasm_xmalloc(sizeof(stabs_stab));
dbgbc = yasm_bc_create_common(&stabs_bc_stab_callback, stab, 0);
dbgbc->len = info.stablen;
dbgbc->offset = 0;
yasm_section_bcs_append(info.stab, dbgbc);
/* initial strtab bytecodes */
nullbc = stabs_dbgfmt_append_bcstr(info.stabstr, "");
filebc = stabs_dbgfmt_append_bcstr(info.stabstr, object->src_filename);
stext = yasm_object_find_general(object, ".text");
firstsym = yasm_symtab_use(object->symtab, ".text", 0);
firstbc = yasm_section_bcs_first(stext);
/* N_SO file stab */
stabs_dbgfmt_append_stab(&info, info.stab, filebc, N_SO, 0,
firstsym, firstbc, 0);
yasm_object_sections_traverse(object, (void *)&info,
stabs_dbgfmt_generate_sections);
/* fill initial pseudo-stab's fields */
laststr = yasm_section_bcs_last(info.stabstr);
if (laststr == NULL)
yasm_internal_error(".stabstr has no entries");
stab->bcvalue = NULL;
stab->symvalue = NULL;
stab->value = yasm_bc_next_offset(laststr);
stab->bcstr = filebc;
stab->type = N_UNDF;
stab->other = 0;
if (info.stabcount > 0xffff) {
yasm_warn_set(YASM_WARN_GENERAL, N_("over 65535 stabs"));
yasm_errwarn_propagate(errwarns, 0);
stab->desc = 0xffff;
} else
stab->desc = (unsigned short)info.stabcount;
}
static int
stabs_bc_stab_tobytes(yasm_bytecode *bc, unsigned char **bufp,
unsigned char *bufstart, void *d,
yasm_output_value_func output_value,
yasm_output_reloc_func output_reloc)
{
/* This entire function, essentially the core of rendering stabs to a file,
* needs to become endian aware. Size appears not to be an issue, as known
* 64-bit systems use truncated values in 32-bit fields. */
const stabs_stab *stab = (const stabs_stab *)bc->contents;
unsigned char *buf = *bufp;
YASM_WRITE_32_L(buf, stab->bcstr ? stab->bcstr->offset : 0);
YASM_WRITE_8(buf, stab->type);
YASM_WRITE_8(buf, stab->other);
YASM_WRITE_16_L(buf, stab->desc);
if (stab->symvalue != NULL) {
bc->offset += 8;
output_reloc(stab->symvalue, bc, buf, 4, 32, 0, d);
bc->offset -= 8;
buf += 4;
}
else if (stab->bcvalue != NULL) {
YASM_WRITE_32_L(buf, stab->bcvalue->offset);
}
else {
YASM_WRITE_32_L(buf, stab->value);
}
*bufp = buf;
return 0;
}
static int
stabs_bc_str_tobytes(yasm_bytecode *bc, unsigned char **bufp,
unsigned char *bufstart, void *d,
yasm_output_value_func output_value,
yasm_output_reloc_func output_reloc)
{
const char *str = (const char *)bc->contents;
unsigned char *buf = *bufp;
strcpy((char *)buf, str);
buf += strlen(str)+1;
*bufp = buf;
return 0;
}
static void
stabs_bc_stab_destroy(void *contents)
{
yasm_xfree(contents);
}
static void
stabs_bc_str_destroy(void *contents)
{
yasm_xfree(contents);
}
static void
stabs_bc_stab_print(const void *contents, FILE *f, int indent_level)
{
const stabs_stab *stab = (const stabs_stab *)contents;
const char *str = "";
fprintf(f, "%*s.stabs \"%s\", 0x%x, 0x%x, 0x%x, 0x%lx\n",
indent_level, "", str, stab->type, stab->other, stab->desc,
stab->bcvalue ? stab->bcvalue->offset : stab->value);
}
static void
stabs_bc_str_print(const void *contents, FILE *f, int indent_level)
{
fprintf(f, "%*s\"%s\"\n", indent_level, "", (const char *)contents);
}
static int
stabs_bc_stab_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
void *add_span_data)
{
yasm_internal_error(N_("tried to resolve a stabs stab bytecode"));
/*@notreached@*/
return 0;
}
static int
stabs_bc_str_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
void *add_span_data)
{
yasm_internal_error(N_("tried to resolve a stabs str bytecode"));
/*@notreached@*/
return 0;
}
/* Define dbgfmt structure -- see dbgfmt.h for details */
yasm_dbgfmt_module yasm_stabs_LTX_dbgfmt = {
"Stabs debugging format",
"stabs",
NULL, /* no directives */
stabs_dbgfmt_create,
stabs_dbgfmt_destroy,
stabs_dbgfmt_generate
};