/****************************************************************************
*
*  This code is Public Domain.
*
*  ========================================================================
*
* Description:  COFF output routines
*
****************************************************************************/

#include <ctype.h>
#include <time.h>

#include "globals.h"
#include "memalloc.h"
#include "mangle.h"
#include "parser.h"
#include "fixup.h"
#include "segment.h"
#include "extern.h"
#include "coff.h"
#include "coffspec.h"
#include "input.h"
#include "myassert.h"
#include "omfspec.h"
#include "linnum.h"

#if COFF_SUPPORT

#define HELPSYMS     0 /* use helper symbols for assembly time symbol refs */
#define MANGLE_BYTES 8 /* extra size required for name decoration */
/* v2.04: to make JWasm always add static (=private) procs to the symbol table
 * set STATIC_PROCS to 1. Normally those procs are only added if line
 * number/symbolic debugging information is generated.
 */
#define STATIC_PROCS 1 /* v2.10: changed to 1, because Masm 6-10 DOES add static procs */
#define COMPID       0 /* 1=add comp.id absolute symbol */
/* size of codeview temp segment buffer; prior to v2.12 it was 1024 - but this may be
 * insufficient if MAX_ID_LEN has been enlarged.
 */
#define SIZE_CV_SEGBUF ( MAX_LINE_LEN * 2 )

struct stringitem {
    struct stringitem *next;
    char string[1];
};

extern void cv_write_debug_tables( struct dsym *, struct dsym *, void * );

enum dbgseg_index {
    DBGS_SYMBOLS,
    DBGS_TYPES,
    DBGS_MAX
};

struct dbg_section {
    //const char *name;
    struct dsym *seg;
    struct qdesc q;
};

/* -Zi option section info */
static const char * const SymDebName[DBGS_MAX] = { ".debug$S", ".debug$T" };

/* v2.12: all coff global variables replaced by struct coffmod */
struct coffmod {
    struct dsym *directives;    /* .drectve section, contains linker directives */
    struct dsym *sxdata;        /* section for safe exception handler data */
    struct asym *lastproc;      /* used if -Zd is set */
    char        *dot_file_value;/* value of .file symbol (isn't written by Masm v8+) */
    uint_32     start_data;     /* file position of start section data */
    uint_32     size_data;      /* total of section contents (incl relocs + linnums) */
    uint_32     start_files;    /* symbol table index of first .file entry used by line number info */
    uint_32     sectionstart;   /* symbol table index of first section */
    struct dbg_section SymDeb[DBGS_MAX]; /* codeview debug sections */
    struct {
        struct stringitem *head;
        struct stringitem *tail;
        uint_32 size;
    } LongNames;
#ifdef DEBUG_OUT
    uint_32     start_symtab;   /* calculated start of symbol table when section table is written */
#endif
};

static const char szdrectve[] = { ".drectve" };

static const IMAGE_SYMBOL isFeat00 = {
    {"@feat.00"},
     1,
     IMAGE_SYM_ABSOLUTE,
     IMAGE_SYM_TYPE_NULL,
     IMAGE_SYM_CLASS_STATIC,
     0
};
#if COMPID
static const IMAGE_SYMBOL isCompId = {
    {"@comp.id"},
    //8803 + ( 0x2a << 16 ), /* value of Masm v6.15.8803 */
    //9466 + ( 0x40 << 16 ), /* value of Masm v7.00.9466 */
    50727 + ( 0x7d << 16 ), /* value of Masm v8.00.50727.104 */
    //30729 + ( 0x95 << 16 ), /* value of Masm v9.00.30729.01 */
    //30319 + ( 0x9e << 16 ), /* value of Masm v10.00.30319.01 */
    IMAGE_SYM_ABSOLUTE,
    IMAGE_SYM_TYPE_NULL,
    IMAGE_SYM_CLASS_STATIC,
    0 /* # of aux symbols */
};
#endif

/* alloc a string which will be stored in the COFF string table */

static uint_32 Coff_AllocString( struct coffmod *cm, const char *string, int len )
/********************************************************************************/
{
    struct stringitem *name;
    uint_32 oldsize = cm->LongNames.size;

    cm->LongNames.size += len + 1;
    name = LclAlloc( len + sizeof( struct stringitem ) );
    name->next = NULL;
    strcpy( name->string, string );
    if ( cm->LongNames.head ) {
        cm->LongNames.tail->next = name;
        cm->LongNames.tail = name;
    } else {
        cm->LongNames.head = cm->LongNames.tail = name;
    }
    return( oldsize );
}

/* return number of line number items */

static unsigned GetLinnumItems( struct qdesc *q )
/***********************************************/
{
    unsigned i;
    struct line_num_info *curr;

    for ( i = 0, curr = q->head; curr; i++, curr = curr->next );
    return( i );
}

/* write COFF section table */

