From: Michael Urman <mu@tortall.net> Date: Fri, 15 Aug 2003 03:43:55 +0000 (-0000) Subject: STABS debugging information. This includes, naturally, several draft X-Git-Tag: v0.3.0~6^2~7 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=47576797ad4671019f3de1821e21c1117143b1b1;p=yasm STABS debugging information. This includes, naturally, several draft changes to the dbgfmt interface, and other assorted updates, including: * yasm.c now calls df->initialize() and df->generate() * a dbgfmt bytecode type with associated handling * yasm_output_reloc_func type for use particularly by dbgfmts * df: initialize updated; generate, bc_dbgfmt_data_{output|delete|print} added * null-dbgfmt structure brought in line with these additions * elf-objfmt made aware of stabs sections, and what to do with them The bad news: * just enough stabs output to support line number information in GDB * GDB identifies function labels off by 3 bytes in my test, but line numbers remain correct, somehow. Unknown whether stabs-dbgfmt or GDB at fault. svn path=/trunk/yasm/; revision=1037 --- diff --git a/frontends/yasm/yasm.c b/frontends/yasm/yasm.c index 0d6a2cc1..e162254a 100644 --- a/frontends/yasm/yasm.c +++ b/frontends/yasm/yasm.c @@ -456,6 +456,20 @@ main(int argc, char *argv[]) "yasm.out"); } + /* Initialize the debug format */ + if (cur_dbgfmt->initialize) { + if (cur_dbgfmt->initialize(in_filename, obj_filename, &yasm_std_linemgr, + cur_objfmt, cur_arch, machine_name)) + { + print_error( + _("%s: debug format `%s' does not work with object format `%s'"), + _("FATAL"), cur_dbgfmt->keyword, cur_objfmt->keyword); + if (in != stdin) + fclose(in); + return EXIT_FAILURE; + } + } + /* Initialize the object format */ if (cur_objfmt->initialize) { if (cur_objfmt->initialize(in_filename, obj_filename, cur_dbgfmt, @@ -549,6 +563,11 @@ main(int argc, char *argv[]) return EXIT_FAILURE; } + /* generate any debugging information */ + if (cur_dbgfmt->generate) { + cur_dbgfmt->generate(sections); + } + /* open the object file for output (if not already opened by dbg objfmt) */ if (!obj && strcmp(cur_objfmt->keyword, "dbg") != 0) { obj = open_obj("wb"); diff --git a/libyasm/bytecode.c b/libyasm/bytecode.c index 179a06d7..b03f4dfc 100644 --- a/libyasm/bytecode.c +++ b/libyasm/bytecode.c @@ -37,6 +37,7 @@ #include "bytecode.h" #include "objfmt.h" +#include "dbgfmt.h" #include "arch.h" @@ -98,6 +99,14 @@ typedef struct bytecode_objfmt_data { /*@only@*/ void *data; /* objfmt-specific data */ } bytecode_objfmt_data; +typedef struct bytecode_dbgfmt_data { + yasm_bytecode bc; /* base structure */ + + unsigned int type; /* dbgfmt-specific type */ + /*@dependent@*/ yasm_dbgfmt *df; /* dbgfmt that created the data */ + /*@only@*/ void *data; /* dbgfmt-specific data */ +} bytecode_dbgfmt_data; + /* Static structures for when NULL is passed to conversion functions. */ /* for Convert*ToBytes() */ unsigned char bytes_static[16]; @@ -300,6 +309,31 @@ yasm_bc_new_objfmt_data(unsigned int type, unsigned long len, yasm_objfmt *of, return (yasm_bytecode *)objfmt_data; } +yasm_bytecode * +yasm_bc_new_dbgfmt_data(unsigned int type, unsigned long len, yasm_dbgfmt *df, + void *data, unsigned long lindex) +{ + bytecode_dbgfmt_data *dbgfmt_data; + + dbgfmt_data = (bytecode_dbgfmt_data *) + yasm_bc_new_common(YASM_BC__DBGFMT_DATA, sizeof(bytecode_dbgfmt_data), + lindex); + + dbgfmt_data->type = type; + dbgfmt_data->df = df; + /*@-mustfree@*/ + dbgfmt_data->data = data; + /*@=mustfree@*/ + + /* Yes, this breaks the paradigm just a little. But this data is very + * unlike other bytecode data--it's internally generated after the + * other bytecodes have been resolved, and the length is ALWAYS known. + */ + dbgfmt_data->bc.len = len; + + return (yasm_bytecode *)dbgfmt_data; +} + void yasm_bc_delete(yasm_bytecode *bc) { @@ -307,6 +341,7 @@ yasm_bc_delete(yasm_bytecode *bc) bytecode_reserve *reserve; bytecode_incbin *incbin; bytecode_objfmt_data *objfmt_data; + bytecode_dbgfmt_data *dbgfmt_data; if (!bc) return; @@ -340,6 +375,15 @@ yasm_bc_delete(yasm_bytecode *bc) yasm_internal_error( N_("objfmt can't handle its own objfmt data bytecode")); break; + case YASM_BC__DBGFMT_DATA: + dbgfmt_data = (bytecode_dbgfmt_data *)bc; + if (dbgfmt_data->df->bc_dbgfmt_data_delete) + dbgfmt_data->df->bc_dbgfmt_data_delete(dbgfmt_data->type, + dbgfmt_data->data); + else + yasm_internal_error( + N_("dbgfmt can't handle its own dbgfmt data bytecode")); + break; default: if ((unsigned int)bc->type < (unsigned int)cur_arch->bc_type_max) cur_arch->bc_delete(bc); @@ -361,6 +405,7 @@ yasm_bc_print(FILE *f, int indent_level, const yasm_bytecode *bc) const bytecode_incbin *incbin; const bytecode_align *align; const bytecode_objfmt_data *objfmt_data; + const bytecode_dbgfmt_data *dbgfmt_data; switch (bc->type) { case YASM_BC__EMPTY: @@ -414,6 +459,16 @@ yasm_bc_print(FILE *f, int indent_level, const yasm_bytecode *bc) else fprintf(f, "%*sUNKNOWN\n", indent_level, ""); break; + case YASM_BC__DBGFMT_DATA: + dbgfmt_data = (const bytecode_dbgfmt_data *)bc; + fprintf(f, "%*s_DbgFmt_Data_\n", indent_level, ""); + if (dbgfmt_data->df->bc_dbgfmt_data_print) + dbgfmt_data->df->bc_dbgfmt_data_print(f, indent_level, + dbgfmt_data->type, + dbgfmt_data->data); + else + fprintf(f, "%*sUNKNOWN\n", indent_level, ""); + break; default: if ((unsigned int)bc->type < (unsigned int)cur_arch->bc_type_max) cur_arch->bc_print(f, indent_level, bc); @@ -768,6 +823,7 @@ yasm_bc_tobytes(yasm_bytecode *bc, unsigned char *buf, unsigned long *bufsize, /*@out@*/ unsigned long *multiple, /*@out@*/ int *gap, const yasm_section *sect, void *d, yasm_output_expr_func output_expr, + /*@null@*/ yasm_output_reloc_func output_reloc, /*@null@*/ yasm_output_bc_objfmt_data_func output_bc_objfmt_data) /*@sets *buf@*/ @@ -776,6 +832,7 @@ yasm_bc_tobytes(yasm_bytecode *bc, unsigned char *buf, unsigned long *bufsize, unsigned char *origbuf, *destbuf; /*@dependent@*/ /*@null@*/ const yasm_intnum *num; bytecode_objfmt_data *objfmt_data; + bytecode_dbgfmt_data *dbgfmt_data; unsigned long datasize; int error = 0; @@ -836,6 +893,16 @@ yasm_bc_tobytes(yasm_bytecode *bc, unsigned char *buf, unsigned long *bufsize, yasm_internal_error( N_("Have objfmt data bytecode but no way to output it")); break; + case YASM_BC__DBGFMT_DATA: + dbgfmt_data = (bytecode_dbgfmt_data *)bc; + if (dbgfmt_data->df->bc_dbgfmt_data_output) + error = dbgfmt_data->df->bc_dbgfmt_data_output(bc, + dbgfmt_data->type, dbgfmt_data->data, &destbuf, + sect, output_reloc, d); + else + yasm_internal_error( + N_("Have dbgfmt data bytecode but no way to output it")); + break; default: if ((unsigned int)bc->type < (unsigned int)cur_arch->bc_type_max) error = cur_arch->bc_tobytes(bc, &destbuf, sect, d, diff --git a/libyasm/bytecode.h b/libyasm/bytecode.h index 276da4c7..00989b4f 100644 --- a/libyasm/bytecode.h +++ b/libyasm/bytecode.h @@ -57,6 +57,7 @@ typedef enum { YASM_BC__RESERVE, /**< Reserved space. */ YASM_BC__INCBIN, /**< Included binary file. */ YASM_BC__ALIGN, /**< Alignment to a boundary. */ + YASM_BC__DBGFMT_DATA, /**< yasm_dbgfmt specific data. */ YASM_BC__OBJFMT_DATA /**< yasm_objfmt specific data. */ } yasm_bytecode_type; @@ -192,6 +193,18 @@ void yasm_bc_set_multiple(yasm_bytecode *bc, /*@keep@*/ yasm_expr *e); (unsigned int type, unsigned long len, yasm_objfmt *of, /*@only@*/ void *data, unsigned long lindex); +/** Create a bytecode that includes yasm_dbgfmt-specific data. + * \param type yasm_dbgfmt-specific type + * \param len length (in bytes) of data + * \param df yasm_dbgfmt storing the data + * \param data data (kept, do not free) + * \param lindex line index (as from yasm_linemgr) for the bytecode + * \return Newly allocated bytecode. + */ +/*@only@*/ yasm_bytecode *yasm_bc_new_dbgfmt_data + (unsigned int type, unsigned long len, yasm_dbgfmt *df, + /*@only@*/ void *data, unsigned long lindex); + /** Delete (free allocated memory for) a bytecode. * \param bc bytecode (only pointer to it); may be NULL */ @@ -258,6 +271,8 @@ yasm_bc_resolve_flags yasm_bc_resolve(yasm_bytecode *bc, int save, * representation * \param output_bc_objfmt_data function to call to convert yasm_objfmt data * bytecodes into their byte representation + * \param objfmt_output_reloc function to call to output relocation entries + * for a single sym * \return Newly allocated buffer that should be used instead of buf for * reading the byte representation, or NULL if buf was big enough to * hold the entire byte representation. @@ -268,6 +283,7 @@ yasm_bc_resolve_flags yasm_bc_resolve(yasm_bytecode *bc, int save, (yasm_bytecode *bc, unsigned char *buf, unsigned long *bufsize, /*@out@*/ unsigned long *multiple, /*@out@*/ int *gap, const yasm_section *sect, void *d, yasm_output_expr_func output_expr, + /*@null@*/ yasm_output_reloc_func output_reloc, /*@null@*/ yasm_output_bc_objfmt_data_func output_bc_objfmt_data) /*@sets *buf@*/; diff --git a/libyasm/coretype.h b/libyasm/coretype.h index 46d8762d..01bf6ec0 100644 --- a/libyasm/coretype.h +++ b/libyasm/coretype.h @@ -177,6 +177,10 @@ typedef int (*yasm_output_expr_func) /*@observer@*/ const yasm_section *sect, yasm_bytecode *bc, int rel, int warn, /*@null@*/ void *d) /*@uses *ep@*/; +typedef int (*yasm_output_reloc_func) + (yasm_symrec *sym, yasm_bytecode *bc, unsigned char *buf, size_t destsize, + size_t valsize, int rel, int warn, const yasm_section *sect, void *d); + /** Convert a yasm_objfmt-specific data bytecode into its byte representation. * Usually implemented by object formats to output their own generated data. * \param type yasm_objfmt-specific type diff --git a/libyasm/dbgfmt.h b/libyasm/dbgfmt.h index 57ce67a1..a6a8e077 100644 --- a/libyasm/dbgfmt.h +++ b/libyasm/dbgfmt.h @@ -43,7 +43,7 @@ * definitions match the module loader's function definitions. The version * number must never be decreased. */ -#define YASM_DBGFMT_VERSION 0 +#define YASM_DBGFMT_VERSION 1 /** YASM debug format interface. */ struct yasm_dbgfmt { @@ -66,9 +66,11 @@ struct yasm_dbgfmt { * \param in_filename primary input filename * \param obj_filename object filename * \param of object format in use + * \return Nonzero if object format does not provide needed support. */ - void (*initialize) (const char *in_filename, const char *obj_filename, - yasm_objfmt *of); + int (*initialize) (const char *in_filename, const char *obj_filename, + yasm_linemgr *lm, yasm_objfmt *of, yasm_arch *a, + const char *machine); /** Clean up anything allocated by initialize(). Function may be * unimplemented (NULL) if not needed by the debug format. @@ -76,10 +78,51 @@ struct yasm_dbgfmt { void (*cleanup) (void); /** DEBUG directive support. + * \param name directive name * \param valparams value/parameters * \param lindex line index (as from yasm_linemgr) + * \return Nonzero if directive was not recognized; 0 if directive was + * recognized even if it wasn't valid. */ - void (*directive) (yasm_valparamhead *valparams, unsigned long lindex); + int (*directive) (const char *name, yasm_valparamhead *valparams, + unsigned long lindex); + + /** Generate debugging information bytecodes + * \param sections list of sections + */ + void (*generate) (yasm_sectionhead *sections); + + /** Output debug format-specific bytecode data (YASM_BC_DBGFMT_DATA). + * Function may be unimplemented (NULL) if no YASM_BC_DBGFMT_DATA is ever + * allocated by the debug format. + * \param type debug format-specific bytecode type + * \param data debug format-specific data + * \param buf provided buffer, as long as registered length + */ + int (*bc_dbgfmt_data_output)(yasm_bytecode *bc, unsigned int type, + const void *data, unsigned char **buf, + const yasm_section *sect, + yasm_output_reloc_func output_reloc, + void *objfmt_d); + + /** Delete debug format-specific bytecode data (YASM_BC_DBGFMT_DATA). + * Funtion may be unimplemented (NULL) if no YASM_BC_DBGFMT_DATA is ever + * allocated by the debug format. + * \param type debug format-specific bytecode type + * \param data debug-format specific data + */ + void (*bc_dbgfmt_data_delete)(unsigned int type, /*@only@*/ void *data); + + /** Print debug format-specific bytecode data (YASM_BC_DBGFMT_DATA). For + * debugging purposes. Function may be unimplemented (NULL) if no + * YASM_BC_DBGFMT_DATA is ever allocated by the debug format. + * \param f file + * \param indent_level indentation level + * \param type debug format-specific bytecode type + * \param data debug format-specific data + */ + void (*bc_dbgfmt_data_print)(FILE *f, int indent_level, unsigned int type, + const void *data); }; #endif diff --git a/modules/dbgfmts/Makefile.inc b/modules/dbgfmts/Makefile.inc index 70e93732..e61366de 100644 --- a/modules/dbgfmts/Makefile.inc +++ b/modules/dbgfmts/Makefile.inc @@ -1,5 +1,7 @@ # $IdPath$ EXTRA_DIST += modules/dbgfmts/null/Makefile.inc +EXTRA_DIST += modules/dbgfmts/stabs/Makefile.inc include modules/dbgfmts/null/Makefile.inc +include modules/dbgfmts/stabs/Makefile.inc diff --git a/modules/dbgfmts/null/null-dbgfmt.c b/modules/dbgfmts/null/null-dbgfmt.c index 7bc698de..55d71c9f 100644 --- a/modules/dbgfmts/null/null-dbgfmt.c +++ b/modules/dbgfmts/null/null-dbgfmt.c @@ -38,5 +38,9 @@ yasm_dbgfmt yasm_null_LTX_dbgfmt = { "null", NULL, /*null_dbgfmt_initialize*/ NULL, /*null_dbgfmt_cleanup*/ - NULL /*null_dbgfmt_directive*/ + NULL, /*null_dbgfmt_directive*/ + NULL, /*null_dbgfmt_generate*/ + NULL, /*null_dbgfmt_bc_data_output*/ + NULL, /*null_dbgfmt_bc_data_delete*/ + NULL /*null_dbgfmt_bc_data_print*/ }; diff --git a/modules/dbgfmts/stabs/Makefile.inc b/modules/dbgfmts/stabs/Makefile.inc new file mode 100644 index 00000000..89547723 --- /dev/null +++ b/modules/dbgfmts/stabs/Makefile.inc @@ -0,0 +1,8 @@ +# $IdPath$ + +pkglib_LTLIBRARIES += dbgfmt_stabs.la + +dbgfmt_stabs_la_SOURCES = modules/dbgfmts/stabs/stabs-dbgfmt.c +dbgfmt_stabs_la_LDFLAGS = -module -avoid-version -no-undefined +dbgfmt_stabs_la_LIBADD = libyasm.la +YASM_MODULES += -dlopen dbgfmt_stabs.la diff --git a/modules/dbgfmts/stabs/stabs-dbgfmt.c b/modules/dbgfmts/stabs/stabs-dbgfmt.c new file mode 100644 index 00000000..efb56db9 --- /dev/null +++ b/modules/dbgfmts/stabs/stabs-dbgfmt.c @@ -0,0 +1,450 @@ +/* + * Stabs debugging format + * + * Copyright (C) 2003 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> +/*@unused@*/ RCSID("$IdPath$"); + +#define YASM_LIB_INTERNAL +#define YASM_BC_INTERNAL +#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; + +#define STABS_DEBUG_DATA 1 +#define STABS_DEBUG_STR 2 + +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_symrec *firstsym; /* track leading sym of section/function */ + yasm_bytecode *firstbc; /* and its bytecode */ +} 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; + +/* helper struct for finding first sym (and bytecode) of a section */ +typedef struct { + yasm_symrec *sym; + yasm_bytecode *precbc; + yasm_section *sect; +} stabs_symsect; + +yasm_dbgfmt yasm_stabs_LTX_dbgfmt; + +static yasm_objfmt *cur_objfmt = NULL; +static const char *filename = NULL; +static yasm_linemgr *linemgr = NULL; +static yasm_arch *cur_arch = NULL; +static const char *cur_machine = NULL; +static size_t stabs_relocsize_bits = 0; +static size_t stabs_relocsize_bytes = 0; + +static int +stabs_dbgfmt_initialize(const char *in_filename, const char *obj_filename, + yasm_linemgr *lm, yasm_objfmt *of, yasm_arch *a, + const char *machine) +{ + cur_objfmt = of; + filename = in_filename; + linemgr = lm; + cur_arch = a; + cur_machine = machine; + return 0; +} + +static void +stabs_dbgfmt_cleanup(void) +{ +} + +/* 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 = yasm_bc_new_dbgfmt_data( + STABS_DEBUG_STR, strlen(str)+1, + &yasm_stabs_LTX_dbgfmt, + (void *)yasm__xstrdup(str), 0); + yasm_bytecodehead *bcs = yasm_section_get_bytecodes(sect); + yasm_bytecode *precbc = yasm_bcs_last(bcs); + bc->offset = precbc ? precbc->offset + precbc->len : 0; + yasm_bcs_append(bcs, 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, *precbc; + yasm_bytecodehead *bcs; + 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_new_dbgfmt_data(STABS_DEBUG_DATA, info->stablen, + &yasm_stabs_LTX_dbgfmt, (void *)stab, + bcvalue ? bcvalue->line : 0); + bcs = yasm_section_get_bytecodes(sect); + precbc = yasm_bcs_last(bcs); + bc->offset = precbc ? precbc->offset + precbc->len : 0; + yasm_bcs_append(bcs, bc); + info->stabcount++; + return stab; +} + +/* Update current first sym and bytecode if it's in the right section */ +static int +stabs_dbgfmt_first_sym_traversal(yasm_symrec *sym, void *d) +{ + stabs_symsect *symsect = (stabs_symsect *)d; + yasm_section *sect; + yasm_bytecode *precbc; + + if (!yasm_symrec_get_label(sym, §, &precbc)) + return 1; + if (precbc == NULL) + precbc = yasm_bcs_first(yasm_section_get_bytecodes(sect)); + if ((sect == symsect->sect) + && ((symsect->sym == NULL) + || precbc->offset < symsect->precbc->offset)) + { + symsect->sym = sym; + symsect->precbc = precbc; + } + return 1; +} + +/* Find the first sym and its preceding bytecode in a given section */ +static void +stabs_dbgfmt_first_sym_by_sect(stabs_info *info, yasm_section *sect) +{ + stabs_symsect symsect = { NULL, NULL, NULL }; + if (sect == NULL) { + info->firstsym = NULL; + info->firstbc = NULL; + } + + symsect.sect = sect; + yasm_symrec_traverse((void *)&symsect, stabs_dbgfmt_first_sym_traversal); + info->firstsym = symsect.sym; + info->firstbc = symsect.precbc; +} + +static int +stabs_dbgfmt_generate_bcs(yasm_bytecode *bc, void *d) +{ + stabs_info *info = (stabs_info *)d; + linemgr->lookup(bc->line, &info->curfile, &info->curline); + + 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);*/ + } + if (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->firstbc->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); + + stabs_dbgfmt_first_sym_by_sect(info, sect); + if (yasm__strcasecmp(sectname, ".text")==0) { + char *str; + const char *symname=yasm_symrec_get_name(info->firstsym); + size_t len = strlen(symname)+4; + str = yasm_xmalloc(len); + snprintf(str, len, "%s:F1", symname); + stabs_dbgfmt_append_stab(info, info->stab, + stabs_dbgfmt_append_bcstr(info->stabstr, str), + N_FUN, 0, info->firstsym, info->firstbc, 0); + yasm_xfree(str); + } + yasm_bcs_traverse(yasm_section_get_bytecodes(sect), d, + stabs_dbgfmt_generate_bcs); + + return 1; +} + +static void +stabs_dbgfmt_generate(yasm_sectionhead *sections) +{ + stabs_info info; + int new; + yasm_bytecode *dbgbc; + yasm_bytecodehead *bcs; + stabs_stab *stab; + yasm_bytecode *filebc, *nullbc, *laststr; + yasm_section *stext; + + /* Stablen is determined by arch/machine */ + if (yasm__strcasecmp(cur_arch->keyword, "x86") == 0) { + if (yasm__strcasecmp(cur_machine, "x86") == 0) { + info.stablen = 12; + } + else if (yasm__strcasecmp(cur_machine, "amd64") == 0) { + info.stablen = 16; + } + else + return; + } + else /* unknown machine; generate nothing */ + return; + + stabs_relocsize_bytes = info.stablen - 8; + stabs_relocsize_bits = stabs_relocsize_bytes * 8; + + info.lastline = 0; + info.stabcount = 0; + info.stab = yasm_sections_switch_general(sections, ".stab", 0, 0, &new, 0); + if (!new) { + yasm_bytecode *last = yasm_bcs_last( + yasm_section_get_bytecodes(info.stab)); + if (last == NULL) + yasm__error( + yasm_bcs_first(yasm_section_get_bytecodes(info.stab))->line, + N_("stabs debugging conflicts with user-defined section .stab")); + else + yasm__warning(YASM_WARN_GENERAL, 0, + N_("stabs debugging overrides empty section .stab")); + } + + info.stabstr = yasm_sections_switch_general(sections, ".stabstr", 0, 0, + &new, 0); + if (!new) { + yasm_bytecode *last = yasm_bcs_last( + yasm_section_get_bytecodes(info.stabstr)); + if (last == NULL) + yasm__error( + yasm_bcs_first(yasm_section_get_bytecodes(info.stabstr))->line, + N_("stabs debugging conflicts with user-defined section .stabstr")); + else + yasm__warning(YASM_WARN_GENERAL, 0, + N_("stabs debugging overrides empty section .stabstr")); + } + + + + /* initial pseudo-stab */ + stab = yasm_xmalloc(sizeof(stabs_stab)); + dbgbc = yasm_bc_new_dbgfmt_data(STABS_DEBUG_DATA, info.stablen, + &yasm_stabs_LTX_dbgfmt, (void *)stab, 0); + bcs = yasm_section_get_bytecodes(info.stab); + yasm_bcs_append(bcs, dbgbc); + + /* initial strtab bytecodes */ + nullbc = stabs_dbgfmt_append_bcstr(info.stabstr, ""); + filebc = stabs_dbgfmt_append_bcstr(info.stabstr, filename); + + stext = yasm_sections_find_general(sections, ".text"); + info.firstsym = yasm_symrec_use(".text", 0); + info.firstbc = yasm_bcs_first(yasm_section_get_bytecodes(stext)); + /* N_SO file stab */ + stabs_dbgfmt_append_stab(&info, info.stab, filebc, N_SO, 0, + info.firstsym, info.firstbc, 0); + + yasm_sections_traverse(sections, (void *)&info, + stabs_dbgfmt_generate_sections); + + /* fill initial pseudo-stab's fields */ + laststr = yasm_bcs_last(yasm_section_get_bytecodes(info.stabstr)); + if (laststr == NULL) + yasm_internal_error(".stabstr has no entries"); + + stab->bcvalue = NULL; + stab->symvalue = NULL; + stab->value = laststr->offset + laststr->len; + stab->bcstr = filebc; + stab->type = N_UNDF; + stab->other = 0; + stab->desc = info.stabcount; +} + +static int +stabs_dbgfmt_bc_data_output(yasm_bytecode *bc, unsigned int type, + const void *data, unsigned char **buf, + const yasm_section *sect, + yasm_output_reloc_func output_reloc, void *objfmt_d) +{ + unsigned char *bufp = *buf; + if (type == STABS_DEBUG_DATA) { + const stabs_stab *stab = data; + + YASM_WRITE_32_L(bufp, stab->bcstr ? stab->bcstr->offset : 0); + YASM_WRITE_8(bufp, stab->type); + YASM_WRITE_8(bufp, stab->other); + YASM_WRITE_16_L(bufp, stab->desc); + + if (stab->symvalue != NULL) { + printf("DBG: "); + bc->offset += 8; + output_reloc(stab->symvalue, bc, bufp, stabs_relocsize_bytes, + stabs_relocsize_bits, 0, 0, sect, objfmt_d); + bc->offset -= 8; + bufp += stabs_relocsize_bytes; + } + else if (stab->bcvalue != NULL) { + YASM_WRITE_32_L(bufp, stab->bcvalue->offset); + } + else { + YASM_WRITE_32_L(bufp, stab->value); + } + } + else if (type == STABS_DEBUG_STR) { + const char *str = data; + strcpy((char *)bufp, str); + bufp += strlen(str)+1; + } + + *buf = bufp; + return 0; +} + +static void +stabs_dbgfmt_bc_data_delete(unsigned int type, void *data) +{ + /* both stabs and strs are allocated at the top level pointer */ + yasm_xfree(data); +} + +static void +stabs_dbgfmt_bc_data_print(FILE *f, int indent_level, unsigned int type, + const void *data) +{ + if (type == STABS_DEBUG_DATA) { + const stabs_stab *stab = data; + 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); + } + else if (type == STABS_DEBUG_STR) + fprintf(f, "%*s\"%s\"\n", indent_level, "", (const char *)data); +} + +/* Define dbgfmt structure -- see dbgfmt.h for details */ +yasm_dbgfmt yasm_stabs_LTX_dbgfmt = { + YASM_DBGFMT_VERSION, + "Stabs debugging format", + "stabs", + stabs_dbgfmt_initialize, + stabs_dbgfmt_cleanup, + NULL /*stabs_dbgfmt_directive*/, + stabs_dbgfmt_generate, + stabs_dbgfmt_bc_data_output, + stabs_dbgfmt_bc_data_delete, + stabs_dbgfmt_bc_data_print +}; diff --git a/modules/objfmts/bin/bin-objfmt.c b/modules/objfmts/bin/bin-objfmt.c index 2026a188..6cbcdf4c 100644 --- a/modules/objfmts/bin/bin-objfmt.c +++ b/modules/objfmts/bin/bin-objfmt.c @@ -189,7 +189,7 @@ bin_objfmt_output_bytecode(yasm_bytecode *bc, /*@null@*/ void *d) assert(info != NULL); bigbuf = yasm_bc_tobytes(bc, info->buf, &size, &multiple, &gap, info->sect, - info, bin_objfmt_output_expr, NULL); + info, bin_objfmt_output_expr, NULL, NULL); /* Don't bother doing anything else if size ended up being 0. */ if (size == 0) { diff --git a/modules/objfmts/coff/coff-objfmt.c b/modules/objfmts/coff/coff-objfmt.c index 05d35884..73dd43ba 100644 --- a/modules/objfmts/coff/coff-objfmt.c +++ b/modules/objfmts/coff/coff-objfmt.c @@ -404,7 +404,7 @@ coff_objfmt_output_bytecode(yasm_bytecode *bc, /*@null@*/ void *d) assert(info != NULL); bigbuf = yasm_bc_tobytes(bc, info->buf, &size, &multiple, &gap, info->sect, - info, coff_objfmt_output_expr, NULL); + info, coff_objfmt_output_expr, NULL, NULL); /* Don't bother doing anything else if size ended up being 0. */ if (size == 0) { diff --git a/modules/objfmts/elf/elf-objfmt.c b/modules/objfmts/elf/elf-objfmt.c index 605f8842..113d61ba 100644 --- a/modules/objfmts/elf/elf-objfmt.c +++ b/modules/objfmts/elf/elf-objfmt.c @@ -55,6 +55,7 @@ typedef struct { FILE *f; elf_secthead *shead; yasm_section *sect; + yasm_sectionhead *sections; unsigned long sindex; } elf_objfmt_output_info; @@ -65,6 +66,7 @@ static elf_strtab_head* elf_strtab; /* strtab entries */ yasm_objfmt yasm_elf_LTX_objfmt; static /*@dependent@*/ yasm_arch *cur_arch; +static /*@dependent@*/ yasm_dbgfmt *cur_dbgfmt; static elf_symtab_entry * @@ -130,6 +132,7 @@ elf_objfmt_initialize(const char *in_filename, elf_symtab_entry *entry; cur_arch = a; + cur_dbgfmt = df; if (!elf_set_arch(a, machine)) return 1; @@ -173,6 +176,32 @@ elf_objfmt_output_align(FILE *f, unsigned int align) return pos; } +static int +elf_objfmt_output_reloc(yasm_symrec *sym, yasm_bytecode *bc, + unsigned char *buf, size_t destsize, size_t valsize, + int rel, int warn, const yasm_section *sect, void *d) +{ + elf_reloc_entry *reloc; + elf_objfmt_output_info *info = d; + yasm_intnum *zero = yasm_intnum_new_uint(0); + int retval; + + reloc = elf_reloc_entry_new(sym, + yasm_intnum_new_uint(bc->offset), rel, valsize); + if (reloc == NULL) { + yasm__error(bc->line, N_("elf: invalid relocation size")); + return 1; + } + /* allocate .rel sections on a need-basis */ + if (elf_secthead_append_reloc(info->shead, reloc)) + elf_objfmt_parse_scnum++; + + retval = cur_arch->intnum_tobytes(zero, buf, destsize, valsize, 0, + bc, rel, warn, bc->line); + yasm_intnum_delete(zero); + return retval; +} + /* PASS1 */ static int elf_objfmt_output_expr(yasm_expr **ep, unsigned char *buf, size_t destsize, @@ -275,7 +304,8 @@ elf_objfmt_output_bytecode(yasm_bytecode *bc, /*@null@*/ void *d) yasm_internal_error("null info struct"); bigbuf = yasm_bc_tobytes(bc, buf, &size, &multiple, &gap, info->sect, - info, elf_objfmt_output_expr, NULL); + info, elf_objfmt_output_expr, + elf_objfmt_output_reloc, NULL); /* Don't bother doing anything else if size ended up being 0. */ if (size == 0) { @@ -320,6 +350,35 @@ elf_objfmt_output_bytecode(yasm_bytecode *bc, /*@null@*/ void *d) return 0; } +static elf_secthead * +elf_objfmt_new_dbg_secthead(yasm_section *sect, elf_objfmt_output_info *info) +{ + elf_secthead *shead; + elf_section_type type=SHT_PROGBITS; + yasm_intnum *align=NULL; + elf_size entsize=0; + const char *sectname = yasm_section_get_name(sect); + elf_strtab_entry *name = elf_strtab_append_str(elf_shstrtab, sectname); + + if (yasm__strcasecmp(sectname, ".stab")==0) { + align = yasm_intnum_new_uint(4); + entsize = 12; + } else if (yasm__strcasecmp(sectname, ".stabstr")==0) { + type = SHT_STRTAB; + align = yasm_intnum_new_uint(1); + } + else + yasm_internal_error(N_("Unrecognized section without data")); + + shead = elf_secthead_new(name, type, 0, elf_objfmt_parse_scnum++, 0, 0); + elf_secthead_set_align(shead, align); + elf_secthead_set_entsize(shead, entsize); + + yasm_section_set_of_data(sect, &yasm_elf_LTX_objfmt, shead); + + return shead; +} + /* PASS1 */ static int elf_objfmt_output_section(yasm_section *sect, /*@null@*/ void *d) @@ -339,7 +398,7 @@ elf_objfmt_output_section(yasm_section *sect, /*@null@*/ void *d) yasm_internal_error("null info struct"); shead = yasm_section_get_of_data(sect); if (shead == NULL) - yasm_internal_error("no section header attached to section"); + shead = elf_objfmt_new_dbg_secthead(sect, info); /* don't output header-only sections */ if ((elf_secthead_get_type(shead) & SHT_NOBITS) == SHT_NOBITS) @@ -448,7 +507,7 @@ elf_objfmt_output(FILE *f, yasm_sectionhead *sections, int all_syms) * if all_syms, register them by name. if not, use strtab entry 0 */ yasm_symrec_traverse((void *)&all_syms, elf_objfmt_append_local_sym); elf_symtab_nlocal = elf_symtab_assign_indices(elf_symtab); - + /* output known sections - includes reloc sections which aren't in yasm's * list. Assign indices as we go. */ info.sindex = 3; @@ -483,6 +542,21 @@ elf_objfmt_output(FILE *f, yasm_sectionhead *sections, int all_syms) return; elf_shead_addr = (unsigned long) pos; + /* stabs debugging support */ + if (strcmp(cur_dbgfmt->keyword, "stabs")==0) { + yasm_section *stabsect = yasm_sections_find_general(sections, ".stab"); + yasm_section *stabstrsect = yasm_sections_find_general(sections, ".stabstr"); + if (stabsect && stabstrsect) { + elf_secthead *stab = yasm_section_get_of_data(stabsect); + elf_secthead *stabstr = yasm_section_get_of_data(stabstrsect); + if (stab && stabstr) { + elf_secthead_set_link(stab, elf_secthead_get_index(stabstr)); + } + else + yasm_internal_error(N_("missing .stab or .stabstr section/data")); + } + } + /* output dummy section header - 0 */ info.sindex = 0; @@ -722,6 +796,7 @@ elf_objfmt_directive(/*@unused@*/ const char *name, /* Define valid debug formats to use with this object format */ static const char *elf_objfmt_dbgfmt_keywords[] = { "null", + "stabs", NULL }; diff --git a/modules/objfmts/elf/elf.c b/modules/objfmts/elf/elf.c index d4b06d2f..d18fcfe4 100644 --- a/modules/objfmts/elf/elf.c +++ b/modules/objfmts/elf/elf.c @@ -891,6 +891,12 @@ elf_secthead_get_sym(elf_secthead *shead) return shead->sym; } +elf_section_index +elf_secthead_get_index(elf_secthead *shead) +{ + return shead->index; +} + const yasm_intnum * elf_secthead_set_align(elf_secthead *shead, yasm_intnum *align) { @@ -930,6 +936,12 @@ elf_secthead_set_rel_name(elf_secthead *shead, elf_strtab_entry *entry) return shead->rel_name = entry; } +elf_size +elf_secthead_set_entsize(elf_secthead *shead, elf_size size) +{ + return shead->entsize = size; +} + yasm_symrec * elf_secthead_set_sym(elf_secthead *shead, yasm_symrec *sym) { diff --git a/modules/objfmts/elf/elf.h b/modules/objfmts/elf/elf.h index 9a825973..c06fa7ea 100644 --- a/modules/objfmts/elf/elf.h +++ b/modules/objfmts/elf/elf.h @@ -395,6 +395,7 @@ int elf_secthead_is_empty(elf_secthead *shead); struct yasm_symrec *elf_secthead_get_sym(elf_secthead *shead); const struct yasm_intnum *elf_secthead_set_align(elf_secthead *shead, struct yasm_intnum *align); +elf_section_index elf_secthead_get_index(elf_secthead *shead); elf_section_info elf_secthead_set_info(elf_secthead *shead, elf_section_info info); elf_section_index elf_secthead_set_index(elf_secthead *shead, @@ -405,6 +406,7 @@ elf_section_index elf_secthead_set_rel_index(elf_secthead *shead, elf_section_index sectidx); elf_strtab_entry *elf_secthead_set_rel_name(elf_secthead *shead, elf_strtab_entry *entry); +elf_size elf_secthead_set_entsize(elf_secthead *shead, elf_size size); struct yasm_symrec *elf_secthead_set_sym(elf_secthead *shead, struct yasm_symrec *sym); void elf_secthead_add_size(elf_secthead *shead, yasm_intnum *size);