/****************************************************************************
*
*  This code is Public Domain.
* https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter6-43405.html#scrolltoc
*
*  ========================================================================
*
* Description:  ELF output routines
*
****************************************************************************/

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

#include "globals.h"
#include "memalloc.h"
#include "parser.h"
#include "mangle.h"
#include "fixup.h"
#include "segment.h"
#include "extern.h"
#include "elf.h"
#include "elfspec.h"
#include "myassert.h"

#if ELF_SUPPORT

/* v2.03: create weak externals for ALIAS symbols.
 * Since the syntax of the ALIAS directive requires
 * 2 names and, OTOH, the ELF implementation of weak 
 * externals has no "default resolution", the usefullness
 * of this option is questionable. Additionally, there's
 * the "EXTERN <name> (<altname>)" syntax, which also allows
 * to define a weak external.
 */
#define ELFALIAS 0

/* start label is always public for COFF/ELF, no need to add it */
#define ADDSTARTLABEL 0

/* there's no STT_IMPORT type for ELF, it's OW specific */
#define OWELFIMPORT 0

/* use GNU extensions for LD ( 16bit and 8bit relocations ) */
#define GNURELOCS 1

#define MANGLE_BYTES 8 /* extra size required for name decoration */

#define IsWeak( x ) ( x.iscomm == FALSE && x.altname )

/* section attributes for ELF
 *         execute write  alloc  type
 *---------------------------------------
 * CODE      x              x    progbits
 * CONST                    x    progbits
 * DATA              x      x    progbits
 * BSS               x      x    nobits
 * STACK             x      x    progbits
 * others            x      x    progbits
 *
 * todo: translate section bits:
 * - INFO    -> SHT_NOTE  (added in v2.07)
 * - DISCARD ->
 * - SHARED  ->
 * - EXECUTE -> SHF_EXECINSTR
 * - READ    ->
 * - WRITE   -> SHF_WRITE
 */

/* v2.12: global variables replaced by struct elfmod */
//static uint_32 symindex;       /* entries in symbol table */
//static uint_32 start_globals;  /* start index globals in symbol table */
//static char *srcname;          /* name of source module (name + extension) */
//static bool extused;

struct localname {
    void *next;
    struct asym *sym;
};

enum internal_sections {
    SHSTRTAB_IDX,
    SYMTAB_IDX,
    STRTAB_IDX,
    NUM_INTSEGS
};

struct intsegparm {
    const char *name;
    unsigned type;
};

/* constant parameters of internal sections; order must match enum internal_sections */
static const struct intsegparm internal_segparms[] = {
    { ".shstrtab", SHT_STRTAB },
    { ".symtab",   SHT_SYMTAB },
    { ".strtab",   SHT_STRTAB },
};

struct intseg {
    uint_32 size;
    uint_32 fileoffset;
    void *data;
};

//static struct intseg internal_segs[NUM_INTSEGS];

/* v2.12: all global variables replaced by struct elfmod */
struct elfmod {
    uint_32 symindex;       /* entries in symbol table */
    uint_32 start_globals;  /* start index globals in symbol table */
    char *srcname;          /* name of source module (name + extension) */
#if GNURELOCS
    bool extused;           /* gnu extensions used */
#endif
    struct intseg internal_segs[NUM_INTSEGS];
    union {
        Elf32_Ehdr ehdr32;
#if AMD64_SUPPORT
        Elf64_Ehdr ehdr64;
#endif
    };
};

/* struct to help convert section names in COFF, ELF, PE */
struct conv_section {
    uint_8 len;
    uint_8 flags; /* see below */
    const char *src;
    const char *dst;
};
enum cvs_flags {
    CSF_GRPCHK = 1
};

static const struct conv_section cst[] = {
    { 5, CSF_GRPCHK, "_TEXT", ".text"  },
    { 5, CSF_GRPCHK, "_DATA", ".data"  },
    { 5, CSF_GRPCHK, "CONST", ".rodata" }, /* v2.05: .rdata -> .rodata */
    { 4, 0,          "_BSS",  ".bss"   }
};


/* translate section names:
 * see cst[] above for details.
 */
static char *ElfConvertSectionName( const struct asym *sym, char *buffer )
/************************************************************************/
{
    int i;

    for ( i = 0; i < sizeof( cst ) / sizeof( cst[0] ); i++ ) {
        if ( memcmp( sym->name, cst[i].src, cst[i].len ) == 0 ) {
            if ( sym->name[cst[i].len] == NULLC )
                return( (char *)cst[i].dst );
            else if ( ( cst[i].flags & CSF_GRPCHK )  && sym->name[cst[i].len] == '$' ) {
                strcpy( buffer, cst[i].dst );
                strcat( buffer, sym->name+cst[i].len );
                return( buffer );
            }
        }
    }
    return( sym->name );
}

/* get number of sections that have relocations */

static int get_num_reloc_sections( void )
/***************************************/
{
    struct dsym    *curr;
    int num = 0;

    for( curr = SymTables[TAB_SEG].head; curr; curr = curr->next ) {
        if ( curr->e.seginfo->FixupList.head )
            num++;
    }
    return( num );
}

/* fill entries in ELF32 symbol table */

static uint_32 set_symtab32( struct elfmod *em, uint_32 entries, struct localname *localshead )
/*********************************************************************************************/
{
    uint_32   strsize = 1;
    uint_32   len;
    uint_8    stt;
    struct dsym   *curr;
    struct asym   *sym;
    struct localname *localscurr;
    struct qnode  *q;
    Elf32_Sym *p32;
    char      buffer[MAX_ID_LEN + MANGLE_BYTES + 1];

    em->internal_segs[SYMTAB_IDX].size = entries * sizeof( Elf32_Sym );
    em->internal_segs[SYMTAB_IDX].data = LclAlloc( em->internal_segs[SYMTAB_IDX].size );
    memset( em->internal_segs[SYMTAB_IDX].data, 0, em->internal_segs[SYMTAB_IDX].size );

    p32 = (Elf32_Sym *)em->internal_segs[SYMTAB_IDX].data;

    p32++; /* skip NULL entry */

    /* 1. make file entry */
    p32->st_name = strsize;  /* symbol's name in string table */
    strsize += strlen( em->srcname ) + 1;
    p32->st_value = 0;
    p32->st_size = 0;
    p32->st_info = ELF32_ST_INFO( STB_LOCAL, STT_FILE ); /* symbol's type and binding info */
    p32->st_shndx = SHN_ABS; /* section index */
    p32++;

    /* 2. make section entries */
    for( curr = SymTables[TAB_SEG].head; curr; curr = curr->next ) {
        //p32->st_name = ?;  /* name isn't set */
        p32->st_info = ELF32_ST_INFO( STB_LOCAL, STT_SECTION );
        p32->st_shndx = GetSegIdx( curr->sym.segment );
        p32++;
    }

    /* 3. locals */

    for ( localscurr = localshead ; localscurr ; localscurr = localscurr->next ) {
        len = Mangle( localscurr->sym, buffer );
        p32->st_name = strsize;
        curr = (struct dsym *)localscurr->sym->segment;
        if ( curr && curr->e.seginfo->segtype != SEGTYPE_CODE )
            stt = STT_OBJECT;
        else
            stt = STT_FUNC;
        p32->st_info = ELF32_ST_INFO( STB_LOCAL, stt );
        p32->st_value = localscurr->sym->offset;
#if 1 /* v2.07: changed - to make MT_ABS obsolete */
        if ( curr )
            p32->st_shndx = GetSegIdx( &curr->sym );
        else
            p32->st_shndx = SHN_ABS;
#else
        if ( localscurr->sym->mem_type == MT_ABS )
            p32->st_shndx = SHN_ABS;
        else
            p32->st_shndx = GetSegIdx( &curr->sym );
#endif
        strsize += len + 1;
        DebugMsg(("set_symtab32, LOCAL: symbol %s, value=%X\n", buffer, p32->st_value));
        p32++;
    }

    /* 4. 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 )
            continue;
        len = Mangle( &curr->sym, buffer );

        p32->st_name = strsize;

        /* for COMMUNALs, store their size in the Value field */
        if ( curr->sym.iscomm == TRUE ) {
            p32->st_info = ELF32_ST_INFO( STB_GLOBAL, STT_COMMON );
            p32->st_value = curr->sym.total_size;
            p32->st_shndx = SHN_COMMON;
        } else {
#if OWELFIMPORT
            p32->st_info = ( IsWeak( curr->sym ) ? ELF32_ST_INFO( STB_WEAK, STT_IMPORT ) : ELF32_ST_INFO( STB_GLOBAL, STT_IMPORT ) );
#else
            /* todo: set STT_FUNC for prototypes/code labels??? */
            p32->st_info = ( IsWeak( curr->sym ) ? ELF32_ST_INFO( STB_WEAK, STT_NOTYPE ) : ELF32_ST_INFO( STB_GLOBAL, STT_NOTYPE ) );
#endif
            p32->st_value = curr->sym.offset; /* is always 0 */
            p32->st_shndx = SHN_UNDEF;
        }

        strsize += len + 1;
        DebugMsg(("set_symtab32, EXTERNAL: symbol %s, info=%X, shndx=%X, value=%X\n", buffer, p32->st_info, p32->st_shndx, p32->st_value));
        p32++;
    }