static ret_code coff_write_section_table( struct module_info *modinfo, struct coffmod *cm )
/*****************************************************************************************/
{
    struct dsym  *curr;
    struct fixup *fix;
    uint_32      fileoffset; /* calculated file offset for section data, relocs and linenumber info */
    unsigned     len;
    char         *secname;
    //struct omf_rec  *objr;
    //char        *p;
    //uint        size_relocs = 0;
    enum seg_type segtype;
    struct IMAGE_SECTION_HEADER ish;
    char buffer[MAX_ID_LEN+1];

    DebugMsg(("coff_write_section_table: enter, sections=%u\n", modinfo->g.num_segs ));

    fileoffset = sizeof( struct IMAGE_FILE_HEADER ) + modinfo->g.num_segs * sizeof( struct IMAGE_SECTION_HEADER );
    cm->start_data = fileoffset;
    for( curr = SymTables[TAB_SEG].head; curr; curr = curr->next ) {

        /* v2.04: can't happen */
        //if( curr->sym.state != SYM_SEG ) {
        //    EmitErr( SEGMENT_NOT_DEFINED, curr->sym.name );
        //    continue;
        //}
        segtype = SEGTYPE_UNDEF;
        /* v2.07: prefer ALIAS name if defined. */
        secname = ( curr->e.seginfo->aliasname ? curr->e.seginfo->aliasname : ConvertSectionName( &curr->sym, &segtype, buffer ) );
        len = strlen( secname );
        /* if section name is longer than 8 chars, a '/' is stored,
         followed by a number in ascii which is the offset for the string table
         */
        if ( len <= IMAGE_SIZEOF_SHORT_NAME )
            strncpy( ish.Name, secname, IMAGE_SIZEOF_SHORT_NAME );
        else
            sprintf( ish.Name, "/%u", Coff_AllocString( cm, secname, len ) );

        /* v2.04: what is the old line supposed to do? */
        //ish.Misc.PhysicalAddress = fileoffset - (size_relocs + sizeof(struct IMAGE_FILE_HEADER) + ModuleInfo->g.num_segs * sizeof(struct IMAGE_SECTION_HEADER));
        ish.Misc.PhysicalAddress = 0;
        ish.VirtualAddress = 0;
        ish.SizeOfRawData = curr->sym.max_offset;
        ish.PointerToRawData = ( ish.SizeOfRawData ? fileoffset : 0 );
        ish.PointerToRelocations = 0;
        ish.PointerToLinenumbers = 0;
        ish.NumberOfRelocations = 0;
        ish.NumberOfLinenumbers = 0;
        ish.Characteristics = 0;

        /* set field Characteristics; optionally reset PointerToRawData/SizeOfRawData */
        if ( curr->e.seginfo->info ) {
            /* v2.09: set "remove" flag for .drectve section, as it was done in v2.06 and earlier */
            if ( curr == cm->directives )
                ish.Characteristics = ( IMAGE_SCN_LNK_INFO | IMAGE_SCN_LNK_REMOVE );
            else
                ish.Characteristics = ( IMAGE_SCN_LNK_INFO | IMAGE_SCN_CNT_INITIALIZED_DATA );
        } else {
            if ( curr->e.seginfo->alignment != MAX_SEGALIGNMENT ) /* ABS not possible */
                ish.Characteristics |= (uint_32)(curr->e.seginfo->alignment + 1) << 20;
#if COMDATSUPP
            if ( curr->e.seginfo->comdat_selection )
                ish.Characteristics |= IMAGE_SCN_LNK_COMDAT;
#endif
            if ( curr->e.seginfo->segtype == SEGTYPE_CODE ) {
                ish.Characteristics |= IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ;
            } else if ( curr->e.seginfo->readonly ) {
                ish.Characteristics |= IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;
            } else if ( curr->e.seginfo->clsym && strcmp( curr->e.seginfo->clsym->name, "CONST" ) == 0 ) {
                ish.Characteristics |= IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;
            } else if ( curr->e.seginfo->segtype == SEGTYPE_BSS || segtype == SEGTYPE_BSS ) {
                /* v2.12: if segtype is bss, ensure that seginfo->segtype is also bss; else
                 * the segment might be written in coff_write_data().
                 */
                curr->e.seginfo->segtype = SEGTYPE_BSS;
                ish.Characteristics |= IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
                /* ish.SizeOfRawData = 0; */
                ish.PointerToRawData = 0;
            } else if ( curr->e.seginfo->combine == COMB_STACK && curr->e.seginfo->bytes_written == 0 ) {
                ish.Characteristics |= IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
                ish.SizeOfRawData = 0;
                ish.PointerToRawData = 0;
            } else
                ish.Characteristics |= IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
        }

        /* manual characteristics set? */
        if ( curr->e.seginfo->characteristics ) {
            ish.Characteristics &= 0x1FFFFFF; /* clear the IMAGE_SCN_MEM flags */
            ish.Characteristics |= (uint_32)(curr->e.seginfo->characteristics & 0xFE) << 24;
            /* the INFO bit (bit 0) needs special handling! */
            //if ( curr->e.seginfo->characteristics & 1 )
            //    ish.Characteristics |= IMAGE_SCN_LNK_INFO;
        }

        if ( ish.PointerToRawData )
            fileoffset += ish.SizeOfRawData;

        /* set fields PointerToRelocations/NumberOfRelocations; update 'fileoffset'.
         * v2.10: don't use the 16-bit NumberOfRelocations member to count relocs!
         * if the true number of relocs doesn't fit in 16-bits, set the appropriate
         * flag in the section header!
         */
        if ( curr->e.seginfo->FixupList.head ) {
            for ( fix = curr->e.seginfo->FixupList.head; fix ; fix = fix->nextrlc ) {
                //printf("segment %s, reloc.sym=%s\n", curr->sym.name, fix->sym ? fix->sym : "NULL" );
                if ( fix->sym == NULL ) {
#if AMD64_SUPPORT
                    if ( fix->type == FIX_RELOFF32 ) {
                        uint_32 *cp = (uint_32 *)( curr->e.seginfo->CodeBuffer + (fix->locofs - curr->e.seginfo->start_loc ));
                        uint_32 src = fix->locofs + fix->addbytes;
                        (*cp) -= src;
                    }
#endif
                    fix->type = FIX_VOID;
                    continue;
                }
                curr->e.seginfo->num_relocs++;
            }
            fileoffset = (fileoffset + 1) & ~1;
            ish.PointerToRelocations = fileoffset;
            /* v2.10: handle the "relocs overflow"-case */
            fileoffset += curr->e.seginfo->num_relocs * sizeof( IMAGE_RELOCATION );
            if ( curr->e.seginfo->num_relocs <= 0xffff ) {
                ish.NumberOfRelocations =  (uint_16)curr->e.seginfo->num_relocs;
            } else {
                DebugMsg(( "coff_write_section_table(%s): %" I32_SPEC "u fixups (more than 0xffff!)!\n", curr->sym.name, curr->e.seginfo->num_relocs ));
                ish.NumberOfRelocations =  0xffff;
                ish.Characteristics |= IMAGE_SCN_LNK_NRELOC_OVFL;
                /* add 1 relocation - the true number of relocations is stored in the first relocation item */
                fileoffset += sizeof( IMAGE_RELOCATION );
            }
            //printf( "segment %s has %u relocs\n", curr->sym.name, curr->e.seginfo->num_relocs );
        }

        /* set fields PointerToLinenumbers/NumberOfLinenumbers; update 'fileoffset' */

        if ( curr->e.seginfo->LinnumQueue ) {
            ish.PointerToLinenumbers = fileoffset;
            ish.NumberOfLinenumbers = GetLinnumItems( curr->e.seginfo->LinnumQueue );
            fileoffset += ish.NumberOfLinenumbers * sizeof( IMAGE_LINENUMBER );
        }

        DebugMsg(( "coff_write_section_table(%s): name=%.8s Fixups=%u, Linnums=%u\n", curr->sym.name, ish.Name, ish.NumberOfRelocations, ish.NumberOfLinenumbers ));
        if ( fwrite( &ish, 1, sizeof( ish ), CurrFile[OBJ] ) != sizeof( ish ) )
            WriteError();
    }
#ifdef DEBUG_OUT
    cm->start_symtab = fileoffset;
#endif
    DebugMsg(("coff_write_section_table: exit\n"));
    return( NOT_ERROR );
}


/*
 * get value for field 'type' ( IMAGE_SYM_TYPE_x ) in symbol table.
 * MS tools use only 0x0 or 0x20.
 */
static short CoffGetType( const struct asym *sym )
/************************************************/
{
    if ( sym->isproc )
        return( 0x20 );

#if 0
    switch (sym->mem_type) {
    case MT_BYTE:
    case MT_SBYTE:
        return( IMAGE_SYM_TYPE_BYTE );
    case MT_WORD:
    case MT_SWORD:
        return( IMAGE_SYM_TYPE_WORD );
    case MT_DWORD:
    case MT_SDWORD:
        return( IMAGE_SYM_TYPE_DWORD );
    }
#endif
    return( IMAGE_SYM_TYPE_NULL );
}

/*
 * get value for field 'class' ( IMAGE_SYM_CLASS_x ) in symbol table.
 */
static short CoffGetClass( const struct asym *sym )
/*************************************************/
{
    if ( sym->state == SYM_EXTERNAL )
        if ( sym->iscomm == FALSE && sym->altname )
            return( IMAGE_SYM_CLASS_WEAK_EXTERNAL );
        else
            return( IMAGE_SYM_CLASS_EXTERNAL );
    else if ( sym->ispublic == TRUE )
        return( IMAGE_SYM_CLASS_EXTERNAL );
#if HELPSYMS
    else if ( sym->variable == TRUE ) /* assembly time variable in fixup */
        return( IMAGE_SYM_CLASS_LABEL );
#endif
    /* v2.09: don't declare private procs as label */
    //else if ( sym->mem_type == MT_NEAR )/* added v2.0 */
    else if ( sym->mem_type == MT_NEAR && sym->isproc == FALSE )
        return( IMAGE_SYM_CLASS_LABEL );

    return( IMAGE_SYM_CLASS_STATIC );
}

/* calc number of entries in symbol table for a file entry */

static unsigned GetFileAuxEntries( uint_16 file, char * *fname )
/**************************************************************/
{
    const struct fname_item *curr;
    unsigned len;
    curr = GetFName( file );
    if ( fname )
        *fname = curr->fname;
    len = strlen( curr->fname );
    /**/myassert( len ); /* name length must be > 0 */
    return ( len / sizeof( IMAGE_AUX_SYMBOL ) + ( len % sizeof( IMAGE_AUX_SYMBOL ) ? 1 : 0 ) );
}

#if COMDATSUPP

/* v2.10: COMDAT CRC calculation ( suggestion by drizz, slightly modified ) */

static uint_32 CRC32Table[256]; /* table is initialized if needed */

static void InitCRC32( void )
/***************************/
{
    int i,j;
    uint_32 crc;
    for ( i = 0; i < 256; i++ ) {
        for ( j = 0, crc = i; j < 8; j++ ) {
            crc = ( crc >> 1 ) ^ (0xEDB88320 * ( crc & 1 ) );
        }
        CRC32Table[i] = crc;
    }
}

static uint_32 CRC32Comdat( uint_8 *lpBuffer, uint_32 dwBufLen, uint_32 dwCRC )
/*****************************************************************************/
{
    static bool init = FALSE;
    uint_8 byt;

    if ( !init ) {
        init = TRUE;
        InitCRC32();
    }
    if ( lpBuffer ) /* v2.11: lpBuffer may be NULL ( uninitialized data segs ) */
        for ( ; dwBufLen; dwBufLen-- ) {
            byt = *lpBuffer++;
            byt = byt ^ (uint_8)dwCRC;
            dwCRC = dwCRC >> 8;
            dwCRC = dwCRC ^ CRC32Table[byt];
        }
    return( dwCRC );
}
#endif

