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, &sect, &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);