#if ELFALIAS
    /* 5. aliases */
    for( curr = SymTables[TAB_ALIAS].head ; curr != NULL ;curr = curr->next ) {
        len = Mangle( &curr->sym, buffer );

        p32->st_name = strsize;

#if OWELFIMPORT
        p32->st_info = ELF32_ST_INFO( STB_WEAK, STT_IMPORT );
#else
        p32->st_info = ELF32_ST_INFO( STB_WEAK, STT_NOTYPE );
#endif
        p32->st_value = 0; /* is always 0 */
        p32->st_shndx = SHN_UNDEF;

        strsize += len + 1;
        DebugMsg(("set_symtab32, ALIAS: symbol %s, value=%X\n", buffer, p32->st_value));
        p32++;
    }
#endif

    /* 6. PUBLIC entries */
    for ( q = ModuleInfo.g.PubQueue.head; q; q = q->next ) {
        sym = q->sym;
        len = Mangle( sym, buffer );

        curr = (struct dsym *)sym->segment;
        if ( curr && curr->e.seginfo->segtype != SEGTYPE_CODE )
            stt = STT_OBJECT;
        else
            stt = STT_FUNC;

        p32->st_name = strsize;
        p32->st_info = ELF32_ST_INFO( STB_GLOBAL, stt );
        p32->st_value = sym->offset;
#if 1 /* v2.07: changed - to make MT_ABS obsolete */
        if ( sym->state == SYM_INTERNAL )
            if ( curr )
                p32->st_shndx = GetSegIdx( &curr->sym );
            else
                p32->st_shndx = SHN_ABS;
        else
            p32->st_shndx = SHN_UNDEF;
#else
        if ( sym->mem_type == MT_ABS )
            p32->st_shndx = SHN_ABS;
        else if ( curr )
            p32->st_shndx = GetSegIdx( &curr->sym );
        else
            p32->st_shndx = SHN_UNDEF;
#endif
        strsize += len + 1;

        DebugMsg(("set_symtab32, PUBLIC+LOCAL: symbol %s, value=%X\n", buffer, p32->st_value));

        p32++;
    }
#if ADDSTARTLABEL
    if ( ModuleInfo.g.start_label ) {
        len = Mangle( ModuleInfo.g.start_label, buffer );
        p32->st_name = strsize;
        p32->st_info = ELF32_ST_INFO( STB_ENTRY, STT_FUNC );
        p32->st_value = ModuleInfo.g.start_label->offset;
        p32->st_shndx = GetSegIdx( ModuleInfo.g.start_label->segment );
        strsize += len + 1;
        DebugMsg(("set_symtab32, ENTRY: symbol %s, value=%X\n", buffer, p32->st_value));
        p32++;
    }
#endif
    return( strsize );
}

#if AMD64_SUPPORT

/* fill entries in ELF64 symbol table:
 typedef struct {
    uint_32  st_name;        //  +0 symbol name index into string table
    uint_8   st_info;        //  +4 symbol's type and binding attribs.
    uint_8   st_other;       //  +5 no meaning yet.
    uint_16  st_shndx;       //  +6 section index
    uint_64  st_value;       //  +8 symbol "value"
    uint_64  st_size;        // +16 symbol size
} Elf64_Sym;
 */

static uint_32 set_symtab64( struct elfmod *em, uint_32 entries, struct localname *localshead )
/*********************************************************************************************/
{
    uint_32   strsize = 1;
    uint_32   len;
    uint_8    stt;
    struct dsym   *curr;
    struct asym   *sym;
    struct localname *localscurr;
    struct qnode  *q;
    Elf64_Sym *p64;
    char      buffer[MAX_ID_LEN + MANGLE_BYTES + 1];

    em->internal_segs[SYMTAB_IDX].size = entries * sizeof( Elf64_Sym );
    em->internal_segs[SYMTAB_IDX].data = LclAlloc( em->internal_segs[SYMTAB_IDX].size );
    memset( em->internal_segs[SYMTAB_IDX].data, 0, em->internal_segs[SYMTAB_IDX].size );

    p64 = (Elf64_Sym *)em->internal_segs[SYMTAB_IDX].data;

    p64++; /* skip NULL entry */

    /* 1. make file entry */
    p64->st_name = strsize;  /* symbol's name in string table */
    strsize += strlen( em->srcname ) + 1;
    p64->st_value = 0;
    p64->st_size = 0;
    p64->st_info = ELF64_ST_INFO( STB_LOCAL, STT_FILE ); /* symbol's type and binding info */
    p64->st_shndx = SHN_ABS; /* section index */
    p64++;

    /* 2. make section entries */
    for( curr = SymTables[TAB_SEG].head; curr; curr = curr->next ) {
        //p64->st_name = ?;  /* name isn't set */
        p64->st_info = ELF64_ST_INFO( STB_LOCAL, STT_SECTION );
        p64->st_shndx = GetSegIdx( curr->sym.segment );
        p64++;
    }

    /* 3. locals */

    for ( localscurr = localshead ; localscurr ; localscurr = localscurr->next ) {
        len = Mangle( localscurr->sym, buffer );
        p64->st_name = strsize;
        curr = (struct dsym *)localscurr->sym->segment;
        if ( curr && curr->e.seginfo->segtype != SEGTYPE_CODE )
            stt = STT_OBJECT;
        else
            stt = STT_FUNC;
        p64->st_info = ELF64_ST_INFO( STB_LOCAL, stt );
        p64->st_value = localscurr->sym->offset;
#if 1 /* v2.07: changed - to make MT_ABS obsolete */
        if ( curr )
            p64->st_shndx = GetSegIdx( &curr->sym );
        else
            p64->st_shndx = SHN_ABS;
#else
        if ( localscurr->sym->mem_type == MT_ABS )
            p64->st_shndx = SHN_ABS;
        else
            p64->st_shndx = GetSegIdx( &curr->sym );
#endif
        strsize += len + 1;
        DebugMsg(("set_symtab64, LOCAL: symbol %s, value=%" I64_SPEC "X\n", buffer, p64->st_value));
        p64++;
    }

    /* 4. 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 )
            continue;
        len = Mangle( &curr->sym, buffer );

        p64->st_name = strsize;

        /* for COMMUNALs, store their size in the Value field */
        if ( curr->sym.iscomm == TRUE ) {
            p64->st_info = ELF64_ST_INFO( STB_GLOBAL, STT_COMMON );
            p64->st_value = curr->sym.total_size;
            p64->st_shndx = SHN_COMMON;
        } else {
#if OWELFIMPORT
            p64->st_info = ( IsWeak( curr->sym ) ? ELF64_ST_INFO( STB_WEAK, STT_IMPORT ) : ELF64_ST_INFO( STB_GLOBAL, STT_IMPORT ) );
#else
            /* todo: set STT_FUNC for prototypes??? */
            p64->st_info = ( IsWeak( curr->sym ) ? ELF64_ST_INFO( STB_WEAK, STT_NOTYPE ) : ELF64_ST_INFO( STB_GLOBAL, STT_NOTYPE ) );
#endif
            p64->st_value = curr->sym.offset; /* is always 0 */
            p64->st_shndx = SHN_UNDEF;
        }

        strsize += len + 1;
        DebugMsg(("set_symtab64, EXTERNAL: symbol %s, info=%X, shndx=%X, value=%" I64_SPEC "X\n", buffer, p64->st_info, p64->st_shndx, p64->st_value));
        p64++;
    }