/* write COFF symbol table
 * contents of the table
 * - @@feat.00 if .SAFESEH was set
 * - file (+ 1-n aux)
 * - 2 entries per section
 * - externals, communals and publics
 * - entries for relocations (internal)
 * - aliases (weak externals)
 */
static uint_32 coff_write_symbols( struct module_info *modinfo, struct coffmod *cm )
/**********************************************************************************/
{
    uint_32     cntSymbols = 0;
    struct dsym *curr;
    struct asym *sym;
    struct qnode *q;
    char        *p;
    unsigned    len;
    unsigned    i;
    IMAGE_SYMBOL is;
    IMAGE_AUX_SYMBOL ias;
    unsigned    lastfile = 0;
    char        buffer[MAX_ID_LEN + MANGLE_BYTES + 1];

    DebugMsg(("coff_write_symbols: enter\n"));

#if COMPID
    /* write "@comp.id" entry */
    if ( fwrite( &isCompId, 1, sizeof(IMAGE_SYMBOL), CurrFile[OBJ] ) != sizeof(IMAGE_SYMBOL) )
        WriteError();
    cntSymbols++;
#endif
    /* "@feat.00" entry (for SafeSEH) */
    if ( Options.safeseh ) {
        if ( fwrite( &isFeat00, 1, sizeof(IMAGE_SYMBOL), CurrFile[OBJ] ) != sizeof(IMAGE_SYMBOL) )
            WriteError();
        cntSymbols++;
    }

    /* .file item (optionally disabled by -zlf) */

    if ( Options.no_file_entry == FALSE ) {
        strncpy( (char *)is.N.ShortName, ".file", IMAGE_SIZEOF_SHORT_NAME );
        is.Value = ( Options.line_numbers ? cm->start_files : 0 );  /* index of next .file entry */
        is.SectionNumber = (uint_16)IMAGE_SYM_DEBUG;
        is.Type = IMAGE_SYM_TYPE_NULL;
        is.StorageClass = IMAGE_SYM_CLASS_FILE;

        p = cm->dot_file_value;
        i = strlen( p );
        is.NumberOfAuxSymbols = i / sizeof(IMAGE_AUX_SYMBOL) + (i % sizeof(IMAGE_AUX_SYMBOL) ? 1 : 0);
        if ( fwrite( &is, 1, sizeof(is), CurrFile[OBJ] ) != sizeof(is) )
            WriteError();

        for ( i = is.NumberOfAuxSymbols;i;i--, p += sizeof(IMAGE_AUX_SYMBOL) ) {
            strncpy( (char *)ias.File.Name, p, sizeof(IMAGE_AUX_SYMBOL) );
            if ( fwrite( &ias, 1, sizeof(ias), CurrFile[OBJ] ) != sizeof(ias) )
                WriteError();
        }
        cntSymbols += is.NumberOfAuxSymbols + 1;
    }

    /* next are section entries */

    for( i = 1, curr = SymTables[TAB_SEG].head; curr; curr = curr->next, i++ ) {
        /* v2.07: prefer ALIAS name if defined */
        p = ( curr->e.seginfo->aliasname ? curr->e.seginfo->aliasname : ConvertSectionName( &curr->sym, NULL, buffer ) );
        len = strlen( p );
        if ( len <= IMAGE_SIZEOF_SHORT_NAME )
            strncpy( (char *)is.N.ShortName, p, IMAGE_SIZEOF_SHORT_NAME );
        else {
            is.N.Name.Short = 0;
            is.N.Name.Long = Coff_AllocString( cm, p, len );
        }
        is.Value = 0;
        is.SectionNumber = i;
        is.Type = IMAGE_SYM_TYPE_NULL;
        is.StorageClass = IMAGE_SYM_CLASS_STATIC;
        is.NumberOfAuxSymbols = 0;
        if ( Options.no_section_aux_entry == FALSE )
            is.NumberOfAuxSymbols = 1;

        DebugMsg(("coff_write_symbols(%u, SECT): %s, type=%x, stgcls=%x\n", cntSymbols, curr->sym.name, is.Type, is.StorageClass ));

        if ( fwrite( &is, 1, sizeof(is), CurrFile[OBJ] ) != sizeof(is) )
            WriteError();
        cntSymbols++;

        /* write the auxiliary symbol record for sections.
         * may be suppressed with option -zls.
         */
        if ( Options.no_section_aux_entry == FALSE
#if COMDATSUPP
            || curr->e.seginfo->comdat_selection
#endif
            ) {
            ias.Section.Length = curr->sym.max_offset;
            /* v2.10: check for overflow */
            //ias.Section.NumberOfRelocations = curr->e.seginfo->num_relocs;
            ias.Section.NumberOfRelocations = ( curr->e.seginfo->num_relocs > 0xffff ? 0xffff : (uint_16)curr->e.seginfo->num_relocs );
            if ( Options.line_numbers )
                ias.Section.NumberOfLinenumbers = curr->e.seginfo->num_linnums;
            else
                ias.Section.NumberOfLinenumbers = 0;
            /* CheckSum, Number and Selection are for COMDAT sections only */
#if COMDATSUPP
            if ( curr->e.seginfo->comdat_selection ) {
                ias.Section.CheckSum = CRC32Comdat( curr->e.seginfo->CodeBuffer, curr->sym.max_offset, 0 );
                ias.Section.Number = curr->e.seginfo->comdat_number;
                ias.Section.Selection = curr->e.seginfo->comdat_selection;
            } else {
#endif
                ias.Section.CheckSum = 0;
                ias.Section.Number = 0;
                ias.Section.Selection = 0;
#if COMDATSUPP
            };
#endif
            if ( fwrite( &ias, 1, sizeof(ias), CurrFile[OBJ] ) != sizeof(ias) )
                WriteError();
            DebugMsg(("coff_write_symbols(%u, SECT): %s, AUX, relocs=%u, linnums=%u\n", cntSymbols, curr->sym.name, ias.Section.NumberOfRelocations, ias.Section.NumberOfLinenumbers ));
            cntSymbols++;
        }
    }

    /* third are externals + communals ( + protos [since v2.01] ) */

    for( curr = SymTables[TAB_EXT].head ; curr != NULL ;curr = curr->next ) {
        /* skip "weak" (=unused) externdefs */
        if ( curr->sym.iscomm == FALSE && curr->sym.weak == TRUE ) {
            DebugMsg(("coff_write_symbols(EXT+COMM): %s skipped, used=%u, comm=%u, weak=%u\n", curr->sym.name, curr->sym.used, curr->sym.iscomm, curr->sym.weak ));
            continue;
        }
        len = Mangle( &curr->sym, buffer );

        is.Type = CoffGetType(&curr->sym);
        is.StorageClass = CoffGetClass(&curr->sym);

        DebugMsg(("coff_write_symbols(%u, EXT+COMM): %s, type=%x, stgcls=%x\n", cntSymbols, curr->sym.name, is.Type, is.StorageClass ));

        /* for COMMUNALs, store their size in the Value field */
        if (curr->sym.iscomm == TRUE)
            is.Value = curr->sym.total_size;
        else
            is.Value = curr->sym.offset; /* is always 0 */
        is.SectionNumber = IMAGE_SYM_UNDEFINED;
        is.NumberOfAuxSymbols = (( curr->sym.iscomm == FALSE && curr->sym.altname ) ? 1 : 0 );

        if ( len <= IMAGE_SIZEOF_SHORT_NAME )
            strncpy( (char *)is.N.ShortName, buffer, IMAGE_SIZEOF_SHORT_NAME );
        else {
            is.N.Name.Short = 0;
            is.N.Name.Long = Coff_AllocString( cm, buffer, len );
        }
        if ( fwrite( &is, 1, sizeof(is), CurrFile[OBJ] ) != sizeof(is) )
            WriteError();
        cntSymbols++;

        /* for weak externals, write the auxiliary record */
        if ( curr->sym.iscomm == FALSE && curr->sym.altname ) {
            memset( &ias, 0, sizeof(ias) );
            ias.Sym.TagIndex = curr->sym.altname->ext_idx;
            /* v2.10: weak externals defined via "extern sym (altsym) ..."
             * are to have characteristics IMAGE_WEAK_EXTERN_SEARCH_LIBRARY.
             */
            //ias.Sym.Misc.TotalSize = IMAGE_WEAK_EXTERN_SEARCH_ALIAS;
            ias.Sym.Misc.TotalSize = IMAGE_WEAK_EXTERN_SEARCH_LIBRARY;
            if ( fwrite( &ias, 1, sizeof(ias), CurrFile[OBJ] ) != sizeof(ias) )
                WriteError();
            cntSymbols++;
        }
    }

    /* publics and internal symbols. The internal symbols have
     * been written to the "public" queue inside coff_write_data().
     */
    for ( q = ModuleInfo.g.PubQueue.head; q; q = q->next ) {
        sym = q->sym;
        len = Mangle( sym, buffer );
#ifdef DEBUG_OUT
        if ( sym->state == SYM_INTERNAL && sym->isproc == TRUE && Options.line_numbers )
            DebugMsg(("coff_write_symbols(%u): %s, file=%u\n", cntSymbols, sym->name, sym->debuginfo->file ));
#endif
        if ( Options.line_numbers &&
            sym->isproc &&
            sym->debuginfo->file != lastfile ) {
            lastfile = sym->debuginfo->file;
            strncpy( (char *)is.N.ShortName, ".file", IMAGE_SIZEOF_SHORT_NAME );
            is.SectionNumber = (uint_16)IMAGE_SYM_DEBUG;
            is.Type = IMAGE_SYM_TYPE_NULL;
            is.StorageClass = IMAGE_SYM_CLASS_FILE;
            is.NumberOfAuxSymbols = GetFileAuxEntries( sym->debuginfo->file, &p );
            is.Value = sym->debuginfo->next_file;
            if ( fwrite( &is, 1, sizeof(is), CurrFile[OBJ] ) != sizeof(is) )
                WriteError();

            for ( i = is.NumberOfAuxSymbols; i; i--, p += sizeof(IMAGE_AUX_SYMBOL) ) {
                strncpy( (char *)ias.File.Name, p, sizeof(IMAGE_AUX_SYMBOL) );
                if ( fwrite( &ias, 1, sizeof(ias), CurrFile[OBJ] ) != sizeof(ias) )
                    WriteError();
            }
            cntSymbols += is.NumberOfAuxSymbols + 1;
        }
        is.Type = CoffGetType( sym );
        is.StorageClass = CoffGetClass( sym );
        is.Value = sym->offset;
#if 1 /* v2.07: changed - to make MT_ABS obsolete */
        if ( sym->state == SYM_INTERNAL ) {
            if ( sym->segment )
                is.SectionNumber = GetSegIdx( sym->segment );
            else
                is.SectionNumber = (uint_16)IMAGE_SYM_ABSOLUTE;
        } else
            is.SectionNumber = IMAGE_SYM_UNDEFINED;
#else
        if ( sym->state == SYM_EXTERNAL )
            is.SectionNumber = IMAGE_SYM_UNDEFINED;
        else if ( sym->mem_type == MT_ABS )
            is.SectionNumber = IMAGE_SYM_ABSOLUTE;
        else if ( sym->segment )
            is.SectionNumber = GetSegIdx( sym->segment );
        else
            is.SectionNumber = IMAGE_SYM_UNDEFINED;
#endif
        is.NumberOfAuxSymbols = 0;
        if ( Options.line_numbers && sym->isproc )
            is.NumberOfAuxSymbols++;

        if ( len <= IMAGE_SIZEOF_SHORT_NAME )
            strncpy( (char *)is.N.ShortName, buffer, IMAGE_SIZEOF_SHORT_NAME );
        else {
            is.N.Name.Short = 0;
            is.N.Name.Long = Coff_AllocString( cm, buffer, len );
        }

        DebugMsg(("coff_write_symbols(%u, PUB+INT): %s, ofs=%X, type=%X, stgcls=%X\n", cntSymbols, buffer, is.Value, is.Type, is.StorageClass ));

        if ( fwrite( &is, 1, sizeof(is), CurrFile[OBJ] ) != sizeof(is) )
            WriteError();
        cntSymbols++;
        if ( Options.line_numbers && sym->isproc ) {
            /* write:
             * 1.   the aux for the proc
             * 2+3. a .bf record with 1 aux
             * 4.   a .lf record with 0 aux
             * 5+6. a .ef record with 1 aux
             */
            ias.Sym.TagIndex = cntSymbols+1;
            ias.Sym.Misc.TotalSize = sym->total_size;
            ias.Sym.FcnAry.Function.PointerToLinenumber = sym->debuginfo->ln_fileofs;
            ias.Sym.FcnAry.Function.PointerToNextFunction = sym->debuginfo->next_proc;
            if ( fwrite( &ias, 1, sizeof(ias), CurrFile[OBJ] ) != sizeof(ias) )
                WriteError();

            strncpy( (char *)is.N.ShortName, ".bf", IMAGE_SIZEOF_SHORT_NAME );
            is.Type = IMAGE_SYM_TYPE_NULL;
            is.NumberOfAuxSymbols = 1;
            is.StorageClass = IMAGE_SYM_CLASS_FUNCTION;
            if ( fwrite( &is, 1, sizeof(is), CurrFile[OBJ] ) != sizeof(is) )
                WriteError();
            ias.Sym.TagIndex = 0;
            ias.Sym.Misc.LnSz.Linenumber = sym->debuginfo->start_line;
            if ( sym->debuginfo->next_proc )
                ias.Sym.FcnAry.Function.PointerToNextFunction = sym->debuginfo->next_proc + 2;
            else
                ias.Sym.FcnAry.Function.PointerToNextFunction = 0;
            if ( fwrite( &ias, 1, sizeof(ias), CurrFile[OBJ] ) != sizeof(ias) )
                WriteError();

            strncpy( (char *)is.N.ShortName, ".lf", IMAGE_SIZEOF_SHORT_NAME );
            is.Type = IMAGE_SYM_TYPE_NULL;
            is.NumberOfAuxSymbols = 0;
            is.StorageClass = IMAGE_SYM_CLASS_FUNCTION;
            is.Value = sym->debuginfo->line_numbers;
            if ( fwrite( &is, 1, sizeof(is), CurrFile[OBJ] ) != sizeof(is) )
                WriteError();

            strncpy( (char *)is.N.ShortName, ".ef", IMAGE_SIZEOF_SHORT_NAME );
            is.Type = IMAGE_SYM_TYPE_NULL;
            is.NumberOfAuxSymbols = 1;
            is.StorageClass = IMAGE_SYM_CLASS_FUNCTION;
            is.Value = sym->offset + sym->total_size;
            if ( fwrite( &is, 1, sizeof(is), CurrFile[OBJ] ) != sizeof(is) )
                WriteError();
            ias.Sym.TagIndex = 0;
            ias.Sym.Misc.LnSz.Linenumber = sym->debuginfo->end_line;
            if ( fwrite( &ias, 1, sizeof(ias), CurrFile[OBJ] ) != sizeof(ias) )
                WriteError();

            cntSymbols += 6;
        }
    }

    /* aliases. A weak external entry with 1 aux entry is created.
     */
    for( curr = SymTables[TAB_ALIAS].head ; curr != NULL ;curr = curr->next ) {
        struct asym * sym;

        len = Mangle( &curr->sym, buffer );

        if ( len <= IMAGE_SIZEOF_SHORT_NAME )
            strncpy( (char *)is.N.ShortName, buffer, IMAGE_SIZEOF_SHORT_NAME );
        else {
            is.N.Name.Short = 0;
            is.N.Name.Long = Coff_AllocString( cm, buffer, len );
        }

        is.Value = 0;
        is.SectionNumber = IMAGE_SYM_UNDEFINED;
        is.Type = IMAGE_SYM_TYPE_NULL;
        is.StorageClass = IMAGE_SYM_CLASS_WEAK_EXTERNAL;
        is.NumberOfAuxSymbols = 1;

        DebugMsg(("coff_write_symbols(%u, ALIAS): symbol %s, ofs=%X\n", cntSymbols, buffer, is.Value ));

        if ( fwrite( &is, 1, sizeof(is), CurrFile[OBJ] ) != sizeof(is) )
            WriteError();
        cntSymbols++;

        memset( &ias, 0, sizeof(ias) );

        /* v2.04b: adjusted to new field <substitute> */
        //sym = SymSearch( curr->sym.string_ptr );
        sym = curr->sym.substitute;
        if (sym)
            ias.Sym.TagIndex = sym->ext_idx;

        ias.Sym.Misc.TotalSize = IMAGE_WEAK_EXTERN_SEARCH_ALIAS;
        if ( fwrite( &ias, 1, sizeof(ias), CurrFile[OBJ] ) != sizeof(ias) )
            WriteError();
        cntSymbols++;

    }

    DebugMsg(("coff_write_symbols: exit cntSymbols=%u\n", cntSymbols ));
    return( cntSymbols );
}