#if ELFALIAS
    /* 5. aliases */

    for( curr = SymTables[TAB_ALIAS].head ; curr != NULL ;curr = curr->next ) {
        len = Mangle( &curr->sym, buffer );

        p64->st_name = strsize;

#if OWELFIMPORT
        p64->st_info = ELF64_ST_INFO( STB_WEAK, STT_IMPORT );
#else
        p64->st_info = ELF64_ST_INFO( STB_WEAK, STT_NOTYPE );
#endif
        p64->st_value = 0; /* is always 0 */
        p64->st_shndx = SHN_UNDEF;

        strsize += len + 1;
        DebugMsg(("set_symtab64, ALIASES: symbol %s, value=%" I64_SPEC "X\n", buffer, p64->st_value));
        p64++;
    }
#endif

    /* 6. PUBLIC entries */
    for ( q = ModuleInfo.g.PubQueue.head; q; q = q->next ) {
        sym = q->sym;
        len = Mangle( sym, buffer );

        curr = (struct dsym *)sym->segment;
        if ( curr && curr->e.seginfo->segtype != SEGTYPE_CODE )
            stt = STT_OBJECT;
        else
            stt = STT_FUNC;

        p64->st_name = strsize;
        p64->st_info = ELF64_ST_INFO( STB_GLOBAL, stt );
        p64->st_value = sym->offset;
#if 1 /* v2.07: changed - to make MT_ABS obsolete */
        if ( sym->state == SYM_INTERNAL )
            if ( curr )
                p64->st_shndx = GetSegIdx( &curr->sym );
            else
                p64->st_shndx = SHN_ABS;
        else
            p64->st_shndx = SHN_UNDEF;
#else
        if ( sym->mem_type == MT_ABS )
            p64->st_shndx = SHN_ABS;
        else if ( curr )
            p64->st_shndx = GetSegIdx( &curr->sym );
        else
            p64->st_shndx = SHN_UNDEF;
#endif
        strsize += len + 1;

        DebugMsg(("set_symtab64, PUBLIC+LOCAL: symbol %s, info=%X, shndx=%X, value=%" I64_SPEC "X\n", buffer, p64->st_info, p64->st_shndx, p64->st_value));

        p64++;
    }
#if ADDSTARTLABEL
    if ( ModuleInfo.g.start_label ) {
        len = Mangle( ModuleInfo.g.start_label, buffer );
        p64->st_name = strsize;
        p64->st_info = ELF64_ST_INFO( STB_ENTRY, STT_FUNC );
        p64->st_value = ModuleInfo.g.start_label->offset;
        p64->st_shndx = GetSegIdx( ModuleInfo.g.start_label->segment );
        strsize += len + 1;
        DebugMsg(("set_symtab64, ENTRY: symbol %s, value=%" I64_SPEC "X\n", buffer, p64->st_value));
        p64++;
    }
#endif
    return( strsize );
}
#endif

/* calculate size of .symtab + .strtab section.
 * set content of these sections.
 */
static void set_symtab_values( struct elfmod *em )
/************************************************/
{
    uint_32 strsize;
    uint_32 entries;
    struct dsym *curr;
    //struct asym *sym;
    struct qnode *q;
    char *p2;
    struct {
        struct localname *head;
        struct localname *tail;
    } locals = { NULL, NULL };
    struct localname *localscurr;

    /* symbol table. there is
     - 1 NULL entry,
     - 1 entry for the module/file,
     - 1 entry for each section and
     - n entries for local symbols
     - m entries for global symbols
     */

    /* symbol table starts with 1 NULL entry + 1 file entry */
    em->symindex = 1 + 1;

    for( curr = SymTables[TAB_SEG].head; curr; curr = curr->next )
        curr->sym.ext_idx = em->symindex++;

    /* add local symbols to symbol table */

    for( curr = SymTables[TAB_SEG].head; curr; curr = curr->next ) {
        if ( curr->e.seginfo->num_relocs ) {
            struct fixup *fix = curr->e.seginfo->FixupList.head;
            for ( ; fix; fix = fix->nextrlc ) {
                /* if it's not EXTERNAL/PUBLIC, add symbol. */
                /* however, if it's an assembly time variable */
                /* use a raw section reference */
                if ( fix->sym->variable ) {
                    fix->sym = fix->segment_var;
                } else if ( ( fix->sym->state == SYM_INTERNAL ) &&
                    fix->sym->included == FALSE &&
                    fix->sym->ispublic == FALSE ) {
                    fix->sym->included = TRUE;
                    localscurr = LclAlloc( sizeof( struct localname ) );
                    localscurr->next = NULL;
                    localscurr->sym = fix->sym;
                    if (locals.tail) {
                        locals.tail->next = localscurr;
                        locals.tail = localscurr;
                    } else {
                        locals.head = locals.tail = localscurr;
                    }
                    fix->sym->ext_idx = em->symindex++;
                }
            }
        }
    }
    DebugMsg(("set_symtab_values: index after sections: %u\n", em->symindex));
    em->start_globals = em->symindex;

    /* count EXTERNs and used EXTERNDEFs (and PROTOs [since v2.01]) */
    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 = em->symindex++;
    }
    DebugMsg(("set_symtab_values: index after EXTERNALs: %u\n", em->symindex));

#if ELFALIAS
    /* count aliases */
    for( curr = SymTables[TAB_ALIAS].head ; curr != NULL ;curr = curr->next ) {
        curr->sym.idx = em->symindex++;
    }
    DebugMsg(("set_symtab_values: index after ALIASES: %u\n", em->symindex));
#endif

    /* count publics */
    for ( q = ModuleInfo.g.PubQueue.head; q; q = q->next ) {
        q->sym->ext_idx = em->symindex++;
    }
    DebugMsg(("set_symtab_values: index after PUBLICs: %u\n", em->symindex));

    /* size of symbol table is defined */
    entries = em->symindex;

#if ADDSTARTLABEL
    if ( ModuleInfo.g.start_label )
        entries++;
#endif

#if AMD64_SUPPORT
    if ( ModuleInfo.defOfssize == USE64 )
        strsize = set_symtab64( em, entries, locals.head );
    else
#endif
        strsize = set_symtab32( em, entries, locals.head );

    /* generate the string table */
    DebugMsg(("set_symtab_values: creating string table, size=%X\n", strsize));

    em->internal_segs[STRTAB_IDX].size = strsize;
    em->internal_segs[STRTAB_IDX].data = LclAlloc( strsize );
    memset( em->internal_segs[STRTAB_IDX].data, 0, strsize );
    p2 = em->internal_segs[STRTAB_IDX].data;
    *p2++ = NULLC;

    strcpy( p2, em->srcname );
    p2 += strlen( p2 ) + 1;

    for ( localscurr = locals.head ; localscurr ; localscurr = localscurr->next ) {
        p2 += Mangle( localscurr->sym, p2 ) + 1;
    }

    for( curr = SymTables[TAB_EXT].head ; curr != NULL ;curr = curr->next ) {
        if ( curr->sym.iscomm == FALSE && curr->sym.weak == TRUE )
            continue;
        p2 += Mangle( &curr->sym, p2 ) + 1;
    }

#if ELFALIAS
    for( curr = SymTables[TAB_ALIAS].head ; curr != NULL ;curr = curr->next ) {
        p2 += Mangle( &curr->sym, p2 ) + 1;
    }
#endif

    for ( q = ModuleInfo.g.PubQueue.head; q; q = q->next ) {
        p2 += Mangle( q->sym, p2 ) + 1;
    }
#if ADDSTARTLABEL
    if ( ModuleInfo.g.start_label ) {
        Mangle( ModuleInfo.g.start_label, p2 );
    }
#endif
    DebugMsg(("set_symtab_values: exit, symindex=%u\n", em->symindex ));
    return;
}

/* set content + size of .shstrtab section.
 * this section contains the names of all sections.
 * three groups of sections are handled:
 * - sections defined in the program
 * - ELF internal sections
 * - relocation sections
 * alloc .shstrtab
 */
static void set_shstrtab_values( struct elfmod *em )
/**************************************************/
{
    int         i;
    struct dsym *curr;
    char        *p;
    unsigned int size = 1; /* the first byte at offset 0 is the NULL section name */
    char buffer[MAX_ID_LEN+1];

    /* get size of section names defined in the program & relocation sections ) */
    for( curr = SymTables[TAB_SEG].head; curr; curr = curr->next ) {
        /* v2.07: ALIAS name defined? */
        p = ( curr->e.seginfo->aliasname ? curr->e.seginfo->aliasname : ElfConvertSectionName( &curr->sym, buffer ) );
        size += strlen( p ) + 1;
        if ( curr->e.seginfo->FixupList.head )
            size += strlen( p ) +
#if AMD64_SUPPORT
                (( ModuleInfo.defOfssize == USE64 ) ? sizeof(".rela") : sizeof(".rel"));
#else
                sizeof(".rel");
#endif
    }
    /* get internal section name sizes */
    for ( i = 0; i < NUM_INTSEGS; i++ ) {
        size += strlen( internal_segparms[i].name ) + 1;
    }

    em->internal_segs[SHSTRTAB_IDX].size = size;

    /* size is known, now alloc .shstrtab data buffer and fill it */

    em->internal_segs[SHSTRTAB_IDX].data = LclAlloc( size );
    p = (char *)em->internal_segs[SHSTRTAB_IDX].data;
    *p++ = NULLC; /* NULL section name */

    /* 1. names of program sections */
    for( curr = SymTables[TAB_SEG].head; curr; curr = curr->next ) {
        strcpy( p, curr->e.seginfo->aliasname ? curr->e.seginfo->aliasname : ElfConvertSectionName( &curr->sym, buffer ) );
        p += strlen( p ) + 1;
    }
    /* 2. names of internal sections */
    for ( i = 0; i < NUM_INTSEGS; i++ ) {
        strcpy( p, internal_segparms[i].name );
        p += strlen( p ) + 1;
    }
    /* 3. names of "relocation" sections */
    for( curr = SymTables[TAB_SEG].head; curr; curr = curr->next ) {
        if ( curr->e.seginfo->FixupList.head ) {
#if AMD64_SUPPORT
            strcpy( p, (( ModuleInfo.defOfssize == USE64 ) ? ".rela": ".rel") );
#else
            strcpy( p, ".rel" );
#endif
            p += strlen( p );
            strcpy( p, curr->e.seginfo->aliasname ? curr->e.seginfo->aliasname : ElfConvertSectionName( &curr->sym, buffer ) );
            p += strlen( p ) + 1;
        }
    }
    /**/myassert( size == p - (char *)em->internal_segs[SHSTRTAB_IDX].data );
    DebugMsg(("set_shstrtab_values: size=%X\n", size));
    return;
}

static unsigned int get_relocation_count( struct dsym *curr )
/***********************************************************/
{
    unsigned relocs;
    struct fixup *fix;

    for ( relocs = 0, fix = curr->e.seginfo->FixupList.head; fix ; fix = fix->nextrlc, relocs++ );

    return( relocs );
}

static unsigned int Get_Alignment( struct dsym *curr )
/****************************************************/
{
    if ( curr->e.seginfo->alignment == MAX_SEGALIGNMENT )
        return( 0 );
    return( 1 << curr->e.seginfo->alignment );
}

/* write ELF32 section table.
 * fileoffset: start of section data ( behind elf header and section table )
 * there are 3 groups of sections to handle:
 * - the sections defined in the module
 * - the internal ELF sections ( .shstrtab, .symtab, .strtab )
 * - the 'relocation' sections
 */