static int GetStartLabel( struct module_info *modinfo, char *buffer, bool msg )
/*****************************************************************************/
{
    int size = 0;
    char temp[ MAX_ID_LEN + MANGLE_BYTES + 1 ];

    if ( modinfo->g.start_label ) {
        Mangle( modinfo->g.start_label, temp );
        if ( Options.entry_decorated )
            strcpy( buffer, temp );
        else {
            if ( modinfo->g.start_label->langtype != LANG_C &&
                modinfo->g.start_label->langtype != LANG_STDCALL &&
                modinfo->g.start_label->langtype != LANG_SYSCALL ) {
                if ( *modinfo->g.start_label->name != '_' ) {
                    /* v2.12: leading underscore not required if module is 64-bit */
                    /*if ( msg && ( modinfo->fctype != FCT_WIN64 ) ) */
                    if ( msg && ( modinfo->defOfssize != USE64 ) )
                        EmitWarn( 2, LEADING_UNDERSCORE_REQUIRED_FOR_START_LABEL, modinfo->g.start_label->name );
                    strcpy( buffer, temp );
                } else {
                    strcpy( buffer, temp+1 );
                }
            } else
                strcpy( buffer, temp+1 );
        }
        size = strlen( buffer ) + 8; // 8 == size of " -entry:"
    }
    return( size );
}

/* callback to flush contents of codeview symbolic debug info sections */

static uint_8 *coff_flushfunc( struct dsym *seg, uint_8 *curr, unsigned size, void *pv )
/**************************************************************************************/
{
    unsigned currsize = curr - seg->e.seginfo->CodeBuffer;
    struct coffmod *cm;
    int i;
    uint_8 *p;

    if ( ( currsize + size ) > SIZE_CV_SEGBUF ) {
        if ( currsize ) {
            p = LclAlloc( currsize + sizeof( struct qditem ) );
            ((struct qditem *)p)->next = NULL;
            ((struct qditem *)p)->size = currsize;
            memcpy( p + sizeof( struct qditem ), seg->e.seginfo->CodeBuffer, currsize );
            cm = pv;
            i = ( seg == cm->SymDeb[DBGS_TYPES].seg ? DBGS_TYPES : DBGS_SYMBOLS );
            if ( cm->SymDeb[i].q.head == NULL )
                cm->SymDeb[i].q.head = cm->SymDeb[i].q.tail = p;
            else {
                ((struct qditem *)(cm->SymDeb[i].q.tail))->next = p;
                cm->SymDeb[i].q.tail = p;
            }
            seg->e.seginfo->current_loc = seg->e.seginfo->start_loc + currsize;
            seg->e.seginfo->start_loc = seg->e.seginfo->current_loc;
        }
        return( seg->e.seginfo->CodeBuffer );
    }
    return( curr );
}

/* calc the current number of entries in the symbol table
 * so we know the index if a new entry has to be added
 * called by coff_write_data()
 */
static uint_32 SetSymbolIndices( struct module_info *ModuleInfo, struct coffmod *cm )
/***********************************************************************************/
{
    struct qnode *q;
    struct dsym  *curr;
    struct asym  *sym;
    uint_32 index;
    uint_32 i;
    struct asym *lastfproc;
    unsigned lastfile = 0;

    index = 0;
    cm->lastproc = NULL;
    cm->start_files = 0;  /* v2.11: added */

#if COMPID
    index++;
#endif
    /* add absolute symbol @@feat.00 if -SAFESEH is set */
    if ( Options.safeseh )
        index++;

    /* count AUX entries for .file. Depends on sizeof filename */

    if ( Options.no_file_entry == FALSE ) {
        i = strlen( cm->dot_file_value );
        index += i / sizeof( IMAGE_AUX_SYMBOL ) + 1;
        if ( i % sizeof( IMAGE_AUX_SYMBOL ) )
            index++;
    }

    /* add entries for sections */

    cm->sectionstart = index;
    index += ModuleInfo->g.num_segs;
    if ( Options.no_section_aux_entry == FALSE )
        index += ModuleInfo->g.num_segs;

    /* count externals and protos */
    for( curr = SymTables[TAB_EXT].head ; curr != NULL ; curr = curr->next ) {
        if ( curr->sym.iscomm == FALSE && curr->sym.weak == TRUE )
            continue;
        curr->sym.ext_idx = index++;
        /* weak externals need an additional aux entry */
        if ( curr->sym.iscomm == FALSE && curr->sym.altname )
            index++;
    }

#if STATIC_PROCS
    /* v2.04: count private procedures (will become static symbols) */
    if ( Options.no_static_procs == FALSE )
        for( curr = SymTables[TAB_PROC].head ; curr != NULL ; curr = curr->nextproc )
            if ( curr->sym.state == SYM_INTERNAL && curr->sym.ispublic == FALSE && curr->sym.included == FALSE ) {
                curr->sym.included = TRUE;
                AddPublicData( &curr->sym );
            }
#endif
    /* count items in public queue */
    for ( q = ModuleInfo->g.PubQueue.head; q; q = q->next ) {
        sym = q->sym;
        /* if line numbers are on, co, add 6 entries for procs */
        if ( Options.line_numbers && sym->isproc ) {
            if (  sym->debuginfo->file != lastfile ) {
                if ( cm->start_files == 0 )
                    cm->start_files = index;
                else
                    lastfproc->debuginfo->next_file = index;
                lastfproc = sym;
                index += 1 + GetFileAuxEntries( sym->debuginfo->file, NULL );
                lastfile = sym->debuginfo->file;
            }
            sym->ext_idx = index++;
            index += 6;
        } else
            sym->ext_idx = index++;
    }
    return( index );
}

/* write fixups for a section. */