static int elf_write_section_table32( struct module_info *modinfo, struct elfmod *em, uint_32 fileoffset )
/********************************************************************************************************/
{
    int         i;
    struct dsym *curr;
    uint_8      *p;
    //uint_32     fileoffset;
    Elf32_Shdr  shdr32;

    DebugMsg(("elf_write_section_table32( fileofs=%X ): enter\n", fileoffset ));

    //fileoffset = sizeof(Elf64_Ehdr) + ehdr->e_shnum * ehdr->e_shentsize;
    fileoffset = (fileoffset + 0xF) & ~0xF;

    /* set contents and size of internal .shstrtab section */
    set_shstrtab_values( em );

    /* write the NULL entry */
    memset( &shdr32, 0, sizeof( shdr32) );
    if ( fwrite( &shdr32, 1, sizeof(shdr32), CurrFile[OBJ] ) != sizeof(shdr32) ) /* write the empty NULL entry */
        WriteError();

    /* use p to scan strings (=section names) of .shstrtab */
    p = (uint_8 *)em->internal_segs[SHSTRTAB_IDX].data;
    p++; /* skip 'name' of NULL entry */

    /* write the section headers defined in the module, */
    for( curr = SymTables[TAB_SEG].head; curr; curr = curr->next ) {

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

        shdr32.sh_name = p - (uint_8 *)em->internal_segs[SHSTRTAB_IDX].data;
        p += strlen( (char *)p ) + 1;
        if ( curr->e.seginfo->info == TRUE ) { /* v2.07:added; v2.12: highest priority */
            shdr32.sh_type = SHT_NOTE;
            shdr32.sh_flags = 0;
        } else {
            shdr32.sh_type = ( curr->e.seginfo->segtype != SEGTYPE_BSS ? SHT_PROGBITS : SHT_NOBITS );
            if ( curr->e.seginfo->segtype == SEGTYPE_CODE ) {
                shdr32.sh_flags = SHF_EXECINSTR | SHF_ALLOC;
            } else if ( curr->e.seginfo->readonly == TRUE ) {
                shdr32.sh_flags = SHF_ALLOC;
            } else if ( curr->e.seginfo->clsym && strcmp( curr->e.seginfo->clsym->name, "CONST" ) == 0 ) {
                shdr32.sh_flags = SHF_ALLOC; /* v2.07: added */
            } else {
                shdr32.sh_flags = SHF_WRITE | SHF_ALLOC;
            }
        }
#if 0
        /* todo: translate values in field <characteristics> to
         * elf section flags.
         */
        if ( curr->e.seginfo->characteristics == ??? ) {
        }
#endif
        shdr32.sh_addr = 0;
        /* v2.12: the sh_offset field holds the file position, even for SHT_NOBITS */
        //if ( shdr32.sh_type != SHT_NOBITS ) {
            shdr32.sh_offset = fileoffset; /* start of section in file */
            curr->e.seginfo->fileoffset = fileoffset; /* save the offset in the segment */
        //}
        /* v2.07: set size for all sections, including .bss */
        shdr32.sh_size = curr->sym.max_offset;
        shdr32.sh_link = 0;
        shdr32.sh_info = 0;
        shdr32.sh_addralign = Get_Alignment( curr );
        shdr32.sh_entsize = 0;

        if ( fwrite( &shdr32, 1, sizeof(shdr32), CurrFile[OBJ] ) != sizeof(shdr32) )
            WriteError();
        curr->e.seginfo->num_relocs = get_relocation_count( curr );

        /* v2.12: don't adjust fileoffset for SHT_NOBITS sections.
         * it didn't cause fatal damage previously, but made the
         * object module unnecessary large.
         */
        if ( shdr32.sh_type != SHT_NOBITS ) {
            fileoffset += shdr32.sh_size;
            fileoffset = (fileoffset + 0xF) & ~0xF;
        }

        DebugMsg(("elf_write_section_table32(%s): ofs=%X size=%X numrelocs=%u\n", curr->sym.name, shdr32.sh_offset, shdr32.sh_size, curr->e.seginfo->num_relocs));
    }

    /* set size and contents of .symtab and .strtab sections */
    set_symtab_values( em );

    /* write headers of internal sections */
    for ( i = 0; i < NUM_INTSEGS; i++ ) {
        shdr32.sh_name = p - (uint_8 *)em->internal_segs[SHSTRTAB_IDX].data;
        p += strlen( (char *)p ) + 1;
        shdr32.sh_type = internal_segparms[i].type;
        shdr32.sh_flags = 0;
        shdr32.sh_offset = fileoffset; /* start of section in file */
        em->internal_segs[i].fileoffset = fileoffset;
        shdr32.sh_size = em->internal_segs[i].size;
        /* section .symtab is special */
        if ( i == SYMTAB_IDX ) {
            shdr32.sh_link = 1 + modinfo->g.num_segs + STRTAB_IDX;
            shdr32.sh_info = em->start_globals;
            shdr32.sh_addralign = 4;
            shdr32.sh_entsize = sizeof( Elf32_Sym );
        } else {
            shdr32.sh_link = 0;
            shdr32.sh_info = 0;
            shdr32.sh_addralign = 1;
            shdr32.sh_entsize = 0;
        }
        if ( fwrite( &shdr32, 1, sizeof( shdr32 ), CurrFile[OBJ] ) != sizeof( shdr32 ) )
            WriteError();

        fileoffset += shdr32.sh_size;
        fileoffset = (fileoffset + 0xF) & ~0xF;
        DebugMsg(("elf_write_section_table32(%s): ofs=%X size=%X\n", internal_segparms[i].name, shdr32.sh_offset, shdr32.sh_size));
    }

    /* write headers of reloc sections */
    for( curr = SymTables[TAB_SEG].head; curr; curr = curr->next ) {
        if ( curr->e.seginfo->FixupList.head == NULL )
            continue;

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

        shdr32.sh_name = p - (uint_8 *)em->internal_segs[SHSTRTAB_IDX].data;
        p += strlen( (char *)p ) + 1;
        shdr32.sh_type = SHT_REL;
        shdr32.sh_flags = 0;
        shdr32.sh_addr = 0;
        shdr32.sh_offset = fileoffset; /* start of section in file */
        /* save the file offset in the slot reserved for ELF relocs */
        curr->e.seginfo->reloc_offset = fileoffset;
        /* size of section in file */
        shdr32.sh_size = curr->e.seginfo->num_relocs * sizeof( Elf32_Rel );
        shdr32.sh_link = 1 + modinfo->g.num_segs + SYMTAB_IDX;
        /* set info to the src section index */
        shdr32.sh_info = GetSegIdx( curr->sym.segment );
        shdr32.sh_addralign = 4;
        shdr32.sh_entsize = sizeof( Elf32_Rel );

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

        fileoffset += shdr32.sh_size;
        fileoffset = (fileoffset + 0xF) & ~0xF;
        DebugMsg(("elf_write_section_table32(%s): relocs, ofs=%X size=%X\n", curr->sym.name, shdr32.sh_offset, shdr32.sh_size));

    }
    DebugMsg(("elf_write_section_table32: exit, final fileofs=%X\n", fileoffset ));
    return( NOT_ERROR );
}

#if AMD64_SUPPORT

/* write ELF64 section table. */