static void coff_write_fixups( struct dsym *section, uint_32 *poffset, uint_32 *pindex )
/**************************************************************************************/
{
    uint_32 offset = *poffset;
    uint_32 index = *pindex;
    struct fixup *fix;
    IMAGE_RELOCATION ir;

    /* v2.10: handle the reloc-overflow-case */
    if ( section->e.seginfo->num_relocs > 0xffff ) {
        DebugMsg(("coff_write_fixups(%s): enter, num_relocs=%" I32_SPEC "u\n", section->sym.name, section->e.seginfo->num_relocs ));
        ir.VirtualAddress = section->e.seginfo->num_relocs + 1;
        ir.SymbolTableIndex = 0;
        ir.Type = IMAGE_REL_I386_ABSOLUTE; /* doesn't matter if 32- or 64-bit */
        if ( fwrite( &ir, 1, sizeof(ir), CurrFile[OBJ] ) != sizeof(ir) )
            WriteError();
        offset += sizeof( ir );
    }
    /* reset counter */
    section->e.seginfo->num_relocs = 0;

    for ( fix = section->e.seginfo->FixupList.head; fix ; fix = fix->nextrlc ) {
#if AMD64_SUPPORT
        if ( section->e.seginfo->Ofssize == USE64 ) {
            switch ( fix->type ) {
            case FIX_VOID:
                continue;
            case FIX_RELOFF32: /* 32bit offset */
                /* translated to IMAGE_REL_AMD64_REL32_[1|2|3|4|5] */
                ir.Type = IMAGE_REL_AMD64_REL32 + (fix->addbytes - 4);
                break;
            case FIX_OFF32: /* 32bit offset */
                ir.Type = IMAGE_REL_AMD64_ADDR32;
                break;
#if IMAGERELSUPP
            case FIX_OFF32_IMGREL:
                ir.Type = IMAGE_REL_AMD64_ADDR32NB;
                break;
#endif
#if SECTIONRELSUPP
            case FIX_OFF32_SECREL:
                ir.Type = IMAGE_REL_AMD64_SECREL;
                break;
#endif
            case FIX_OFF64: /* 64bit offset */
                ir.Type = IMAGE_REL_AMD64_ADDR64;
                break;
            case FIX_SEG: /* segment fixup */
                ir.Type = IMAGE_REL_AMD64_SECTION; /* ??? */
                break;
#if 0 /* not supported by COFF64! shouldn't reach this point */
            case FIX_RELOFF8:
            case FIX_RELOFF16:
            case FIX_OFF8:
            case FIX_OFF16:
            case FIX_HIBYTE:
            case FIX_PTR16: /* 16bit far pointer */
            case FIX_PTR32: /* 32bit far pointer */
#endif
            default:
                DebugMsg(("coff_write_fixups(%s, %Xh): reloc loc=%X type=%u idx=%u sym=%s\n",
                          section->sym.name, offset, fix->locofs, fix->type, fix->sym->ext_idx, fix->sym->name));
                EmitErr( UNKNOWN_FIXUP_TYPE, fix->type, section->sym.name, fix->locofs );
                continue; /* v2.03: skip this fixup */
                //break;
            }
        } else
#endif
            switch ( fix->type ) {
            case FIX_VOID:
                continue;
            case FIX_RELOFF16: /* 16bit offset */
                ir.Type = IMAGE_REL_I386_REL16;
                break;
            case FIX_OFF16: /* 16bit offset */
                ir.Type = IMAGE_REL_I386_DIR16;
                break;
            case FIX_RELOFF32: /* 32bit offset */
                ir.Type = IMAGE_REL_I386_REL32;
                break;
            case FIX_OFF32: /* 32bit offset */
                ir.Type = IMAGE_REL_I386_DIR32;
                break;
#if IMAGERELSUPP
            case FIX_OFF32_IMGREL:
                ir.Type = IMAGE_REL_I386_DIR32NB;
                break;
#endif
#if SECTIONRELSUPP
            case FIX_OFF32_SECREL:
                ir.Type = IMAGE_REL_I386_SECREL;
                break;
#endif
            case FIX_SEG: /* segment fixup */
                ir.Type = IMAGE_REL_I386_SECTION; /* ??? */
                break;
#if 0
            case FIX_OFF8:
            case FIX_RELOFF8:
            case FIX_HIBYTE:
            case FIX_PTR16: /* 16bit far pointer */
            case FIX_PTR32: /* 32bit far pointer */
#endif
            default:
                DebugMsg(("coff_write_fixups(%s, %Xh): reloc loc=%X type=%u idx=%u sym=%s\n",
                          section->sym.name, offset, fix->locofs, fix->type, fix->sym->ext_idx, fix->sym->name));
                EmitErr( UNKNOWN_FIXUP_TYPE, fix->type, section->sym.name, fix->locofs );
                continue; /* v2.03: skip this fixup */
                //break;
            }
        /* if it's not EXTERNAL/PUBLIC, add symbol */
        /* if it's an assembly time variable, create helper symbol */
        if ( fix->sym->variable == TRUE ) {
#if HELPSYMS
            struct asym *sym;
            char buffer[12];
            sprintf( buffer, "$$%06X", fix->offset );
            sym = SymAlloc( buffer );
            sym->state = fix->sym->state;
            sym->mem_type = fix->sym->mem_type;
            sym->offset = fix->offset;
            sym->segment = fix->segment;
            sym->variable = TRUE; /* storage class LABEL */
            fix->sym = sym;
            AddPublicData( fix->sym );
            fix->sym->idx = index++;
#else
            /* just use the segment entry. This approach requires
             * that the offset is stored inline at the reloc location
             * (patch in fixup.c)
             */
            fix->sym = fix->segment_var;
#endif
        } else if (( fix->sym->state == SYM_INTERNAL ) &&
                   fix->sym->included == FALSE &&
                   fix->sym->ispublic == FALSE ) {
            fix->sym->included = TRUE;
            AddPublicData( fix->sym );
            DebugMsg(("coff_write_fixups(%s, %Xh): %s added to symbol table, idx=%u\n",
                      section->sym.name, offset, fix->sym->name, index ));
            fix->sym->ext_idx = index++;
            if ( Options.line_numbers && fix->sym->isproc )
                index += 6;
        }
        ir.VirtualAddress = fix->locofs;
        ir.SymbolTableIndex = fix->sym->ext_idx;
        if ( fwrite( &ir, 1, sizeof(ir), CurrFile[OBJ] ) != sizeof(ir) )
            WriteError();
        DebugMsg(("coff_write_fixups(%s, %Xh): reloc loc=%X type=%u idx=%u sym=%s\n",
                  section->sym.name, offset, ir.VirtualAddress, ir.Type, ir.SymbolTableIndex, fix->sym->name));
        offset += sizeof( ir );
        section->e.seginfo->num_relocs++;
    } /* end for */
    DebugMsg(("coff_write_fixups(%s): exit, num_relocs=%" I32_SPEC "u\n", section->sym.name, section->e.seginfo->num_relocs ));
    *poffset = offset;
    *pindex = index;
}

/* write section data */

static ret_code coff_write_data( struct module_info *modinfo, struct coffmod *cm )
/********************************************************************************/
{
    struct dsym *section;
    uint_32 offset = 0; /* offset within section contents */
    int i;
    uint_32 index;

    DebugMsg(("coff_write_data(%s): enter\n", modinfo->name ));

    /* v2.11: removed. max_offset has been set already */
    //if ( directives )
    //    directives->sym.max_offset = size_drectve;

    /* calc the current index for the COFF symbol table */
    index = SetSymbolIndices( modinfo, cm );

    /* fill the SafeSEH array */
    if ( modinfo->g.SafeSEHQueue.head ) {
        struct qnode *sehp;
        struct qnode *sehp2;
        uint_32 *pdw;
        for( sehp = modinfo->g.SafeSEHQueue.head, pdw = (uint_32 *)cm->sxdata->e.seginfo->CodeBuffer; sehp ; sehp = sehp2 ) {
            sehp2 = sehp->next;
            DebugMsg(("coff_write_data: .sxdata value=%08Xh\n", ((struct asym *)sehp->elmt)->ext_idx));
            *pdw++ = ((struct asym *)sehp->elmt)->ext_idx;
            LclFree( sehp );
        }
    }

#if HELPSYMS==0
    for( i = 0, section = SymTables[TAB_SEG].head; section ; i++, section = section->next ) {
        section->sym.ext_idx = cm->sectionstart + i;
        if ( Options.no_section_aux_entry == FALSE )
            section->sym.ext_idx += i;
    }
#endif

    /* now scan the section's relocations. If a relocation refers to
     a symbol which is not public/external, it must be added to the
     symbol table. If the symbol is an assembly time variable, a helper
     symbol - name is $$<offset:6> is to be added.
     */

    for( section = SymTables[TAB_SEG].head; section ; section = section->next ) {
        uint_32 size;
        size = section->sym.max_offset;
#ifdef DEBUG_OUT
        if ( section->e.seginfo->CodeBuffer )
            DebugMsg(("coff_write_data(%s, %Xh): size=%X, written=%X, content=[%02X %02X ...]\n",
                      section->sym.name, offset, size - section->e.seginfo->start_loc, section->e.seginfo->bytes_written, *(section->e.seginfo->CodeBuffer), *(section->e.seginfo->CodeBuffer+1)));
        else
            DebugMsg(("coff_write_data(%s, %Xh): size=%X, buffer=NULL\n",
                      section->sym.name, offset, size - section->e.seginfo->start_loc ));
#endif

        /* don't write section data for bss and uninitialized stack segments */
        if ( section->e.seginfo->combine == COMB_STACK && section->e.seginfo->bytes_written == 0 )
            continue;
        if ( section->e.seginfo->segtype == SEGTYPE_BSS )
            continue;

        if ( size ) {
            offset += size;
            if ((offset & 1) && section->e.seginfo->FixupList.head ) {
                offset++;
                size++;
            }
            if ( section->e.seginfo->CodeBuffer == NULL ) {
                fseek( CurrFile[OBJ], size, SEEK_CUR );
            } else {
                /* if there was an ORG, the buffer content will
                 * start with the ORG address. The bytes from
                 * 0 - ORG must be written by moving the file pointer!
                 */
                if ( section->e.seginfo->start_loc ) {
                    fseek( CurrFile[OBJ], section->e.seginfo->start_loc, SEEK_CUR );
                    size -= section->e.seginfo->start_loc;
                }

                if ( fwrite( section->e.seginfo->CodeBuffer, 1, size, CurrFile[OBJ] ) != size )
                    WriteError();
            }

            coff_write_fixups( section, &offset, &index );
        } /* end if (size) */

        /* v2.07: the following block has beem moved outside of "if(size)" block,
         * because it may happen that a segment has size 0 and still debug info.
         * In any case it's important to initialize section->e.seginfo->num_linnums
         */
        /* write line number data. The peculiarity of COFF (compared to OMF) is
         * that line numbers are always relative to a function start.
         *
         * an item with line number 0 has a special meaning - it contains a reference
         * ( a symbol table index ) to the function whose line numbers are to follow.
         * this item is to be followed by an item with line number 32767, which then
         * tells the function's start offset ( undocumented! ).
         *
         */
        if( Options.line_numbers && section->e.seginfo->LinnumQueue ) {
            IMAGE_LINENUMBER il;
            struct line_num_info *lni;
            struct asym *last;
            uint_32 line_numbers = 0;
            last = NULL;
            lni = (struct line_num_info *)((struct qdesc *)section->e.seginfo->LinnumQueue)->head;
            DebugMsg(("coff_write_data(%s): writing linnum data\n", section->sym.name ));
            for( ; lni; lni = lni->next ) {
                DebugMsg(("coff_write_data(%s, %Xh): linnum, #=%u, %s=%X, sym=%s\n",
                          section->sym.name, offset, lni->number, lni->number ? "ofs" : "idx", lni->offset, lni->number ? "NULL" : lni->sym->name ));
                if ( lni->number == 0 ) {
                    last = lni->sym;
                    if ( cm->lastproc )
                        cm->lastproc->debuginfo->next_proc = lni->sym->ext_idx;
                    cm->lastproc = lni->sym;
                    lni->sym->debuginfo->next_proc = 0;
                    il.Linenumber = 0;
#ifdef DEBUG_OUT
                    /* if symbol table index is 0, then the proc wasn't added
                     * to the "publics" queue in AddLinnumDataRef().
                     */
                    if ( lni->sym->ext_idx == 0 ) {
                        DebugMsg(("coff_write_data(%s, %Xh): error, %s has symbol table index 0\n",
                                  section->sym.name, offset, lni->sym->name ));
                    }
#endif
                    il.Type.SymbolTableIndex = lni->sym->ext_idx;
                    lni->sym->debuginfo->start_line = lni->line_number;
                    //((struct dsym *)lni->sym)->e.procinfo->file = lni->file;
                    lni->sym->debuginfo->ln_fileofs = cm->start_data + offset;
                    DebugMsg(("coff_write_data(%s, %Xh): proc %s start_line=%u\n",
                              section->sym.name, offset, lni->sym->name, lni->sym->debuginfo->start_line ));
                } else {
                    /**/myassert( last != NULL );
                    il.Linenumber = lni->number - last->debuginfo->start_line;
                    /* if current line number - start line number is 0,
                     * generate a "32767" line number item.
                     */
                    if ( il.Linenumber == 0 )
                        il.Linenumber = 0x7FFF;
                    il.Type.VirtualAddress = lni->offset;
                }
                /* v2.11: last is always != NULL */
                //if ( last ) {
                    last->debuginfo->line_numbers++;
                    last->debuginfo->end_line = lni->number;
                //}
                if ( fwrite( &il, 1, sizeof(il), CurrFile[OBJ] ) != sizeof(il) )
                    WriteError();
                offset += sizeof(il);
                line_numbers++;
            } /* end for */
            section->e.seginfo->num_linnums = line_numbers;
        } /* end if (Options.line_numbers...) */
    } /* end for */

    cm->size_data = offset;

    DebugMsg(("coff_write_data: exit, size of sections=%Xh\n", cm->size_data ));

    return( NOT_ERROR );
}

/* check if a .drectve section has to be created.
 * if yes, create it and set its contents.
 */

static void coff_create_drectve( struct module_info *modinfo, struct coffmod *cm )
/********************************************************************************/
{
    struct dsym *exp;
#if DLLIMPORT
    struct dsym *imp = NULL;
#endif
    char buffer[MAX_ID_LEN + MANGLE_BYTES + 1];

    /* does a proc exist with the EXPORT attribute? */
    for( exp = SymTables[TAB_PROC].head; exp != NULL; exp = exp->nextproc ) {
        if( exp->e.procinfo->isexport )
            break;
    }
#if DLLIMPORT
    /* check if an impdef record is there */
    if ( Options.write_impdef && !Options.names[OPTN_LNKDEF_FN] )
        for ( imp = SymTables[TAB_EXT].head; imp; imp = imp->next )
            if ( imp->sym.isproc && ( imp->sym.weak == FALSE || imp->sym.iat_used == TRUE ) )
                if ( imp->sym.dll && *imp->sym.dll->name )
                    break;
#endif
    /* add a .drectve section if
     - a start_label is defined    and/or
     - a library is included       and/or
     - a proc is exported          and/or
     - impdefs are to be written (-Zd)
     */
    if ( modinfo->g.start_label != NULL ||
        modinfo->g.LibQueue.head != NULL ||
#if DLLIMPORT
        imp != NULL ||
#endif
        exp != NULL ) {
        if ( cm->directives = (struct dsym *)CreateIntSegment( szdrectve, "", MAX_SEGALIGNMENT, modinfo->Ofssize, FALSE ) ) {
            struct dsym *tmp;
            int size = 0;
            struct qitem *q;
            uint_8 *p;
            cm->directives->e.seginfo->info = TRUE;
            DebugMsg(("coff_create_drectve: %s section added\n", szdrectve));

            /* calc the size for this segment */
            /* 1. exports */
            for( tmp = exp; tmp ; tmp = tmp->nextproc ) {
                if( tmp->e.procinfo->isexport ) {
                    size += Mangle( &tmp->sym, buffer );
                    size += sizeof("-export:");
                    if ( Options.no_export_decoration == TRUE )
                        size += tmp->sym.name_size + 1;
                }
            }
            /* 2. defaultlibs */
            for( q = modinfo->g.LibQueue.head; q ; q = q->next ) {
                size += strlen( q->value ) + sizeof("-defaultlib:");
                /* if the name isn't enclosed in double quotes and contains
                 a space, add 2 bytes to enclose it */
                if ( *q->value != '"' && strchr( q->value, ' ') )
                    size += 2;
            }
            /* 3. start label */
            size += GetStartLabel( modinfo, buffer, TRUE );
#if DLLIMPORT
            /* 4. impdefs */
            for( tmp = imp; tmp ; tmp = tmp->next ) {
                if ( tmp->sym.isproc && ( tmp->sym.weak == FALSE || tmp->sym.iat_used == TRUE ) &&
                    tmp->sym.dll && *tmp->sym.dll->name ) {
                    /* format is:
                     * "-import:<mangled_name>=<module_name>.<unmangled_name>" or
                     * "-import:<mangled_name>=<module_name>"
                     */
                    size += sizeof("-import:");
                    size += Mangle( &tmp->sym, buffer );
                    size += 1 + strlen( tmp->sym.dll->name );
                    size += 1 + tmp->sym.name_size;
                }
            }
#endif
            cm->directives->sym.max_offset = size;
            /* v2.09: allocate 1 byte more, because sprintf() is used, which
             * adds a NULLC.
             */
            //directives->e.seginfo->CodeBuffer = LclAlloc( size );
            cm->directives->e.seginfo->CodeBuffer = LclAlloc( size + 1 );
            p = cm->directives->e.seginfo->CodeBuffer;

            /* copy the data */

            /* 1. exports */
            for( tmp = exp; tmp ; tmp = tmp->nextproc ) {
                if( tmp->e.procinfo->isexport ) {
                    Mangle( &tmp->sym, buffer );
                    if ( Options.no_export_decoration == FALSE )
                        p += sprintf( (char *)p, "-export:%s ", buffer );
                    else
                        p += sprintf( (char *)p, "-export:%s=%s ", tmp->sym.name, buffer );
                }
            }
            /* 2. libraries */
            for( q = modinfo->g.LibQueue.head; q ; q = q->next ) {
                if ( *q->value != '"' && strchr( q->value, ' ' ) )
                    p += sprintf( (char *)p,"-defaultlib:\"%s\" ", (char *)q->value );
                else
                    p += sprintf( (char *)p,"-defaultlib:%s ", (char *)q->value );
            }
            /* 3. entry */
            if ( modinfo->g.start_label ) {
                GetStartLabel( modinfo, buffer, FALSE );
                p += sprintf( (char *)p, "-entry:%s ", buffer );
            }
#if DLLIMPORT
            /* 4. impdefs */
            for( tmp = imp; tmp ; tmp = tmp->next ) {
                if ( tmp->sym.isproc && ( tmp->sym.weak == FALSE || tmp->sym.iat_used == TRUE ) &&
                    tmp->sym.dll && *tmp->sym.dll->name ) {
                    strcpy( (char *)p, "-import:" );
                    p += 8;
                    p += Mangle( &tmp->sym, (char *)p );
                    *p++ = '=';
                    strcpy( (char *)p, tmp->sym.dll->name );
                    p += strlen( (char *)p );
                    *p++ = '.';
                    memcpy( p, tmp->sym.name, tmp->sym.name_size );
                    p += tmp->sym.name_size;
                    *p++ = ' ';
                }
            }
#endif
            /**/myassert( size == p - cm->directives->e.seginfo->CodeBuffer );
            //size_drectve = p - directives->e.seginfo->CodeBuffer; /* v2.11:removed */
        }
    }
}