static int elf_write_section_table64( struct module_info *modinfo, struct elfmod *em, uint_32 fileoffset )
/********************************************************************************************************/
{
    int         i;
    struct dsym *curr;
    uint_8      *p;
    Elf64_Shdr  shdr64;

    DebugMsg(("elf_write_section_table64( fileofs=%X ): enter\n", fileoffset ));

    fileoffset = (fileoffset + 0xF) & ~0xF;

    /* set contents and size of internal .shstrtab section */
    set_shstrtab_values( em );

    /* write the NULL entry */
    memset( &shdr64, 0, sizeof( shdr64) );
    if ( fwrite( &shdr64, 1, sizeof(shdr64), CurrFile[OBJ] ) != sizeof(shdr64) ) /* write the empty NULL entry */
        WriteError();

    /* use p to scan strings (=section names) of .shstrtab */
    p = (uint_8 *)em->internal_segs[SHSTRTAB_IDX].data;
    p++; /* skip 'name' of NULL entry */

    /* write the section headers defined in the module */
    for( curr = SymTables[TAB_SEG].head; curr; curr = curr->next ) {

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

        shdr64.sh_name = p - (uint_8 *)em->internal_segs[SHSTRTAB_IDX].data;
        p += strlen( (char *)p ) + 1;
        if ( curr->e.seginfo->info == TRUE ) { /* v2.07:added; v2.12: highest priority */
            shdr64.sh_type = SHT_NOTE;
            shdr64.sh_flags = 0;
        } else {
            shdr64.sh_type = ( curr->e.seginfo->segtype != SEGTYPE_BSS ? SHT_PROGBITS : SHT_NOBITS );
            if ( curr->e.seginfo->segtype == SEGTYPE_CODE ) {
                shdr64.sh_flags = SHF_EXECINSTR | SHF_ALLOC;
            } else if ( curr->e.seginfo->readonly == TRUE ) {
                shdr64.sh_flags = SHF_ALLOC;
            } else if ( curr->e.seginfo->clsym && strcmp( curr->e.seginfo->clsym->name, "CONST" ) == 0 ) {
                shdr64.sh_flags = SHF_ALLOC; /* v2.07: added */
            } else {
                shdr64.sh_flags = SHF_WRITE | SHF_ALLOC;
            }
        }
#if 0
        /* todo: translate values in field <characteristics> to
         * elf section flags.
         */
        if ( curr->e.seginfo->characteristics == ??? ) {
        }
#endif
        shdr64.sh_addr = 0;
        /* v2.12: the sh_offset field holds the file position, even for SHT_NOBITS */
        //if ( shdr64.sh_type != SHT_NOBITS ) {
            shdr64.sh_offset = fileoffset; /* start of section in file */
            curr->e.seginfo->fileoffset = fileoffset; /* save the offset in the segment */
        //}
        /* v2.07: set size for all sections, including .bss */
        shdr64.sh_size = curr->sym.max_offset;
        shdr64.sh_link = 0;
        shdr64.sh_info = 0;
        shdr64.sh_addralign = Get_Alignment( curr );
        shdr64.sh_entsize = 0;

        if ( fwrite( &shdr64, 1, sizeof(shdr64), CurrFile[OBJ] ) != sizeof(shdr64) )
            WriteError();
        curr->e.seginfo->num_relocs = get_relocation_count( curr );

        /* v2.12: don't adjust fileoffset for SHT_NOBITS sections */
        if ( shdr64.sh_type != SHT_NOBITS ) {
            fileoffset += shdr64.sh_size;
            fileoffset = (fileoffset + 0xF) & ~0xF;
        }

        DebugMsg(("elf_write_section_table64(%s): ofs=%" I64_SPEC "X size=%" I64_SPEC "X numrelocs=%u\n", curr->sym.name, shdr64.sh_offset, shdr64.sh_size, curr->e.seginfo->num_relocs));
    }

    /* set size and contents of .symtab and .strtab sections */
    set_symtab_values( em );

    /* write headers of internal sections */
    for ( i = 0; i < NUM_INTSEGS; i++ ) {

        shdr64.sh_name = p - (uint_8 *)em->internal_segs[SHSTRTAB_IDX].data;
        p += strlen( (char *)p ) + 1;
        shdr64.sh_type = internal_segparms[i].type;
        shdr64.sh_flags = 0;
        shdr64.sh_offset = fileoffset; /* start of section in file */
        em->internal_segs[i].fileoffset = fileoffset;
        shdr64.sh_size = em->internal_segs[i].size;
        /* section .symtab is special */
        if ( i == SYMTAB_IDX ) {
            shdr64.sh_link = 1 + modinfo->g.num_segs + STRTAB_IDX;
            shdr64.sh_info = em->start_globals;
            shdr64.sh_addralign = 4;
            shdr64.sh_entsize = sizeof( Elf64_Sym );
        } else {
            shdr64.sh_link = 0;
            shdr64.sh_info = 0;
            shdr64.sh_addralign = 1;
            shdr64.sh_entsize = 0;
        }
        if ( fwrite( &shdr64, 1, sizeof( shdr64 ), CurrFile[OBJ] ) != sizeof( shdr64 ) )
            WriteError();

        fileoffset += shdr64.sh_size;
        fileoffset = (fileoffset + 0xF) & ~0xF;
        DebugMsg(("elf_write_section_table64(%s): ofs=%" I64_SPEC "X size=%" I64_SPEC "X\n", internal_segparms[i].name, shdr64.sh_offset, shdr64.sh_size));
    }

    /* write headers of reloc sections */
    for( curr = SymTables[TAB_SEG].head; curr; curr = curr->next ) {
        if ( curr->e.seginfo->FixupList.head == NULL )
            continue;

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

        shdr64.sh_name = p - (uint_8 *)em->internal_segs[SHSTRTAB_IDX].data;
        p += strlen( (char *)p ) + 1;
        shdr64.sh_type = SHT_RELA; /* v2.05: changed REL to RELA */
        shdr64.sh_flags = 0;
        shdr64.sh_addr = 0;
        shdr64.sh_offset = fileoffset; /* start of section in file */
        /* save the file offset in the slot reserved for ELF relocs */
        curr->e.seginfo->reloc_offset = fileoffset;
        /* size of section in file */
        shdr64.sh_size = curr->e.seginfo->num_relocs * sizeof( Elf64_Rela );
        shdr64.sh_link = 1 + modinfo->g.num_segs + SYMTAB_IDX;
        /* set info to the src section index */
        shdr64.sh_info = GetSegIdx( curr->sym.segment );
        shdr64.sh_addralign = 4;
        shdr64.sh_entsize = sizeof( Elf64_Rela );

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

        fileoffset += shdr64.sh_size;
        fileoffset = (fileoffset + 0xF) & ~0xF;
        DebugMsg(("elf_write_section_table64(%s): relocs, ofs=%" I64_SPEC "X size=%" I64_SPEC "X\n", curr->sym.name, shdr64.sh_offset, shdr64.sh_size));
    }
    DebugMsg(("elf_write_section_table64: exit, final fileofs=%X\n", fileoffset ));
    return( NOT_ERROR );
}
#endif

/* write 1 section's relocations (32-bit) */

static void write_relocs32( struct elfmod *em, struct dsym *curr )
/****************************************************************/
{
    uint_8 elftype;
    struct fixup *fixup;
    Elf32_Rel reloc32;

    DebugMsg(("write_relocs32: enter\n"));
    for ( fixup = curr->e.seginfo->FixupList.head; fixup; fixup = fixup->nextrlc ) {
        reloc32.r_offset = fixup->locofs;
        switch ( fixup->type ) {
        case FIX_OFF32:         elftype = R_386_32;             break;
        case FIX_RELOFF32:      elftype = R_386_PC32;           break;
        //case FIX_???:         elftype = R_386_GOT32;          break;
        //case FIX_???:         elftype = R_386_PLT32;          break;
        //case FIX_???:         elftype = R_386_COPY;           break;
        //case FIX_???:         elftype = R_386_GLOB_DAT;       break;
        //case FIX_???:         elftype = R_386_JMP_SLOT;       break;
        case FIX_OFF32_IMGREL:  elftype = R_386_RELATIVE;       break;
        //case FIX_???:         elftype = R_386_GOTOFF;         break;
        //case FIX_???:         elftype = R_386_GOTPC;          break;
#if GNURELOCS
        case FIX_OFF16:    em->extused = TRUE; elftype = R_386_16;   break;
        case FIX_RELOFF16: em->extused = TRUE; elftype = R_386_PC16; break;
        case FIX_OFF8:     em->extused = TRUE; elftype = R_386_8;    break;
        case FIX_RELOFF8:  em->extused = TRUE; elftype = R_386_PC8;  break;
#endif
        default:
            DebugMsg(("write_relocs32(): unhandled reloc loc=%X type=%u idx=%u sym=%s\n",
                      fixup->locofs, fixup->type, fixup->sym->ext_idx, fixup->sym->name));
            elftype = R_386_NONE;
            if ( fixup->type < FIX_LAST ) {
                EmitErr( INVALID_FIXUP_TYPE, ModuleInfo.fmtopt->formatname, fixup->type, curr->sym.name, fixup->locofs );
            } else
                EmitErr( UNKNOWN_FIXUP_TYPE, fixup->type, curr->sym.name, fixup->locofs );
        }
        /* the low 8 bits of info are type */
        /* the high 24 bits are symbol table index */
        reloc32.r_info = ELF32_R_INFO( fixup->sym->ext_idx, elftype );
        if ( fwrite( &reloc32, 1, sizeof(reloc32), CurrFile[OBJ] ) != sizeof(reloc32) )
            WriteError();
    }
    DebugMsg(("write_relocs32: exit\n"));
    return;
}