/* Write current object module in COFF format.
 * This function is called AFTER all assembly passes have been done.
 */

static ret_code coff_write_module( struct module_info *modinfo )
/**************************************************************/
{
    struct stringitem  *pName;
    struct coffmod cm;
    struct IMAGE_FILE_HEADER ifh;

    DebugMsg(("coff_write_module: enter, sections=%u\n", modinfo->g.num_segs ));

    memset( &cm, 0, sizeof( cm ) );
    //directives = NULL;
    //LongNames.head = NULL;
    cm.LongNames.size = sizeof( uint_32 );

    /* get value for .file symbol */
    cm.dot_file_value = CurrFName[ASM];
#if 0 /* v2.09: always add the name in CurrFName[ASM] */
    cm.dot_file_value += strlen( dot_file_value );
    while ( cm.dot_file_value > CurrFName[ASM] &&
           *(cm.dot_file_value-1) != '/' &&
           *(cm.dot_file_value-1) != '\\') cm.dot_file_value--;
#endif

    /* it might be necessary to add "internal" sections:
     * - .debug$S and .debug$T sections if -Zi was set
     * - .sxdata section if .SAFESEH was used
     * - .drectve section if start label, exports or includelibs are used
     */

    /* if -Zi is set, add .debug$S and .debug$T sections */
    if ( Options.debug_symbols ) {
        int i;
        for ( i = 0; i < DBGS_MAX; i++ ) {
            if ( NULL == ( cm.SymDeb[i].seg = (struct dsym *)CreateIntSegment( SymDebName[i], "", 0, USE32, TRUE ) ) )
                break;
            DebugMsg(("coff_write_module: %s section added\n", SymDebName[i] ));
            cm.SymDeb[i].seg->e.seginfo->characteristics = (IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE) >> 24;
            /* use the source line buffer as code buffer. It isn't needed anymore */
            cm.SymDeb[i].seg->e.seginfo->CodeBuffer = (uint_8 *)CurrSource + i * SIZE_CV_SEGBUF;
            cm.SymDeb[i].seg->e.seginfo->flushfunc = coff_flushfunc;
            cm.SymDeb[i].q.head = NULL;
        }
        if ( i == DBGS_MAX ) {
            cv_write_debug_tables( cm.SymDeb[DBGS_SYMBOLS].seg, cm.SymDeb[DBGS_TYPES].seg, &cm );
            /* the contents have been written in queues. now
             * copy all queue items in ONE buffer.
             */
            for ( i = 0; i < DBGS_MAX; i++ ) {
                uint_8 *src;
                uint_8 *dst;
                if ( cm.SymDeb[i].seg->sym.max_offset ) {
                    cm.SymDeb[i].seg->e.seginfo->CodeBuffer = LclAlloc( cm.SymDeb[i].seg->sym.max_offset );
                    dst = cm.SymDeb[i].seg->e.seginfo->CodeBuffer;
                    for ( src = cm.SymDeb[i].q.head; src; src = ((struct qditem *)src)->next ) {
                        memcpy( dst, src + sizeof( struct qditem ), ((struct qditem *)src)->size );
                        dst += ((struct qditem *)src)->size;
                        /* fixme: free the queue items if FASTMEM==0 */
                    }
                }
            }
        }
    }

    /* if safeSEH procs are defined, add a .sxdata section */
    if ( modinfo->g.SafeSEHQueue.head ) {
        struct qnode *sehp;
        unsigned cnt = 0;
        if ( cm.sxdata = (struct dsym *)CreateIntSegment( ".sxdata", "", MAX_SEGALIGNMENT, modinfo->Ofssize, FALSE ) ) {
            cm.sxdata->e.seginfo->info = TRUE;
            /* calc the size for this segment */
            for( sehp = modinfo->g.SafeSEHQueue.head; sehp ; sehp = sehp->next, cnt++ );
            cm.sxdata->sym.max_offset = cnt*4;
            cm.sxdata->e.seginfo->CodeBuffer = LclAlloc( cnt*4 );
            DebugMsg(("coff_write_module: .sxdata section added, size=%u\n", cnt*4));
        }
    }

    /* create .drectve section if necessary */
    coff_create_drectve( modinfo, &cm );

    /* v2.11: removed, max_offset has been set already */
    //if ( directives )
    //    directives->sym.max_offset = size_drectve;

#if AMD64_SUPPORT
    if ( modinfo->defOfssize == USE64 )
        ifh.Machine = IMAGE_FILE_MACHINE_AMD64;
    else
#endif
        ifh.Machine = IMAGE_FILE_MACHINE_I386;
    ifh.NumberOfSections = modinfo->g.num_segs;
#if 0 //def __UNIX__
    time((int_32 *)&ifh.TimeDateStamp);
#else
    time((time_t *)&ifh.TimeDateStamp);
#endif
    ifh.SizeOfOptionalHeader = 0;
    ifh.Characteristics = 0;

    /* position behind coff file header */
    fseek( CurrFile[OBJ], sizeof( ifh ), SEEK_SET );

    coff_write_section_table( modinfo, &cm );
    coff_write_data( modinfo, &cm );
    /* v2.12: check that calculated sizes are identical to those that have been written */
#ifdef DEBUG_OUT
    /**/myassert( cm.start_symtab == ( cm.start_data + cm.size_data ) );
#endif
    ifh.NumberOfSymbols = coff_write_symbols( modinfo, &cm );
    ifh.PointerToSymbolTable = ( ifh.NumberOfSymbols ? cm.start_data + cm.size_data : 0 );

    /* the string table is ALWAYS written, even if no strings are defined */
    DebugMsg(("coff_write_module: string_table size=%u\n", cm.LongNames.size ));
    if ( fwrite( &cm.LongNames.size, 1, sizeof( cm.LongNames.size ), CurrFile[OBJ] ) != sizeof( cm.LongNames.size ) )
        WriteError();
    for ( pName = cm.LongNames.head; pName; pName = pName->next ) {
        int i = strlen( pName->string ) + 1;
        if ( fwrite( pName->string, 1, i, CurrFile[OBJ] ) != i )
            WriteError();
    }
#if FASTMEM==0
    for ( ; cm.LongNames.head; ) {
        pName = cm.LongNames.head;
        cm.LongNames.head = cm.LongNames.head->next;
        LclFree( pName );
    }
#endif
    /* finally write the COFF file header */
    fseek( CurrFile[OBJ], 0, SEEK_SET);
    if ( fwrite( &ifh, 1, sizeof( ifh ), CurrFile[OBJ] ) != sizeof( ifh ) ) {
        DebugMsg(("coff_write_module: error writing file header\n"));
        WriteError();
    }

    DebugMsg(("coff_write_module: exit\n"));
    return( NOT_ERROR );
}

void coff_init( struct module_info *modinfo )
/*******************************************/
{
    modinfo->g.WriteModule = coff_write_module;
    return;
}
#endif