#if AMD64_SUPPORT

/* write 1 section's relocations (64-bit) */

static void write_relocs64( struct dsym *curr )
/*********************************************/
{
    uint_8 elftype;
    struct fixup *fixup;
    Elf64_Rela reloc64; /* v2.05: changed to Rela */

    DebugMsg(("write_relocs64: enter\n"));
    for ( fixup = curr->e.seginfo->FixupList.head; fixup; fixup = fixup->nextrlc ) {
        unsigned symidx = fixup->sym->ext_idx;
        reloc64.r_offset = fixup->locofs;
        /* v2.07: addend wasn't handled correctly.
         * Also note the type cast for fixup.offset -
         * r_addend has type int_64, while fixup.offset has type uint_32!
         */
        //reloc64.r_addend = fixup->offset;
        /* the following line depends on what's done in store_fixup().
         * if the inline addend is set to 0 there, the fixup->offset
         * must be used in the calculation ( it's 32-bit only!!! ).
         */
        //reloc64.r_addend = (int_32)fixup->offset - fixup->addbytes;
        /*
         * if the inline addend is not touched in store_fixup(),
         * we just have to use the addbytes field.
         */
        reloc64.r_addend = - fixup->addbytes;
        DebugMsg(("write_relocs64(): reloc loc=%X type=%u idx=%u sym=%s ofs=%X addbyt=%u\n",
                  fixup->locofs, fixup->type, fixup->sym->ext_idx, fixup->sym->name, fixup->offset, fixup->addbytes ));
        switch ( fixup->type ) {
        case FIX_RELOFF32:
#if 0  /* v2.07: activate if the section's index is to be used as symtab ref */
            if ( fixup->sym->segment != &curr->sym ) {
                //printf("PC-relative fixup to another section: %s\n", fixup->sym->name );
                reloc64.r_addend += fixup->sym->offset;
                symidx = fixup->sym->segment->ext_idx;
            }
#endif
            reloc64.r_addend += *(int_32*)(curr->e.seginfo->CodeBuffer + fixup->locofs);
            elftype = R_X86_64_PC32;
            break;
        case FIX_OFF64:        elftype = R_X86_64_64;          break;
        //case FIX_???:        elftype = R_X86_64_GOT32;       break;
        //case FIX_???:        elftype = R_X86_64_PLT32;       break;
        //case FIX_???:        elftype = R_X86_64_COPY;        break;
        case FIX_GOT64:        elftype = R_X86_64_GLOB_DAT;    break; /* UASM 2.47 added these plt/got types + fixup.h changes in prep for plt/got support */
        case FIX_PLT64:		   elftype = R_X86_64_JUMP_SLOT;   break;
        case FIX_OFF32_IMGREL: elftype = R_X86_64_RELATIVE;    break;
        case FIX_GOTPCREL64:   elftype = R_X86_64_GOTPCREL;    break;
        case FIX_OFF32:        elftype = R_X86_64_32;          break;
        //case FIX_???:        elftype = R_X86_64_32S;         break;
        case FIX_OFF16:        elftype = R_X86_64_16;          break;
        case FIX_RELOFF16:     elftype = R_X86_64_PC16;        break;
        case FIX_OFF8:         elftype = R_X86_64_8;           break;
        case FIX_RELOFF8:      elftype = R_X86_64_PC8;         break;
        //case FIX_???:        elftype = R_X86_64_DPTMOD64;    break;
        //case FIX_???:        elftype = R_X86_64_DPTOFF64;    break;
        //case FIX_???:        elftype = R_X86_64_TPOFF64;     break;
        //case FIX_???:        elftype = R_X86_64_TLSGD;       break;
        //case FIX_???:        elftype = R_X86_64_TLSLD;       break;
        //case FIX_???:        elftype = R_X86_64_DPTOFF32;    break;
        //case FIX_???:        elftype = R_X86_64_GOTTPOFF;    break;
        //case FIX_???:        elftype = R_X86_64_TPOFF32;     break;
        //case FIX_???:        elftype = R_X86_64_PC64;        break;
        //case FIX_???:        elftype = R_X86_64_GOTOFF64;    break;
        //case FIX_???:        elftype = R_X86_64_GOTPC32;     break;
        //case FIX_???:        elftype = R_X86_64_SIZE32;      break;
        //case FIX_???:        elftype = R_X86_64_SIZE64;      break;
        default:
            DebugMsg(("write_relocs64(): unhandled reloc loc=%X type=%u idx=%u sym=%s\n",
                      fixup->locofs, fixup->type, fixup->sym->ext_idx, fixup->sym->name));
            elftype = R_X86_64_NONE;
            if ( fixup->type < FIX_LAST ) {
                EmitErr( INVALID_FIXUP_TYPE, ModuleInfo.fmtopt->formatname, fixup->type, curr->sym.name, fixup->locofs );
            } else
                EmitErr( UNKNOWN_FIXUP_TYPE, fixup->type, curr->sym.name, fixup->locofs );
        }
        /* the low 8 bits of info are type */
        /* the high 24 bits are symbol table index */
        reloc64.r_info = ELF64_R_INFO( symidx, elftype );
        if ( fwrite( &reloc64, 1, sizeof( reloc64 ), CurrFile[OBJ] ) != sizeof(reloc64) )
            WriteError();
    }
    DebugMsg(("write_relocs64: exit\n"));
    return;
}
#endif

/* write section contents and fixups */

static ret_code elf_write_data( struct module_info *modinfo, struct elfmod *em )
/******************************************************************************/
{
    struct dsym *curr;
    uint_32     size;
    int         i;

    for( curr = SymTables[TAB_SEG].head; curr; curr = curr->next ) {
        size = curr->sym.max_offset - curr->e.seginfo->start_loc;
        if ( curr->e.seginfo->segtype != SEGTYPE_BSS && size != 0 ) {
            fseek( CurrFile[OBJ], curr->e.seginfo->fileoffset + curr->e.seginfo->start_loc, SEEK_SET );
            /*myassert( curr->e.seginfo->CodeBuffer );*/
            if ( fwrite( curr->e.seginfo->CodeBuffer, 1, size, CurrFile[OBJ] ) != size )
                WriteError();
        }
    }

    /* write internal sections */
    for ( i = 0; i < NUM_INTSEGS; i++ ) {
        if ( em->internal_segs[i].data ) {
            fseek( CurrFile[OBJ], em->internal_segs[i].fileoffset, SEEK_SET );
            if ( fwrite( em->internal_segs[i].data, 1, em->internal_segs[i].size, CurrFile[OBJ] ) != em->internal_segs[i].size )
                WriteError();
        }
    }

    /* write reloc sections content */
    for( curr = SymTables[TAB_SEG].head; curr; curr = curr->next ) {
        if ( curr->e.seginfo->num_relocs ) {
            fseek( CurrFile[OBJ], curr->e.seginfo->reloc_offset, SEEK_SET );
#if AMD64_SUPPORT
            if ( modinfo->defOfssize == USE64 )
                write_relocs64( curr );
            else
#endif
                write_relocs32( em, curr );
        }
    }
#if GNURELOCS
    if ( em->extused ) {
        EmitWarn( 2, ELF_GNU_EXTENSIONS_USED );
    }
#endif

    return( NOT_ERROR );
}

/* write ELF module */

static ret_code elf_write_module( struct module_info *modinfo )
/*************************************************************/
{
    struct elfmod em;

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

    memset( &em, 0, sizeof( em ) );
    em.srcname = CurrFName[ASM];
#if 1
    /* the path part is stripped. todo: check if this is ok to do */
    em.srcname += strlen( em.srcname );
    while ( em.srcname > CurrFName[ASM] &&
           *(em.srcname-1) != '/' &&
           *(em.srcname-1) != '\\') em.srcname--;
#endif
    /* position at 0 ( probably unnecessary, since there were no writes yet ) */
    fseek( CurrFile[OBJ], 0, SEEK_SET );

    switch ( modinfo->defOfssize ) {
#if AMD64_SUPPORT
    case USE64:
        memcpy( &em.ehdr64.e_ident, ELF_SIGNATURE, ELF_SIGNATURE_LEN );
        em.ehdr64.e_ident[EI_CLASS] = ELFCLASS64;
        em.ehdr64.e_ident[EI_DATA] = ELFDATA2LSB;
        em.ehdr64.e_ident[EI_VERSION] = EV_CURRENT;
        em.ehdr64.e_ident[EI_OSABI] = modinfo->elf_osabi;
        /* v2.07: set abiversion to 0 */
        //ehdr64.e_ident[EI_ABIVERSION] = EV_CURRENT;
        em.ehdr64.e_ident[EI_ABIVERSION] = 0;
        em.ehdr64.e_type = ET_REL; /* file type */
        em.ehdr64.e_machine = EM_X86_64;
        em.ehdr64.e_version = EV_CURRENT;
        em.ehdr64.e_entry = 0; /* no entry for relocatable objects */
        em.ehdr64.e_phoff = 0; /* no progheaders for relocatable objects */
        em.ehdr64.e_shoff = sizeof( em.ehdr64 );
        em.ehdr64.e_flags = 0;
        em.ehdr64.e_ehsize = sizeof( em.ehdr64 );
        em.ehdr64.e_phentsize = 0; /* no progheaders for relocatable objects */
        em.ehdr64.e_phnum = 0;
        em.ehdr64.e_shentsize = sizeof( Elf64_Shdr );
        /* calculate # of sections. Add the following internal sections:
         - 1 NULL entry
         - 1 .shstrtab
         - 1 .symtab
         - 1 .strtab
         - n .rela<xxx> sections
         */
        em.ehdr64.e_shnum = 1 + modinfo->g.num_segs + 3 + get_num_reloc_sections();
        em.ehdr64.e_shstrndx = 1 + modinfo->g.num_segs + SHSTRTAB_IDX; /* set index of .shstrtab section */
        if ( fwrite( &em.ehdr64, 1, sizeof( em.ehdr64 ), CurrFile[OBJ] ) != sizeof( em.ehdr64 ) )
            WriteError();
        elf_write_section_table64( modinfo, &em,
                                  sizeof( Elf64_Ehdr ) + em.ehdr64.e_shnum * em.ehdr64.e_shentsize );
        break;
#endif
    default:
        memcpy( &em.ehdr32.e_ident, ELF_SIGNATURE, ELF_SIGNATURE_LEN );
        em.ehdr32.e_ident[EI_CLASS] = ELFCLASS32;
        em.ehdr32.e_ident[EI_DATA] = ELFDATA2LSB;
        em.ehdr32.e_ident[EI_VERSION] = EV_CURRENT;
        em.ehdr32.e_ident[EI_OSABI] = modinfo->elf_osabi;
        /* v2.07: set abiversion to 0 */
        //ehdr32.e_ident[EI_ABIVERSION] = EV_CURRENT;
        em.ehdr32.e_ident[EI_ABIVERSION] = 0;
        em.ehdr32.e_type = ET_REL; /* file type */
        em.ehdr32.e_machine = EM_386;
        em.ehdr32.e_version = EV_CURRENT;
        em.ehdr32.e_entry = 0; /* no entry for relocatable objects */
        em.ehdr32.e_phoff = 0; /* no progheaders for relocatable objects */
        em.ehdr32.e_shoff = sizeof( em.ehdr32 );
        em.ehdr32.e_flags = 0;
        em.ehdr32.e_ehsize = sizeof( em.ehdr32 );
        em.ehdr32.e_phentsize = 0; /* no progheaders for relocatable objects */
        em.ehdr32.e_phnum = 0;
        em.ehdr32.e_shentsize = sizeof( Elf32_Shdr );
        /* calculate # of sections. Add the following internal sections:
         - 1 NULL entry
         - 1 .shstrtab
         - 1 .symtab
         - 1 .strtab
         - n .rel<xxx> entries
         */
        em.ehdr32.e_shnum = 1 + modinfo->g.num_segs + 3 + get_num_reloc_sections();
        em.ehdr32.e_shstrndx = 1 + modinfo->g.num_segs + SHSTRTAB_IDX; /* set index of .shstrtab section */
        if ( fwrite( &em.ehdr32, 1, sizeof( em.ehdr32 ), CurrFile[OBJ] ) != sizeof( em.ehdr32 ) )
            WriteError();
        elf_write_section_table32( modinfo, &em,
                                  sizeof( Elf32_Ehdr ) + em.ehdr32.e_shnum * em.ehdr32.e_shentsize );
    };
    elf_write_data( modinfo, &em );
    DebugMsg(("elf_write_module: exit\n"));
    return( NOT_ERROR );
}

/* format-specific init.
 * called once per module.
 */

void elf_init( struct module_info *modinfo )
/******************************************/
{
    modinfo->elf_osabi = ELFOSABI_NONE;
    modinfo->g.WriteModule = elf_write_module;
#if 0
    printf("size Elf_Ehdr: %2u - %2u\n", sizeof(Elf32_Ehdr), sizeof(Elf64_Ehdr));
    printf("size Elf_Shdr: %2u - %2u\n", sizeof(Elf32_Shdr), sizeof(Elf64_Shdr));
    printf("size Elf_Sym : %2u - %2u\n", sizeof(Elf32_Sym ), sizeof(Elf64_Sym ));
    printf("size Elf_Rel : %2u - %2u\n", sizeof(Elf32_Rel ), sizeof(Elf64_Rel ));
    printf("size Elf_Rela: %2u - %2u\n", sizeof(Elf32_Rela), sizeof(Elf64_Rela));
#endif
    return;
}

#endif
