// Copyright (C) ?-1998 by Symantec
// Copyright (C) 2000-2010 by Digital Mars
// All Rights Reserved
// http://www.digitalmars.com
/*
 * This source file is made available for personal use
 * only. The license is in /dmd/src/dmd/backendlicense.txt
 * For any other uses, please contact Digital Mars.
 */


// Output to ELF object files

#if SCPP || MARS
#include        <stdio.h>
#include        <string.h>
#include        <stdlib.h>

#if __sun&&__SVR4
#include        <alloca.h>
#endif

#include        "cc.h"
#include        "global.h"
#include        "code.h"
#include        "type.h"
#include        "melf.h"
#include        "outbuf.h"
#include        "filespec.h"
#include        "cv4.h"
#include        "cgcv.h"
#include        "dt.h"

#include        "aa.h"
#include        "tinfo.h"

#if ELFOBJ

#include        "dwarf.h"

//#define DEBSYM 0x7E

static Outbuffer *fobjbuf;

regm_t BYTEREGS = BYTEREGS_INIT;
regm_t ALLREGS = ALLREGS_INIT;

static char __file__[] = __FILE__;      // for tassert.h
#include        "tassert.h"

#define MATCH_SECTION 1

#define DEST_LEN (IDMAX + IDOHD + 1)
char *obj_mangle2(Symbol *s,char *dest);

#if MARS
// C++ name mangling is handled by front end
#define cpp_mangle(s) ((s)->Sident)
#endif

/***************************************************
 * Correspondence of relocation types
 *      386             32 bit in 64      64 in 64
 *      RI_TYPE_SYM32   R_X86_64_32       R_X86_64_64
 *      RI_TYPE_GOTOFF  R_X86_64_PC32     R_X86_64_
 *      RI_TYPE_GOTPC   R_X86_64_         R_X86_64_
 *      RI_TYPE_GOT32   R_X86_64_         R_X86_64_
 *      RI_TYPE_TLS_GD  R_X86_64_TLSGD    R_X86_64_
 *      RI_TYPE_TLS_IE  R_X86_64_GOTTPOFF R_X86_64_
 *      RI_TYPE_TLS_LE  R_X86_64_TPOFF32  R_X86_64_
 *      RI_TYPE_PLT32   R_X86_64_PLT32    R_X86_64_
 *      RI_TYPE_PC32    R_X86_64_PC32     R_X86_64_
 */

/******************************************
 */

symbol *GOTsym; // global offset table reference

symbol *elfobj_getGOTsym()
{
    if (!GOTsym)
    {
        GOTsym = symbol_name("_GLOBAL_OFFSET_TABLE_",SCglobal,tspvoid);
    }
    return GOTsym;
}

void elfobj_refGOTsym()
{
    if (!GOTsym)
    {
        symbol *s = elfobj_getGOTsym();
        objextern(s);
    }
}

static void objfile_write(FILE *fd, void *buffer, unsigned len);

STATIC char * objmodtoseg (const char *modname);
STATIC void obj_browse_flush();
STATIC void objfixupp (struct FIXUP *);
STATIC void ledata_new (int seg,targ_size_t offset);
void obj_tlssections();

static IDXSYM elf_addsym(IDXSTR sym, targ_size_t val, unsigned sz,
                        unsigned typ,unsigned bind,IDXSEC sec);
static long elf_align(FILE *fd, targ_size_t size, long offset);

// The object file is built is several separate pieces

// Non-repeatable section types have single output buffers
//      Pre-allocated buffers are defined for:
//              Section Names string table
//              Section Headers table
//              Symbol table
//              String table
//              Notes section
//              Comment data

// Section Names  - String table for section names only
static Outbuffer *section_names;
#define SEC_NAMES_INIT  800
#define SEC_NAMES_INC   400

// String Table  - String table for all other names
static Outbuffer *symtab_strings;


// Section Headers
Outbuffer  *SECbuf;             // Buffer to build section table in
#define SecHdrTab ((Elf32_Shdr *)SECbuf->buf)
#define GET_SECTION(secidx) (SecHdrTab + secidx)
#define GET_SECTION_NAME(secidx) (section_names->buf + SecHdrTab[secidx].sh_name)

// The relocation for text and data seems to get lost.
// Try matching the order gcc output them
// This means defining the sections and then removing them if they are
// not used.
static int section_cnt; // Number of sections in table

#define SHI_TEXT        1
#define SHI_RELTEXT     2
#define SHI_DATA        3
#define SHI_RELDATA     4
#define SHI_BSS         5
#define SHI_RODAT       6
#define SHI_STRINGS     7
#define SHI_SYMTAB      8
#define SHI_SECNAMES    9
#define SHI_COM         10
#define SHI_NOTE        11

IDXSYM *mapsec2sym;
#define S2S_INC 20

#define SymbolTable   ((Elf32_Sym *)SYMbuf->buf)
#define SymbolTable64 ((Elf64_Sym *)SYMbuf->buf)
static int symbol_idx;          // Number of symbols in symbol table
static int local_cnt;           // Number of symbols with STB_LOCAL

#define STI_FILE 1              // Where file symbol table entry is
#define STI_TEXT 2
#define STI_DATA 3
#define STI_BSS  4
#define STI_GCC  5              // Where "gcc2_compiled" symbol is */
#define STI_RODAT 6             // Symbol for readonly data
#define STI_NOTE 7              // Where note symbol table entry is
#define STI_COM  8

// NOTE: There seems to be a requirement that the read-only data have the
// same symbol table index and section index. Use section NOTE as a place
// holder. When a read-only string section is required, swap to NOTE.

// Symbol Table
Outbuffer  *SYMbuf;             // Buffer to build symbol table in

// Notes data (note currently used)
static Outbuffer *note_data;
static IDXSEC secidx_note;      // Final table index for note data

// Comment data for compiler version
static Outbuffer *comment_data;
static const char compiler[] = "\0Digital Mars C/C++"
        VERSION
        ;       // compiled by ...

// Each compiler segment is an elf section
// Predefined compiler segments CODE,DATA,CDATA,UDATA map to indexes
//      into SegData[]
//      An additionl index is reserved for comment data
//      New compiler segments are added to end.
//
// There doesn't seem to be any way to get reserved data space in the
//      same section as initialized data or code, so section offsets should
//      be continuous when adding data. Fix-ups anywhere withing existing data.

#define COMD UDATA+1
#define OB_SEG_SIZ      10              // initial number of segments supported
#define OB_SEG_INC      10              // increment for additional segments

#define OB_CODE_STR     100000          // initial size for code
#define OB_CODE_INC     100000          // increment for additional code
#define OB_DATA_STR     100000          // initial size for data
#define OB_DATA_INC     100000          // increment for additional data
#define OB_CDATA_STR      1024          // initial size for data
#define OB_CDATA_INC      1024          // increment for additional data
#define OB_COMD_STR        256          // initial size for comments
                                        // increment as needed
#define OB_XTRA_STR        250          // initial size for extra segments
#define OB_XTRA_INC      10000          // increment size

#define MAP_SEG2SECIDX(seg) (SegData[seg]->SDshtidx)
#define MAP_SEG2SYMIDX(seg) (SegData[seg]->SDsymidx)
#define MAP_SEG2SEC(seg) (&SecHdrTab[MAP_SEG2SECIDX(seg)])
#define MAP_SEG2TYP(seg) (MAP_SEG2SEC(seg)->sh_flags & SHF_EXECINSTR ? CODE : DATA)

seg_data **SegData;
int seg_count;
int seg_max;
int seg_tlsseg = UNKNOWN;
int seg_tlsseg_bss = UNKNOWN;

int elf_getsegment2(IDXSEC shtidx, IDXSYM symidx, IDXSEC relidx);


/*******************************
 * Output a string into a string table
 * Input:
 *      strtab  =       string table for entry
 *      str     =       string to add
 *
 * Returns index into the specified string table.
 */

IDXSTR elf_addstr(Outbuffer *strtab, const char *str)
{
    //dbg_printf("elf_addstr(strtab = x%x str = '%s')\n",strtab,str);
    IDXSTR idx = strtab->size();        // remember starting offset
    strtab->writeString(str);
    //dbg_printf("\tidx %d, new size %d\n",idx,strtab->size());
    return idx;
}

/*******************************
 * Find a string in a string table
 * Input:
 *      strtab  =       string table for entry
 *      str     =       string to find
 *
 * Returns index into the specified string table or 0.
 */

static IDXSTR elf_findstr(Outbuffer *strtab, const char *str, const char *suffix)
{
    const char *ent = (char *)strtab->buf+1;
    const char *pend = ent+strtab->size() - 1;
    const char *s = str;
    const char *sx = suffix;
    int len = strlen(str);

    if (suffix)
        len += strlen(suffix);

    while(ent < pend)
    {
        if(*ent == 0)                   // end of table entry
        {
            if(*s == 0 && !sx)          // end of string - found a match
            {
                return ent - (const char *)strtab->buf - len;
            }
            else                        // table entry too short
            {
                s = str;                // back to beginning of string
                sx = suffix;
                ent++;                  // start of next table entry
            }
        }
        else if (*s == 0 && sx && *sx == *ent)
        {                               // matched first string
            s = sx+1;                   // switch to suffix
            ent++;
            sx = NULL;
        }
        else                            // continue comparing
        {
            if (*ent == *s)
            {                           // Have a match going
                ent++;
                s++;
            }
            else                        // no match
            {
                while(*ent != 0)        // skip to end of entry
                    ent++;
                ent++;                  // start of next table entry
                s = str;                // back to beginning of string
                sx = suffix;
            }
        }
    }
    return 0;                   // never found match
}

/*******************************
 * Output a mangled string into the symbol string table
 * Input:
 *      str     =       string to add
 *
 * Returns index into the table.
 */

static IDXSTR elf_addmangled(Symbol *s)
{
    //printf("elf_addmangled(%s)\n", s->Sident);
    char dest[DEST_LEN];
    char *destr;
    const char *name;
    int len;
    IDXSTR namidx;

    namidx = symtab_strings->size();
    destr = obj_mangle2(s, dest);
    name = destr;
    if (CPP && name[0] == '_' && name[1] == '_')
    {
        if (strncmp(name,"__ct__",6) == 0)
            name += 4;
#if 0
        switch(name[2])
        {
            case 'c':
                if (strncmp(name,"__ct__",6) == 0)
                    name += 4;
                break;
            case 'd':
                if (strcmp(name,"__dl__FvP") == 0)
                    name = "__builtin_delete";
                break;
            case 'v':
                //if (strcmp(name,"__vec_delete__FvPiUIPi") == 0)
                    //name = "__builtin_vec_del";
                //else
                //if (strcmp(name,"__vn__FPUI") == 0)
                    //name = "__builtin_vec_new";
                break;
            case 'n':
                if (strcmp(name,"__nw__FPUI") == 0)
                    name = "__builtin_new";
                break;
        }
#endif
    }
    else if (tyfunc(s->ty()) && s->Sfunc && s->Sfunc->Fredirect)
        name = s->Sfunc->Fredirect;
    len = strlen(name);
    symtab_strings->reserve(len+1);
    strcpy((char *)symtab_strings->p,name);
    symtab_strings->setsize(namidx+len+1);
    if (destr != dest)                  // if we resized result
        mem_free(destr);
    //dbg_printf("\telf_addmagled symtab_strings %s namidx %d len %d size %d\n",name, namidx,len,symtab_strings->size());
    return namidx;
}

/*******************************
 * Output a symbol into the symbol table
 * Input:
 *      stridx  =       string table index for name
 *      val     =       value associated with symbol
 *      sz      =       symbol size
 *      typ     =       symbol type
 *      bind    =       symbol binding
 *      segidx  =       segment index for segment where symbol is defined
 *
 * Returns the symbol table index for the symbol
 */

static IDXSYM elf_addsym(IDXSTR nam, targ_size_t val, unsigned sz,
        unsigned typ, unsigned bind, IDXSEC sec)
{
    //dbg_printf("elf_addsym(nam %d, val %d, sz %x, typ %x, bind %x, sec %d\n",
            //nam,val,sz,typ,bind,sec);
    if (I64)
    {
        if (!SYMbuf)
        {   SYMbuf = new Outbuffer(50 * sizeof(Elf64_Sym));
            SYMbuf->reserve(100 * sizeof(Elf64_Sym));
        }
        Elf64_Sym sym;
        sym.st_name = nam;
        sym.st_value = val;
        sym.st_size = sz;
        sym.st_info = ELF_ST_INFO(bind,typ);
        sym.st_other = 0;
        sym.st_shndx = sec;
        SYMbuf->write(&sym,sizeof(sym));
    }
    else
    {
        if (!SYMbuf)
        {   SYMbuf = new Outbuffer(50 * sizeof(Elf32_Sym));
            SYMbuf->reserve(100 * sizeof(Elf32_Sym));
        }
        Elf32_Sym sym;
        sym.st_name = nam;
        sym.st_value = val;
        sym.st_size = sz;
        sym.st_info = ELF_ST_INFO(bind,typ);
        sym.st_other = 0;
        sym.st_shndx = sec;
        SYMbuf->write(&sym,sizeof(sym));
    }
    if (bind == STB_LOCAL)
        local_cnt++;
    //dbg_printf("\treturning symbol table index %d\n",symbol_idx);
    return symbol_idx++;
}

/*******************************
 * Create a new section header table entry.
 *
 * Input:
 *      name    =       section name
 *      suffix  =       suffix for name or NULL
 *      type    =       type of data in section sh_type
 *      flags   =       attribute flags sh_flags
 * Output:
 *      section_cnt = assigned number for this section
 *              Note: Sections will be reordered on output
 */

static IDXSEC elf_newsection2(
        elf_u32_f32 name,
        elf_u32_f32 type,
        elf_u32_f32 flags,
        elf_add_f32 addr,
        elf_off_f32 offset,
        elf_u32_f32 size,
        elf_u32_f32 link,
        elf_u32_f32 info,
        elf_u32_f32 addralign,
        elf_u32_f32 entsize)
{
    Elf32_Shdr sec;

    sec.sh_name = name;
    sec.sh_type = type;
    sec.sh_flags = flags;
    sec.sh_addr = addr;
    sec.sh_offset = offset;
    sec.sh_size = size;
    sec.sh_link = link;
    sec.sh_info = info;
    sec.sh_addralign = addralign;
    sec.sh_entsize = entsize;

    if (!SECbuf)
    {   SECbuf = new Outbuffer(4 * sizeof(Elf32_Shdr));
        SECbuf->reserve(16 * sizeof(Elf32_Shdr));
    }
    SECbuf->write((void *)&sec, sizeof(sec));
    return section_cnt++;
}

static IDXSEC elf_newsection(const char *name, const char *suffix,
        elf_u32_f32 type, elf_u32_f32 flags)
{
    Elf32_Shdr sec;

//    dbg_printf("elf_newsection(%s,%s,type %d, flags x%x)\n",
//        name?name:"",suffix?suffix:"",type,flags);

#if 1
    int namidx = elf_addstr(section_names,name);
#else
    int namidx = section_names->size();
    section_names->writeString(name);
#endif
                                        // name in section names table
    if (suffix)                         // suffix - back up over NUL and
    {                                   //          append suffix string
        section_names->setsize(section_names->size()-1);
        section_names->writeString(suffix);
    };
    return elf_newsection2(namidx,type,flags,0,0,0,0,0,0,0);
}

/**************************
 * Ouput read only data and generate a symbol for it.
 *
 */

symbol *elf_sym_cdata(tym_t ty,char *p,int len)
{
    symbol *s;

#if 0
    if (OPT_IS_SET(OPTfwritable_strings))
    {
        alignOffset(DATA, tysize(ty));
        s = symboldata(Doffset, ty);
        SegData[DATA]->SDbuf->write(p,len);
        s->Sseg = DATA;
        s->Soffset = Doffset;   // Remember its offset into DATA section
        Doffset += len;
    }
    else
#endif
    {
        //printf("elf_sym_cdata(ty = %x, p = %x, len = %d, CDoffset = %x)\n", ty, p, len, CDoffset);
        alignOffset(CDATA, tysize(ty));
        s = symboldata(CDoffset, ty);
        obj_bytes(CDATA, CDoffset, len, p);
        s->Sseg = CDATA;
    }

    s->Sfl = /*(config.flags3 & CFG3pic) ? FLgotoff :*/ FLextern;
    return s;
}

/**************************
 * Ouput read only data for data
 *
 */

int elf_data_cdata(char *p, int len, int *pseg)
{
    int oldoff;
    /*if (OPT_IS_SET(OPTfwritable_strings))
    {
        oldoff = Doffset;
        SegData[DATA]->SDbuf->reserve(len);
        SegData[DATA]->SDbuf->writen(p,len);
        Doffset += len;
        *pseg = DATA;
    }
    else*/
    {
        oldoff = CDoffset;
        SegData[CDATA]->SDbuf->reserve(len);
        SegData[CDATA]->SDbuf->writen(p,len);
        CDoffset += len;
        *pseg = CDATA;
    }
    return oldoff;
}

int elf_data_cdata(char *p, int len)
{
    int pseg;

    return elf_data_cdata(p, len, &pseg);
}

/******************************
 * Perform initialization that applies to all .o output files.
 *      Called before any other obj_xxx routines
 */

void obj_init(Outbuffer *objbuf, const char *filename, const char *csegname)
{
    //printf("obj_init()\n");

    cseg = CODE;
    fobjbuf = objbuf;

    mapsec2sym = NULL;
    note_data = NULL;
    secidx_note = 0;
    comment_data = NULL;
    seg_tlsseg = UNKNOWN;
    seg_tlsseg_bss = UNKNOWN;
    GOTsym = NULL;

    // Initialize buffers

    if (symtab_strings)
        symtab_strings->setsize(1);
    else
    {   symtab_strings = new Outbuffer(1024);
        symtab_strings->reserve(2048);
        symtab_strings->writeByte(0);
    }

    if (SECbuf)
        SECbuf->setsize(0);
    section_cnt = 0;

    if (I64)
    {
        static char section_names_init64[] =
          "\0.symtab\0.strtab\0.shstrtab\0.text\0.data\0.bss\0.note\0.comment\0.rodata\0.rela.text\0.rela.data";
        #define NAMIDX_NONE      0
        #define NAMIDX_SYMTAB    1       // .symtab
        #define NAMIDX_STRTAB    9       // .strtab
        #define NAMIDX_SHSTRTAB 17      // .shstrtab
        #define NAMIDX_TEXT     27      // .text
        #define NAMIDX_DATA     33      // .data
        #define NAMIDX_BSS      39      // .bss
        #define NAMIDX_NOTE     44      // .note
        #define NAMIDX_COMMENT  50      // .comment
        #define NAMIDX_RODATA   59      // .rodata
        #define NAMIDX_RELTEXT  67      // .rel.text and .rela.text
        #define NAMIDX_RELDATA  77      // .rel.data
        #define NAMIDX_RELDATA64 78      // .rela.data

        if (section_names)
            section_names->setsize(sizeof(section_names_init64));
        else
        {   section_names = new Outbuffer(512);
            section_names->reserve(1024);
            section_names->writen(section_names_init64, sizeof(section_names_init64));
        }

        // name,type,flags,addr,offset,size,link,info,addralign,entsize
        elf_newsection2(0,               SHT_NULL,   0,                 0,0,0,0,0, 0,0);
        elf_newsection2(NAMIDX_TEXT,SHT_PROGDEF,SHF_ALLOC|SHF_EXECINSTR,0,0,0,0,0, 4,0);
        elf_newsection2(NAMIDX_RELTEXT,SHT_RELA, 0,0,0,0,SHI_SYMTAB,     SHI_TEXT, 8,8);
        elf_newsection2(NAMIDX_DATA,SHT_PROGDEF,SHF_ALLOC|SHF_WRITE,    0,0,0,0,0, 8,0);
        elf_newsection2(NAMIDX_RELDATA64,SHT_RELA, 0,0,0,0,SHI_SYMTAB,   SHI_DATA, 8,8);
        elf_newsection2(NAMIDX_BSS, SHT_NOBITS,SHF_ALLOC|SHF_WRITE,     0,0,0,0,0, 16,0);
        elf_newsection2(NAMIDX_RODATA,SHT_PROGDEF,SHF_ALLOC,            0,0,0,0,0, 1,0);
        elf_newsection2(NAMIDX_STRTAB,SHT_STRTAB, 0,                    0,0,0,0,0, 1,0);
        elf_newsection2(NAMIDX_SYMTAB,SHT_SYMTAB, 0,                    0,0,0,0,0, 8,0);
        elf_newsection2(NAMIDX_SHSTRTAB,SHT_STRTAB, 0,                  0,0,0,0,0, 1,0);
        elf_newsection2(NAMIDX_COMMENT, SHT_PROGDEF,0,                  0,0,0,0,0, 1,0);
        elf_newsection2(NAMIDX_NOTE,SHT_NOTE,   0,                      0,0,0,0,0, 1,0);
    }
    else
    {
        static char section_names_init[] =
          "\0.symtab\0.strtab\0.shstrtab\0.text\0.data\0.bss\0.note\0.comment\0.rodata\0.rel.text\0.rel.data";

        if (section_names)
            section_names->setsize(sizeof(section_names_init));
        else
        {   section_names = new Outbuffer(512);
            section_names->reserve(1024);
            section_names->writen(section_names_init, sizeof(section_names_init));
        }

        // name,type,flags,addr,offset,size,link,info,addralign,entsize
        elf_newsection2(0,               SHT_NULL,   0,                 0,0,0,0,0, 0,0);
        elf_newsection2(NAMIDX_TEXT,SHT_PROGDEF,SHF_ALLOC|SHF_EXECINSTR,0,0,0,0,0, 16,0);
        elf_newsection2(NAMIDX_RELTEXT,SHT_REL, 0,0,0,0,SHI_SYMTAB,      SHI_TEXT, 4,8);
        elf_newsection2(NAMIDX_DATA,SHT_PROGDEF,SHF_ALLOC|SHF_WRITE,    0,0,0,0,0, 4,0);
        elf_newsection2(NAMIDX_RELDATA,SHT_REL, 0,0,0,0,SHI_SYMTAB,      SHI_DATA, 4,8);
        elf_newsection2(NAMIDX_BSS, SHT_NOBITS,SHF_ALLOC|SHF_WRITE,     0,0,0,0,0, 32,0);
        elf_newsection2(NAMIDX_RODATA,SHT_PROGDEF,SHF_ALLOC,            0,0,0,0,0, 1,0);
        elf_newsection2(NAMIDX_STRTAB,SHT_STRTAB, 0,                    0,0,0,0,0, 1,0);
        elf_newsection2(NAMIDX_SYMTAB,SHT_SYMTAB, 0,                    0,0,0,0,0, 4,0);
        elf_newsection2(NAMIDX_SHSTRTAB,SHT_STRTAB, 0,                  0,0,0,0,0, 1,0);
        elf_newsection2(NAMIDX_COMMENT, SHT_PROGDEF,0,                  0,0,0,0,0, 1,0);
        elf_newsection2(NAMIDX_NOTE,SHT_NOTE,   0,                      0,0,0,0,0, 1,0);
    }

    if (SYMbuf)
        SYMbuf->setsize(0);
    symbol_idx = 0;
    local_cnt = 0;
    // The symbols that every object file has
    elf_addsym(0, 0, 0, STT_NOTYPE,  STB_LOCAL, 0);
    elf_addsym(0, 0, 0, STT_FILE,    STB_LOCAL, SHT_ABS);       // STI_FILE
    elf_addsym(0, 0, 0, STT_SECTION, STB_LOCAL, SHI_TEXT);      // STI_TEXT
    elf_addsym(0, 0, 0, STT_SECTION, STB_LOCAL, SHI_DATA);      // STI_DATA
    elf_addsym(0, 0, 0, STT_SECTION, STB_LOCAL, SHI_BSS);       // STI_BSS
    elf_addsym(0, 0, 0, STT_NOTYPE,  STB_LOCAL, SHI_TEXT);      // STI_GCC
    elf_addsym(0, 0, 0, STT_SECTION, STB_LOCAL, SHI_RODAT);     // STI_RODAT
    elf_addsym(0, 0, 0, STT_SECTION, STB_LOCAL, SHI_NOTE);      // STI_NOTE
    elf_addsym(0, 0, 0, STT_SECTION, STB_LOCAL, SHI_COM);       // STI_COM

    // Initialize output buffers for CODE, DATA and COMMENTS
    //      (NOTE not supported, BSS not required)

    seg_count = 0;

    elf_getsegment2(SHI_TEXT, STI_TEXT, SHI_RELTEXT);
    assert(SegData[CODE]->SDseg == CODE);

    elf_getsegment2(SHI_DATA, STI_DATA, SHI_RELDATA);
    assert(SegData[DATA]->SDseg == DATA);

    elf_getsegment2(SHI_RODAT, STI_RODAT, 0);
    assert(SegData[CDATA]->SDseg == CDATA);

    elf_getsegment2(SHI_BSS, STI_BSS, 0);
    assert(SegData[UDATA]->SDseg == UDATA);

    elf_getsegment2(SHI_COM, STI_COM, 0);
    assert(SegData[COMD]->SDseg == COMD);

    if (config.fulltypes)
        dwarf_initfile(filename);
}

/**************************
 * Initialize the start of object output for this particular .o file.
 *
 * Input:
 *      filename:       Name of source file
 *      csegname:       User specified default code segment name
 */

void obj_initfile(const char *filename, const char *csegname, const char *modname)
{
    //dbg_printf("obj_initfile(filename = %s, modname = %s)\n",filename,modname);

    IDXSTR name = elf_addstr(symtab_strings, filename);
    if (I64)
        SymbolTable64[STI_FILE].st_name = name;
    else
        SymbolTable[STI_FILE].st_name = name;

#if 0
    // compiler flag for linker
    if (I64)
        SymbolTable64[STI_GCC].st_name = elf_addstr(symtab_strings,"gcc2_compiled.");
    else
        SymbolTable[STI_GCC].st_name = elf_addstr(symtab_strings,"gcc2_compiled.");
#endif

    if (csegname && *csegname && strcmp(csegname,".text"))
    {   // Define new section and make it the default for cseg segment
        // NOTE: cseg is initialized to CODE
        IDXSEC newsecidx;
        Elf32_Shdr *newtextsec;
        IDXSYM newsymidx;
        SegData[cseg]->SDshtidx = newsecidx =
            elf_newsection(csegname,0,SHT_PROGDEF,SHF_ALLOC|SHF_EXECINSTR);
        newtextsec = &SecHdrTab[newsecidx];
        newtextsec->sh_addralign = 4;
        SegData[cseg]->SDsymidx =
            elf_addsym(0, 0, 0, STT_SECTION, STB_LOCAL, newsecidx);
    }
    if (config.fulltypes)
        dwarf_initmodule(filename, modname);
}

/***************************
 * Renumber symbols so they are
 * ordered as locals, weak and then global
 * Returns:
 *      sorted symbol table, caller must free with util_free()
 */

void *elf_renumbersyms()
{   void *symtab;
    int nextlocal = 0;
    int nextglobal = local_cnt;

    SYMIDX *sym_map = (SYMIDX *)util_malloc(sizeof(SYMIDX),symbol_idx);

    if (I64)
    {
        Elf64_Sym *oldsymtab = (Elf64_Sym *)SYMbuf->buf;
        Elf64_Sym *symtabend = oldsymtab+symbol_idx;

        symtab = util_malloc(sizeof(Elf64_Sym),symbol_idx);

        Elf64_Sym *sl = (Elf64_Sym *)symtab;
        Elf64_Sym *sg = sl + local_cnt;

        int old_idx = 0;
        for(Elf64_Sym *s = oldsymtab; s != symtabend; s++)
        {   // reorder symbol and map new #s to old
            int bind = ELF_ST_BIND(s->st_info);
            if (bind == STB_LOCAL)
            {
                *sl++ = *s;
                sym_map[old_idx] = nextlocal++;
            }
            else
            {
                *sg++ = *s;
                sym_map[old_idx] = nextglobal++;
            }
            old_idx++;
        }
    }
    else
    {
        Elf32_Sym *oldsymtab = (Elf32_Sym *)SYMbuf->buf;
        Elf32_Sym *symtabend = oldsymtab+symbol_idx;

        symtab = util_malloc(sizeof(Elf32_Sym),symbol_idx);

        Elf32_Sym *sl = (Elf32_Sym *)symtab;
        Elf32_Sym *sg = sl + local_cnt;

        int old_idx = 0;
        for(Elf32_Sym *s = oldsymtab; s != symtabend; s++)
        {   // reorder symbol and map new #s to old
            int bind = ELF_ST_BIND(s->st_info);
            if (bind == STB_LOCAL)
            {
                *sl++ = *s;
                sym_map[old_idx] = nextlocal++;
            }
            else
            {
                *sg++ = *s;
                sym_map[old_idx] = nextglobal++;
            }
            old_idx++;
        }
    }

    // Renumber the relocations
    for (int i = 1; i <= seg_count; i++)
    {                           // Map indicies in the segment table
        seg_data *pseg = SegData[i];
        pseg->SDsymidx = sym_map[pseg->SDsymidx];
        if (pseg->SDrel)
        {
            if (I64)
            {
                Elf64_Rela *rel = (Elf64_Rela *) pseg->SDrel->buf;
                for (int r = 0; r < pseg->SDrelcnt; r++)
                {
                    unsigned t = ELF64_R_TYPE(rel->r_info);
                    unsigned si = ELF64_R_SYM(rel->r_info);
                    rel->r_info = ELF64_R_INFO(sym_map[si],t);
                    rel++;
                }
            }
            else
            {
                Elf32_Rel *rel = (Elf32_Rel *) pseg->SDrel->buf;
                assert(pseg->SDrelcnt == pseg->SDrel->size() / sizeof(Elf32_Rel));
                for (int r = 0; r < pseg->SDrelcnt; r++)
                {
                    unsigned t = ELF32_R_TYPE(rel->r_info);
                    unsigned si = ELF32_R_IDX(rel->r_info);
                    rel->r_info = ELF32_R_INFO(sym_map[si],t);
                    rel++;
                }
            }
        }
    };

    return symtab;
}


/***************************
 * Fixup and terminate object file.
 */

void obj_termfile()
{
    //dbg_printf("obj_termfile\n");
    if (configv.addlinenumbers)
    {
        dwarf_termmodule();
    }
}

/*********************************
 * Terminate package.
 */

void obj_term()
{
    //printf("obj_term()\n");
#if SCPP
    if (!errcnt)
#endif
    {
        outfixlist();           // backpatches
    }

    if (configv.addlinenumbers)
    {
        dwarf_termfile();
    }

#if SCPP
    if (errcnt)
        return;
#endif

    // Write out the bytes for the header
    static const char elf_string32[EI_NIDENT] =
    {
        ELFMAG0,ELFMAG1,ELFMAG2,ELFMAG3,
        ELFCLASS32,             // EI_CLASS
        ELFDATA2LSB,    // EI_DATA
        EV_CURRENT,             // EI_VERSION
        ELFOSABI_LINUX,0,       // EI_OSABI,EI_ABIVERSION
        0,0,0,0,0,0,0
    };
    static const char elf_string64[EI_NIDENT] =
    {
        ELFMAG0,ELFMAG1,ELFMAG2,ELFMAG3,
        ELFCLASS64,             // EI_CLASS
        ELFDATA2LSB,    // EI_DATA
        EV_CURRENT,             // EI_VERSION
        ELFOSABI_LINUX,0,       // EI_OSABI,EI_ABIVERSION
        0,0,0,0,0,0,0
    };
    fobjbuf->write(I64 ? elf_string64 : elf_string32, EI_NIDENT);

    long foffset;
    Elf32_Shdr *sechdr;
    seg_data *seg;
    void *symtab = elf_renumbersyms();
    FILE *fd = NULL;

    // Output the ELF Header
    // The section header is build in the static variable elf_header
    static Elf64_Ehdr elf_header =
    {
        ET_REL,                         // e_type
        EM_X86_64,                      // e_machine
        EV_CURRENT,                     // e_version
        0,                              // e_entry
        0,                              // e_phoff
        0,                              // e_shoff
        0,                              // e_flags
        sizeof(Elf64_Ehdr) + EI_NIDENT, // e_ehsize
        sizeof(Elf64_Phdr),             // e_phentsize
        0,                              // e_phnum
        sizeof(Elf64_Shdr),             // e_shentsize
        0,                              // e_shnum
        0                               // e_shstrndx
    };
    int hdrsize = I64 ? sizeof(Elf64_Ehdr) : sizeof(Elf32_Hdr);

    elf_header.e_shnum = section_cnt;
    elf_header.e_shstrndx = SHI_SECNAMES;
    fobjbuf->writezeros(hdrsize);

            // Walk through sections determining size and file offsets
            // Sections will be output in the following order
            //  Null segment
            //  For each Code/Data Segment
            //      code/data to load
            //      relocations without addens
            //  .bss
            //  notes
            //  comments
            //  section names table
            //  symbol table
            //  strings table

    foffset = EI_NIDENT + hdrsize;      // start after header
                                    // section header table at end

    //
    // First output individual section data associate with program
    //  code and data
    //
    //printf("Setup offsets and sizes foffset %d\n\tsection_cnt %d, seg_count %d\n",foffset,section_cnt,seg_count);
    for (int i=1; i<= seg_count; i++)
    {
        seg = SegData[i];
        sechdr = MAP_SEG2SEC(i);        // corresponding section
        foffset = elf_align(fd,sechdr->sh_addralign,foffset);
        if (i == UDATA) // 0, BSS never allocated
        {   // but foffset as if it has
            sechdr->sh_offset = foffset;
            sechdr->sh_size = seg->SDoffset;
                                // accumulated size
            continue;
        }
        else if (sechdr->sh_type == SHT_NOBITS) // .tbss never allocated
        {
            sechdr->sh_offset = foffset;
            sechdr->sh_size = seg->SDoffset;
                                // accumulated size
            continue;
        }
        else if (!seg->SDbuf)
            continue;           // For others leave sh_offset as 0

        sechdr->sh_offset = foffset;
        //printf("\tsection name %d,",sechdr->sh_name);
        if (seg->SDbuf && seg->SDbuf->size())
        {
            //printf(" - size %d\n",seg->SDbuf->size());
            sechdr->sh_size = seg->SDbuf->size();
            fobjbuf->write(seg->SDbuf->buf, sechdr->sh_size);
            foffset += sechdr->sh_size;
        }
        //printf(" assigned offset %d, size %d\n",foffset,sechdr->sh_size);
    }

    //
    // Next output any notes or comments
    //
    if (note_data)
    {
        sechdr = &SecHdrTab[secidx_note];               // Notes
        sechdr->sh_size = note_data->size();
        sechdr->sh_offset = foffset;
        fobjbuf->write(note_data->buf, sechdr->sh_size);
        foffset += sechdr->sh_size;
    }

    if (comment_data)
    {
        sechdr = &SecHdrTab[SHI_COM];           // Comments
        sechdr->sh_size = comment_data->size();
        sechdr->sh_offset = foffset;
        fobjbuf->write(comment_data->buf, sechdr->sh_size);
        foffset += sechdr->sh_size;
    }

    //
    // Then output string table for section names
    //
    sechdr = &SecHdrTab[SHI_SECNAMES];  // Section Names
    sechdr->sh_size = section_names->size();
    sechdr->sh_offset = foffset;
    //dbg_printf("section names offset %d\n",foffset);
    fobjbuf->write(section_names->buf, sechdr->sh_size);
    foffset += sechdr->sh_size;

    //
    // Symbol table and string table for symbols next
    //
    //dbg_printf("output symbol table size %d\n",SYMbuf->size());
    sechdr = &SecHdrTab[SHI_SYMTAB];    // Symbol Table
    sechdr->sh_size = SYMbuf->size();
    sechdr->sh_entsize = I64 ? sizeof(Elf64_Sym) : sizeof(Elf32_Sym);
    sechdr->sh_link = SHI_STRINGS;
    sechdr->sh_info = local_cnt;
    foffset = elf_align(fd,4,foffset);
    sechdr->sh_offset = foffset;
    fobjbuf->write(symtab, sechdr->sh_size);
    foffset += sechdr->sh_size;
    util_free(symtab);

    //dbg_printf("output section strings size 0x%x,offset 0x%x\n",symtab_strings->size(),foffset);
    sechdr = &SecHdrTab[SHI_STRINGS];   // Symbol Strings
    sechdr->sh_size = symtab_strings->size();
    sechdr->sh_offset = foffset;
    fobjbuf->write(symtab_strings->buf, sechdr->sh_size);
    foffset += sechdr->sh_size;

    //
    // Now the relocation data for program code and data sections
    //
    foffset = elf_align(fd,4,foffset);
    //dbg_printf("output relocations size 0x%x, foffset 0x%x\n",section_names->size(),foffset);
    for (int i=1; i<= seg_count; i++)
    {
        seg = SegData[i];
        if (!seg->SDbuf)
            continue;           // 0, BSS never allocated
        if (seg->SDrel && seg->SDrel->size())
        {
            assert(seg->SDrelidx);
            sechdr = &SecHdrTab[seg->SDrelidx];
            sechdr->sh_size = seg->SDrel->size();
            sechdr->sh_offset = foffset;
            if (I64)
                assert(seg->SDrelcnt == seg->SDrel->size() / sizeof(Elf64_Rela));
            else
                assert(seg->SDrelcnt == seg->SDrel->size() / sizeof(Elf32_Rel));
            fobjbuf->write(seg->SDrel->buf, sechdr->sh_size);
            foffset += sechdr->sh_size;
        }
    }

    //
    // Finish off with the section header table
    //
    elf_header.e_shoff = foffset;       // remember location in elf header
    //dbg_printf("output section header table\n");

    // Output the completed Section Header Table
    if (I64)
    {   // Translate section headers to 64 bits
        int sz = section_cnt * sizeof(Elf64_Shdr);
        fobjbuf->reserve(sz);
        for (int i = 0; i < section_cnt; i++)
        {
            Elf32_Shdr *p = SecHdrTab + i;
            Elf64_Shdr s;
            s.sh_name      = p->sh_name;
            s.sh_type      = p->sh_type;
            s.sh_flags     = p->sh_flags;
            s.sh_addr      = p->sh_addr;
            s.sh_offset    = p->sh_offset;
            s.sh_size      = p->sh_size;
            s.sh_link      = p->sh_link;
            s.sh_info      = p->sh_info;
            s.sh_addralign = p->sh_addralign;
            s.sh_entsize   = p->sh_entsize;
            fobjbuf->write(&s, sizeof(s));
        }
        foffset += sz;
    }
    else
    {
        fobjbuf->write(SecHdrTab, section_cnt * sizeof(Elf32_Shdr));
        foffset += section_cnt * sizeof(Elf32_Shdr);
    }

    //
    // Now that we have correct offset to section header table, e_shoff,
    //  go back and re-output the elf header
    //
    fobjbuf->position(EI_NIDENT, hdrsize);
    if (I64)
    {
        fobjbuf->write(&elf_header, hdrsize);
    }
    else
    {   Elf32_Hdr h;
        // Transfer to 32 bit header
        h.e_type      = elf_header.e_type;
        h.e_machine   = EM_386;
        h.e_version   = elf_header.e_version;
        h.e_entry     = elf_header.e_entry;
        h.e_phoff     = elf_header.e_phoff;
        h.e_shoff     = elf_header.e_shoff;
        h.e_flags     = elf_header.e_flags;
        h.e_ehsize    = sizeof(Elf32_Hdr) + EI_NIDENT;
        h.e_phentsize = sizeof(elf_pht);
        h.e_phnum     = elf_header.e_phnum;
        h.e_shentsize = sizeof(Elf32_Shdr);
        h.e_shnum     = elf_header.e_shnum;
        h.e_shstrndx  = elf_header.e_shstrndx;
        fobjbuf->write(&h, hdrsize);
    }
    fobjbuf->position(foffset, 0);
    fobjbuf->flush();
}

/*****************************
 * Line number support.
 */

/***************************
 * Record file and line number at segment and offset.
 * The actual .debug_line segment is put out by dwarf_termfile().
 * Input:
 *      cseg    current code segment
 */

void objlinnum(Srcpos srcpos, targ_size_t offset)
{
    unsigned linnum = srcpos.Slinnum;
    if (linnum == 0)
        return;

#if 0
#if MARS
    printf("objlinnum(cseg=%d, filename=%s linnum=%u, offset=x%lx)\n",
        cseg,srcpos.Sfilename ? srcpos.Sfilename : "null",linnum,offset);
#endif
#if SCPP
    printf("objlinnum(cseg=%d, filptr=%p linnum=%u, offset=x%lx)\n",
        cseg,srcpos.Sfilptr ? *srcpos.Sfilptr : 0,linnum,offset);
    if (srcpos.Sfilptr)
    {
        Sfile *sf = *srcpos.Sfilptr;
        printf("filename = %s\n", sf ? sf->SFname : "null");
    }
#endif
#endif

#if MARS
    if (!srcpos.Sfilename)
        return;
#endif
#if SCPP
    Sfile *sf;
    if (srcpos.Sfilptr)
    {   sfile_debug(&srcpos_sfile(srcpos));
        sf = *srcpos.Sfilptr;
    }
    else
        return;
#endif

    size_t i;
    seg_data *seg = SegData[cseg];

    // Find entry i in SDlinnum_data[] that corresponds to srcpos filename
    for (i = 0; 1; i++)
    {
        if (i == seg->SDlinnum_count)
        {   // Create new entry
            if (seg->SDlinnum_count == seg->SDlinnum_max)
            {   // Enlarge array
                unsigned newmax = seg->SDlinnum_max * 2 + 1;
                //printf("realloc %d\n", newmax * sizeof(linnum_data));
                seg->SDlinnum_data = (linnum_data *)mem_realloc(
                    seg->SDlinnum_data, newmax * sizeof(linnum_data));
                memset(seg->SDlinnum_data + seg->SDlinnum_max, 0,
                    (newmax - seg->SDlinnum_max) * sizeof(linnum_data));
                seg->SDlinnum_max = newmax;
            }
            seg->SDlinnum_count++;
#if MARS
            seg->SDlinnum_data[i].filename = srcpos.Sfilename;
#endif
#if SCPP
            seg->SDlinnum_data[i].filptr = sf;
#endif
            break;
        }
#if MARS
        if (seg->SDlinnum_data[i].filename == srcpos.Sfilename)
#endif
#if SCPP
        if (seg->SDlinnum_data[i].filptr == sf)
#endif
            break;
    }

    linnum_data *ld = &seg->SDlinnum_data[i];
//    printf("i = %d, ld = x%x\n", i, ld);
    if (ld->linoff_count == ld->linoff_max)
    {
        if (!ld->linoff_max)
            ld->linoff_max = 8;
        ld->linoff_max *= 2;
        ld->linoff = (unsigned (*)[2])mem_realloc(ld->linoff, ld->linoff_max * sizeof(unsigned) * 2);
    }
    ld->linoff[ld->linoff_count][0] = srcpos.Slinnum;
    ld->linoff[ld->linoff_count][1] = offset;
    ld->linoff_count++;
}


/*******************************
 * Set start address
 */

void obj_startaddress(Symbol *s)
{
    //dbg_printf("obj_startaddress(Symbol *%s)\n",s->Sident);
    //obj.startaddress = s;
}

/*******************************
 * Output library name.
 * Output:
 */

void obj_includelib(const char *name)
{
    //dbg_printf("obj_includelib(name *%s)\n",name);
}

/**************************
 * Embed string in executable.
 */

void obj_exestr(const char *p)
{
    //dbg_printf("obj_exestr(char *%s)\n",p);
}

/**************************
 * Embed string in obj.
 */

void obj_user(const char *p)
{
    //dbg_printf("obj_user(char *%s)\n",p);
}

/*******************************
 * Output a weak extern record.
 */

void obj_wkext(Symbol *s1,Symbol *s2)
{
    //dbg_printf("obj_wkext(Symbol *%s,Symbol *s2)\n",s1->Sident,s2->Sident);
}

/*******************************
 * Output file name record.
 *
 * Currently assumes that obj_filename will not be called
 *      twice for the same file.
 */

void obj_filename(const char *modname)
{
    //dbg_printf("obj_filename(char *%s)\n",modname);
    unsigned strtab_idx = elf_addstr(symtab_strings,modname);
    elf_addsym(strtab_idx,0,0,STT_FILE,STB_LOCAL,SHT_ABS);
}

/*******************************
 * Embed compiler version in .obj file.
 */

void obj_compiler()
{
    //dbg_printf("obj_compiler\n");
    comment_data = new Outbuffer();
    comment_data->write(compiler,sizeof(compiler));
    //dbg_printf("Comment data size %d\n",comment_data->size());
}


//#if NEWSTATICDTOR

/**************************************
 * Symbol is the function that calls the static constructors.
 * Put a pointer to it into a special segment that the startup code
 * looks at.
 * Input:
 *      s       static constructor function
 *      dtor    !=0 if leave space for static destructor
 *      seg     1:      user
 *              2:      lib
 *              3:      compiler
 */

void obj_staticctor(Symbol *s,int dtor,int none)
{
// Static constructors and destructors
    IDXSEC seg;
    Outbuffer *buf;

    //dbg_printf("obj_staticctor(%s) offset %x\n",s->Sident,s->Soffset);
    //symbol_print(s);
    s->Sseg = seg =
        elf_getsegment(".ctors", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE, 4);
    buf = SegData[seg]->SDbuf;
    if (I64)
        buf->write64(s->Soffset);
    else
        buf->write32(s->Soffset);
    elf_addrel(seg,SegData[seg]->SDoffset,I64 ? R_X86_64_64 : RI_TYPE_SYM32,STI_TEXT,0);
    SegData[seg]->SDoffset = buf->size();
}

/**************************************
 * Symbol is the function that calls the static destructors.
 * Put a pointer to it into a special segment that the exit code
 * looks at.
 * Input:
 *      s       static destructor function
 */

void obj_staticdtor(Symbol *s)
{
    IDXSEC seg;
    Outbuffer *buf;

    //dbg_printf("obj_staticdtor(%s) offset %x\n",s->Sident,s->Soffset);
    //symbol_print(s);
    seg = elf_getsegment(".dtors", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE, 4);
    buf = SegData[seg]->SDbuf;
    if (I64)
        buf->write64(s->Soffset);
    else
        buf->write32(s->Soffset);
    elf_addrel(seg,SegData[seg]->SDoffset,I64 ? R_X86_64_64 : RI_TYPE_SYM32,s->Sxtrnnum,0);
    SegData[seg]->SDoffset = buf->size();
}

//#else

/***************************************
 * Stuff pointer to function in its own segment.
 * Used for static ctor and dtor lists.
 */

void obj_funcptr(Symbol *s)
{
    //dbg_printf("obj_funcptr(%s) \n",s->Sident);
}

//#endif

/***************************************
 * Stuff the following data in a separate segment:
 *      pointer to function
 *      pointer to ehsym
 *      length of function
 */

void obj_ehtables(Symbol *sfunc,targ_size_t size,Symbol *ehsym)
{
    //dbg_printf("obj_ehtables(%s) \n",sfunc->Sident);

    symbol *ehtab_entry = symbol_generate(SCstatic,type_alloc(TYint));
    symbol_keep(ehtab_entry);
    elf_getsegment(".deh_beg", NULL, SHT_PROGDEF, SHF_ALLOC, 4);
    ehtab_entry->Sseg = elf_getsegment(".deh_eh", NULL, SHT_PROGDEF, SHF_ALLOC, 4);
    elf_getsegment(".deh_end", NULL, SHT_PROGDEF, SHF_ALLOC, 4);
    ehtab_entry->Stype->Tmangle = mTYman_c;
    ehsym->Stype->Tmangle = mTYman_c;
    dt_t **pdte = &ehtab_entry->Sdt;
    pdte = dtxoff(pdte,sfunc,0,TYnptr);
    pdte = dtxoff(pdte,ehsym,0,TYnptr);
    pdte = dtnbytes(pdte,4,(char *)&sfunc->Ssize);
    outdata(ehtab_entry);
}

/*********************************************
 * Put out symbols that define the beginning/end of the .deh_eh section.
 */

void obj_ehsections()
{
    int sec = elf_getsegment(".deh_beg", NULL, SHT_PROGDEF, SHF_ALLOC, 4);
    //obj_bytes(sec, 0, 4, NULL);

    IDXSTR namidx = elf_addstr(symtab_strings,"_deh_beg");
    elf_addsym(namidx, 0, 0, STT_OBJECT, STB_GLOBAL, MAP_SEG2SECIDX(sec));
    //elf_addsym(namidx, 0, 4, STT_OBJECT, STB_GLOBAL, MAP_SEG2SECIDX(sec));

    elf_getsegment(".deh_eh", NULL, SHT_PROGDEF, SHF_ALLOC, 4);

    sec = elf_getsegment(".deh_end", NULL, SHT_PROGDEF, SHF_ALLOC, 4);
    namidx = elf_addstr(symtab_strings,"_deh_end");
    elf_addsym(namidx, 0, 0, STT_OBJECT, STB_GLOBAL, MAP_SEG2SECIDX(sec));

    obj_tlssections();
}

/*********************************************
 * Put out symbols that define the beginning/end of the thread local storage sections.
 */

void obj_tlssections()
{
    IDXSTR namidx;

    int sec = elf_getsegment(".tdata", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE|SHF_TLS, 4);
    obj_bytes(sec, 0, 4, NULL);

    namidx = elf_addstr(symtab_strings,"_tlsstart");
    elf_addsym(namidx, 0, 4, STT_TLS, STB_GLOBAL, MAP_SEG2SECIDX(sec));

    elf_getsegment(".tdata.", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE|SHF_TLS, 4);

    sec = elf_getsegment(".tcommon", NULL, SHT_NOBITS, SHF_ALLOC|SHF_WRITE|SHF_TLS, 4);
    namidx = elf_addstr(symtab_strings,"_tlsend");
    elf_addsym(namidx, 0, 4, STT_TLS, STB_GLOBAL, MAP_SEG2SECIDX(sec));
}

/*********************************
 * Setup for Symbol s to go into a COMDAT segment.
 * Output (if s is a function):
 *      cseg            segment index of new current code segment
 *      Coffset         starting offset in cseg
 * Returns:
 *      "segment index" of COMDAT
 */

int obj_comdat(Symbol *s)
{
    const char *prefix;
    int type;
    int flags;

    //printf("obj_comdat(Symbol *%s\n",s->Sident);
    //symbol_print(s);
    symbol_debug(s);
    if (tyfunc(s->ty()))
    {
        //s->Sfl = FLcode;      // was FLoncecode
        //prefix = ".gnu.linkonce.t";   // doesn't work, despite documentation
        prefix = ".text.";              // undocumented, but works
        type = SHT_PROGDEF;
        flags = SHF_ALLOC|SHF_EXECINSTR;
    }
    else if ((s->ty() & mTYLINK) == mTYthread)
    {
        /* Ensure that ".tdata" precedes any other .tdata. section, as the ld
         * linker script fails to work right.
         */
        elf_getsegment(".tdata", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE|SHF_TLS, 4);

        s->Sfl = FLtlsdata;
        prefix = ".tdata.";
        type = SHT_PROGDEF;
        flags = SHF_ALLOC|SHF_WRITE|SHF_TLS;
    }
    else
    {
        s->Sfl = FLdata;
        //prefix = ".gnu.linkonce.d.";
        prefix = ".data.";
        type = SHT_PROGDEF;
        flags = SHF_ALLOC|SHF_WRITE;
    }

    s->Sseg = elf_getsegment(prefix, cpp_mangle(s), type, flags, 4);
                                // find or create new segment
    SegData[s->Sseg]->SDsym = s;
    if (s->Sfl == FLdata || s->Sfl == FLtlsdata)
    {
        objpubdef(s->Sseg,s,0);
        searchfixlist(s);               // backpatch any refs to this symbol
    }
    return s->Sseg;
}

/********************************
 * Get a segment for a segment name.
 * Input:
 *      name            name of segment, if NULL then revert to default name
 *      suffix          append to name
 *      align           alignment
 * Returns:
 *      segment index of found or newly created segment
 */

int elf_getsegment2(IDXSEC shtidx, IDXSYM symidx, IDXSEC relidx)
{
    //printf("SegData = %p\n", SegData);
    int seg = ++seg_count;
    if (seg_count >= seg_max)
    {                           // need more room in segment table
        seg_max += OB_SEG_INC;
        SegData = (seg_data **)mem_realloc(SegData,seg_max * sizeof(seg_data *));
        memset(&SegData[seg_count], 0, (seg_max - seg_count) * sizeof(seg_data *));
    }
    assert(seg_count < seg_max);
    if (!SegData[seg])
    {   SegData[seg] = (seg_data *)mem_calloc(sizeof(seg_data));
        //printf("test2: SegData[%d] = %p\n", seg, SegData[seg]);
    }

    seg_data *pseg = SegData[seg];
    pseg->SDseg = seg;
    pseg->SDshtidx = shtidx;
    pseg->SDoffset = 0;
    if (pseg->SDbuf)
        pseg->SDbuf->setsize(0);
    else
    {   if (SecHdrTab[shtidx].sh_type != SHT_NOBITS)
        {   pseg->SDbuf = new Outbuffer(OB_XTRA_STR);
            pseg->SDbuf->reserve(1024);
        }
    }
    if (pseg->SDrel)
        pseg->SDrel->setsize(0);
    pseg->SDsymidx = symidx;
    pseg->SDrelidx = relidx;
    pseg->SDrelmaxoff = 0;
    pseg->SDrelindex = 0;
    pseg->SDrelcnt = 0;
    pseg->SDshtidxout = 0;
    pseg->SDsym = NULL;
    return seg;
}

int elf_getsegment(const char *name, const char *suffix, int type, int flags,
        int align)
{
    //printf("elf_getsegment(%s,%s,flags %x, align %d)\n",name,suffix,flags,align);

    IDXSTR namidx;
    if (namidx = elf_findstr(section_names,name,suffix))
    {                                   // this section name exists
        for (int seg = CODE; seg <= seg_count; seg++)
        {                               // should be in segment table
            if (MAP_SEG2SEC(seg)->sh_name == namidx)
            {
                return seg;             // found section for segment
            }
        }
        assert(0);      // but it's not a segment
        // FIX - should be an error message conflict with section names
    }

    //dbg_printf("\tNew segment - %d size %d\n", seg,SegData[seg]->SDbuf);
    IDXSEC shtidx = elf_newsection(name,suffix,type,flags);
    SecHdrTab[shtidx].sh_addralign = align;
    IDXSYM symidx = elf_addsym(0, 0, 0, STT_SECTION, STB_LOCAL, shtidx);
    int seg = elf_getsegment2(shtidx, symidx, 0);
    //printf("-elf_getsegment() = %d\n", seg);
    return seg;
}

/********************************
 * Define a new code segment.
 * Input:
 *      name            name of segment, if NULL then revert to default
 *      suffix  0       use name as is
 *              1       append "_TEXT" to name
 * Output:
 *      cseg            segment index of new current code segment
 *      Coffset         starting offset in cseg
 * Returns:
 *      segment index of newly created code segment
 */

int obj_codeseg(char *name,int suffix)
{
    int seg;
    const char *sfx;

    //dbg_printf("obj_codeseg(%s,%x)\n",name,suffix);

    sfx = (suffix) ? "_TEXT" : NULL;

    if (!name)                          // returning to default code segment
    {
        if (cseg != CODE)               // not the current default
        {
            SegData[cseg]->SDoffset = Coffset;
            Coffset = SegData[CODE]->SDoffset;
            cseg = CODE;
        }
        return cseg;
    }

    seg = elf_getsegment(name, sfx, SHT_PROGDEF, SHF_ALLOC|SHF_EXECINSTR, 4);
                                    // find or create code segment

    cseg = seg;                         // new code segment index
    Coffset = 0;

    return seg;
}

/*********************************
 * Define segments for Thread Local Storage.
 * Here's what the elf tls spec says:
 *      Field           .tbss                   .tdata
 *      sh_name         .tbss                   .tdata
 *      sh_type         SHT_NOBITS              SHT_PROGBITS
 *      sh_flags        SHF_ALLOC|SHF_WRITE|    SHF_ALLOC|SHF_WRITE|
 *                      SHF_TLS                 SHF_TLS
 *      sh_addr         virtual addr of section virtual addr of section
 *      sh_offset       0                       file offset of initialization image
 *      sh_size         size of section         size of section
 *      sh_link         SHN_UNDEF               SHN_UNDEF
 *      sh_info         0                       0
 *      sh_addralign    alignment of section    alignment of section
 *      sh_entsize      0                       0
 * We want _tlsstart and _tlsend to bracket all the D tls data.
 * The default linker script (ld -verbose) says:
 *  .tdata      : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
 *  .tbss       : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
 * so if we assign names:
 *      _tlsstart .tdata
 *      symbols   .tdata.
 *      symbols   .tbss
 *      _tlsend   .tbss.
 * this should work.
 * Don't care about sections emitted by other languages, as we presume they
 * won't be storing D gc roots in their tls.
 * Output:
 *      seg_tlsseg      set to segment number for TLS segment.
 * Returns:
 *      segment for TLS segment
 */

seg_data *obj_tlsseg()
{
    /* Ensure that ".tdata" precedes any other .tdata. section, as the ld
     * linker script fails to work right.
     */
    elf_getsegment(".tdata", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE|SHF_TLS, 4);

    static const char tlssegname[] = ".tdata.";
    //dbg_printf("obj_tlsseg(\n");

    if (seg_tlsseg == UNKNOWN)
    {
        seg_tlsseg = elf_getsegment(tlssegname, NULL, SHT_PROGDEF,
            SHF_ALLOC|SHF_WRITE|SHF_TLS, I64 ? 16 : 4);
    }
    return SegData[seg_tlsseg];
}


/*********************************
 * Define segments for Thread Local Storage.
 * Output:
 *      seg_tlsseg_bss  set to segment number for TLS segment.
 * Returns:
 *      segment for TLS segment
 */

seg_data *obj_tlsseg_bss()
{
    static const char tlssegname[] = ".tbss";
    //dbg_printf("obj_tlsseg_bss(\n");

    if (seg_tlsseg_bss == UNKNOWN)
    {
        seg_tlsseg_bss = elf_getsegment(tlssegname, NULL, SHT_NOBITS,
            SHF_ALLOC|SHF_WRITE|SHF_TLS, I64 ? 16 : 4);
    }
    return SegData[seg_tlsseg_bss];
}


/*******************************
 * Output an alias definition record.
 */

void obj_alias(const char *n1,const char *n2)
{
    dbg_printf("obj_alias(%s,%s)\n",n1,n2);
    assert(0);
#if NOT_DONE
    char *buffer = (char *) alloca(strlen(n1) + strlen(n2) + 2 * ONS_OHD);
    unsigned len = obj_namestring(buffer,n1);
    len += obj_namestring(buffer + len,n2);
    objrecord(ALIAS,buffer,len);
#endif
}

char *unsstr(unsigned value)
{
    static char buffer[64];

    sprintf(buffer, "%d", value);
    return buffer;
}

/*******************************
 * Mangle a name.
 * Returns:
 *      mangled name
 */

char *obj_mangle2(Symbol *s,char *dest)
{
    char *name;

    //dbg_printf("obj_mangle('%s'), mangle = x%x\n",s->Sident,type_mangle(s->Stype));
    symbol_debug(s);
    assert(dest);
#if SCPP
    name = CPP ? cpp_mangle(s) : s->Sident;
#elif MARS
    name = cpp_mangle(s);
#else
    name = s->Sident;
#endif
    size_t len = strlen(name);                 // # of bytes in name
    //dbg_printf("len %d\n",len);
    switch (type_mangle(s->Stype))
    {
        case mTYman_pas:                // if upper case
        case mTYman_for:
            if (len >= DEST_LEN)
                dest = (char *)mem_malloc(len + 1);
            memcpy(dest,name,len + 1);  // copy in name and ending 0
            for (int i = 0; 1; i++)
            {   char c = dest[i];
                if (!c)
                    break;
                if (c >= 'a' && c <= 'z')
                    dest[i] = c + 'A' - 'a';
            }
            break;
        case mTYman_std:
#if TARGET_LINUX || TARGET_FREEBSD || TARGET_SOLARIS
            if (tyfunc(s->ty()) && !variadic(s->Stype))
#else
            if (!(config.flags4 & CFG4oldstdmangle) &&
                config.exe == EX_NT && tyfunc(s->ty()) &&
                !variadic(s->Stype))
#endif
            {
                char *pstr = unsstr(type_paramsize(s->Stype));
                size_t pstrlen = strlen(pstr);
                size_t destlen = len + 1 + pstrlen + 1;

                if (destlen > DEST_LEN)
                    dest = (char *)mem_malloc(destlen);
                memcpy(dest,name,len);
                dest[len] = '@';
                memcpy(dest + 1 + len, pstr, pstrlen + 1);
                break;
            }
        case mTYman_cpp:
        case mTYman_c:
        case mTYman_d:
        case mTYman_sys:
        case 0:
            if (len >= DEST_LEN)
                dest = (char *)mem_malloc(len + 1);
            memcpy(dest,name,len+1);// copy in name and trailing 0
            break;

        default:
#ifdef DEBUG
            printf("mangling %x\n",type_mangle(s->Stype));
            symbol_print(s);
#endif
            printf("%d\n", type_mangle(s->Stype));
            assert(0);
    }
    //dbg_printf("\t %s\n",dest);
    return dest;
}

/*******************************
 * Export a function name.
 */

void obj_export(Symbol *s,unsigned argsize)
{
    //dbg_printf("obj_export(%s,%d)\n",s->Sident,argsize);
}

/*******************************
 * Update data information about symbol
 *      align for output and assign segment
 *      if not already specified.
 *
 * Input:
 *      sdata           data symbol
 *      datasize        output size
 *      seg             default seg if not known
 * Returns:
 *      actual seg
 */

int elf_data_start(Symbol *sdata, targ_size_t datasize, int seg)
{
    targ_size_t alignbytes;
    //dbg_printf("elf_data_start(%s,size %d,seg %d)\n",sdata->Sident,datasize,seg);
    //symbol_print(sdata);

    if (sdata->Sseg == UNKNOWN) // if we don't know then there
        sdata->Sseg = seg;      // wasn't any segment override
    else
        seg = sdata->Sseg;
    targ_size_t offset = Offset(seg);
    alignbytes = align(datasize, offset) - offset;
    if (alignbytes)
        obj_lidata(seg, offset, alignbytes);
    sdata->Soffset = offset + alignbytes;
    return seg;
}

/*******************************
 * Update function info before codgen
 *
 * If code for this function is in a different segment
 * than the current default in cseg, switch cseg to new segment.
 */

void elf_func_start(Symbol *sfunc)
{
    //dbg_printf("elf_func_start(%s)\n",sfunc->Sident);
    symbol_debug(sfunc);

    if ((tybasic(sfunc->ty()) == TYmfunc) && (sfunc->Sclass == SCextern))
    {                                   // create a new code segment
        sfunc->Sseg =
            elf_getsegment(".gnu.linkonce.t.", cpp_mangle(sfunc), SHT_PROGDEF, SHF_ALLOC|SHF_EXECINSTR,4);

    }
    else if (sfunc->Sseg == UNKNOWN)
        sfunc->Sseg = CODE;
    //dbg_printf("sfunc->Sseg %d CODE %d cseg %d Coffset %d\n",sfunc->Sseg,CODE,cseg,Coffset);
    cseg = sfunc->Sseg;
    assert(cseg == CODE || cseg > COMD);
    objpubdef(cseg, sfunc, Coffset);
    sfunc->Soffset = Coffset;

    if (config.fulltypes)
        dwarf_func_start(sfunc);
}

/*******************************
 * Update function info after codgen
 */

void elf_func_term(Symbol *sfunc)
{
    //dbg_printf("elf_func_term(%s) offset %x, Coffset %x symidx %d\n",
//          sfunc->Sident, sfunc->Soffset,Coffset,sfunc->Sxtrnnum);

    // fill in the function size
    if (I64)
        SymbolTable64[sfunc->Sxtrnnum].st_size = Coffset - sfunc->Soffset;
    else
        SymbolTable[sfunc->Sxtrnnum].st_size = Coffset - sfunc->Soffset;
    if (config.fulltypes)
        dwarf_func_term(sfunc);
}

/********************************
 * Output a public definition.
 * Input:
 *      seg =           segment index that symbol is defined in
 *      s ->            symbol
 *      offset =        offset of name within segment
 */

void objpubdef(int seg, Symbol *s, targ_size_t offset)
{
    int bind;
    switch (s->Sclass)
    {
        case SCglobal:
        case SCinline:
            bind = STB_GLOBAL;
            break;
        case SCcomdat:
        case SCcomdef:
            bind = STB_WEAK;
            break;
        default:
            bind = STB_LOCAL;
            break;
    }

#if 0
    //printf("\nobjpubdef(%d,%s,%d)\n",seg,s->Sident,offset);
    //symbol_print(s);
#endif

    symbol_debug(s);
    IDXSTR namidx = elf_addmangled(s);
    //printf("\tnamidx %d,section %d\n",namidx,MAP_SEG2SECIDX(seg));
    if (tyfunc(s->ty()))
    {
        s->Sxtrnnum = elf_addsym(namidx, offset, Offset(s->Sseg)-offset,
            STT_FUNC, bind,MAP_SEG2SECIDX(seg));
    }
    else
    {
        s->Sxtrnnum = elf_addsym(namidx, offset, type_size(s->Stype),
            (s->ty() & mTYthread) ? STT_TLS : STT_OBJECT,
            bind,MAP_SEG2SECIDX(seg));
    }
    fflush(NULL);
}

/*******************************
 * Output an external symbol for name.
 * Input:
 *      name    Name to do EXTDEF on
 *              (Not to be mangled)
 * Returns:
 *      Symbol table index of the definition
 *      NOTE: Numbers will not be linear.
 */

int objextern(const char *name)
{
    //dbg_printf("objextdef('%s')\n",name);
    assert(name);
    IDXSTR namidx = elf_addstr(symtab_strings,name);
    IDXSYM symidx = elf_addsym(namidx, 0, 0, STT_NOTYPE, STB_GLOBAL, SHT_UNDEF);
    return symidx;
}

int objextdef(const char *name)
{
    return objextern(name);
}

/*******************************
 * Output an external for existing symbol.
 * Input:
 *      s       Symbol to do EXTDEF on
 *              (Name is to be mangled)
 * Returns:
 *      Symbol table index of the definition
 *      NOTE: Numbers will not be linear.
 */

int objextern(Symbol *s)
{
    int symtype,sectype;
    unsigned size;

    //dbg_printf("objextern('%s') %x\n",s->Sident,s->Svalue);
    symbol_debug(s);
    IDXSTR namidx = elf_addmangled(s);

#if SCPP
    if (s->Sscope && !tyfunc(s->ty()))
    {
        symtype = STT_OBJECT;
        sectype = SHT_COMMON;
        size = type_size(s->Stype);
    }
    else
#endif
    {
        symtype = STT_NOTYPE;
        sectype = SHT_UNDEF;
        size = 0;
    }
    if (s->ty() & mTYthread)
    {
        //printf("objextern('%s') %x TLS\n",s->Sident,s->Svalue);
        symtype = STT_TLS;
    }

    s->Sxtrnnum = elf_addsym(namidx, size, size, symtype,
        /*(s->ty() & mTYweak) ? STB_WEAK : */STB_GLOBAL, sectype);
    return s->Sxtrnnum;

}

/*******************************
 * Output a common block definition.
 * Input:
 *      p ->    external identifier
 *      size    size in bytes of each elem
 *      count   number of elems
 * Returns:
 *      Symbol table index for symbol
 */

int obj_comdef(Symbol *s,targ_size_t size,targ_size_t count)
{
    //printf("obj_comdef('%s',%d,%d)\n",s->Sident,size,count);
    symbol_debug(s);

    int align = I64 ? 16 : 4;
    if (s->ty() & mTYthread)
    {
        s->Sseg = elf_getsegment(".tbss.", cpp_mangle(s),
                SHT_NOBITS, SHF_ALLOC|SHF_WRITE|SHF_TLS, align);
        s->Sfl = FLtlsdata;
        SegData[s->Sseg]->SDsym = s;
        SegData[s->Sseg]->SDoffset += size * count;
        objpubdef(s->Sseg, s, 0);
        searchfixlist(s);
        return s->Sseg;
    }
    else
    {
        s->Sseg = elf_getsegment(".bss.", cpp_mangle(s),
                SHT_NOBITS, SHF_ALLOC|SHF_WRITE, align);
        s->Sfl = FLudata;
        SegData[s->Sseg]->SDsym = s;
        SegData[s->Sseg]->SDoffset += size * count;
        objpubdef(s->Sseg, s, 0);
        searchfixlist(s);
        return s->Sseg;
    }
#if 0
    IDXSTR namidx = elf_addmangled(s);
    alignOffset(UDATA,size);
    IDXSYM symidx = elf_addsym(namidx, SegData[UDATA]->SDoffset, size*count,
                    (s->ty() & mTYthread) ? STT_TLS : STT_OBJECT,
                    STB_WEAK, SHI_BSS);
    //dbg_printf("\tobj_comdef returning symidx %d\n",symidx);
    s->Sseg = UDATA;
    s->Sfl = FLudata;
    SegData[UDATA]->SDoffset += size * count;
    return symidx;
#endif
}

int obj_comdef(Symbol *s, int flag, targ_size_t size, targ_size_t count)
{
    return obj_comdef(s, size, count);
}

/***************************************
 * Append an iterated data block of 0s.
 * (uninitialized data only)
 */

void obj_write_zeros(seg_data *pseg, targ_size_t count)
{
    obj_lidata(pseg->SDseg, pseg->SDoffset, count);
}

/***************************************
 * Output an iterated data block of 0s.
 *
 *      For boundary alignment and initialization
 */

void obj_lidata(int seg,targ_size_t offset,targ_size_t count)
{
    //printf("obj_lidata(%d,%x,%d)\n",seg,offset,count);
    if (seg == UDATA || seg == UNKNOWN)
    {   // Use SDoffset to record size of .BSS section
        SegData[UDATA]->SDoffset += count;
    }
    else if (MAP_SEG2SEC(seg)->sh_type == SHT_NOBITS)
    {   // Use SDoffset to record size of .TBSS section
        SegData[seg]->SDoffset += count;
    }
    else
    {
        obj_bytes(seg, offset, count, NULL);
    }
}

/***********************************
 * Append byte to segment.
 */

void obj_write_byte(seg_data *pseg, unsigned byte)
{
    obj_byte(pseg->SDseg, pseg->SDoffset, byte);
}

/************************************
 * Output byte to object file.
 */

void obj_byte(int seg,targ_size_t offset,unsigned byte)
{
    Outbuffer *buf = SegData[seg]->SDbuf;
    int save = buf->size();
    //dbg_printf("obj_byte(seg=%d, offset=x%lx, byte=x%x)\n",seg,offset,byte);
    buf->setsize(offset);
    buf->writeByte(byte);
    if (save > offset+1)
        buf->setsize(save);
    SegData[seg]->SDoffset = offset+1;
    //dbg_printf("\tsize now %d\n",buf->size());
}

/***********************************
 * Append bytes to segment.
 */

void obj_write_bytes(seg_data *pseg, unsigned nbytes, void *p)
{
    obj_bytes(pseg->SDseg, pseg->SDoffset, nbytes, p);
}

/************************************
 * Output bytes to object file.
 * Returns:
 *      nbytes
 */

unsigned obj_bytes(int seg, targ_size_t offset, unsigned nbytes, void *p)
{
#if 0
    if (!(seg >= 0 && seg <= seg_count))
    {   printf("obj_bytes: seg = %d, seg_count = %d\n", seg, seg_count);
        *(char*)0=0;
    }
#endif
    assert(seg >= 0 && seg <= seg_count);
    Outbuffer *buf = SegData[seg]->SDbuf;
    if (buf == NULL)
    {
        //dbg_printf("obj_bytes(seg=%d, offset=x%lx, nbytes=%d, p=x%x)\n", seg, offset, nbytes, p);
        //raise(SIGSEGV);
        assert(buf != NULL);
    }
    int save = buf->size();
    //dbg_printf("obj_bytes(seg=%d, offset=x%lx, nbytes=%d, p=x%x)\n",
            //seg,offset,nbytes,p);
    buf->setsize(offset);
    buf->reserve(nbytes);
    if (p)
    {
        buf->writen(p,nbytes);
    }
    else
    {   // Zero out the bytes
        buf->clearn(nbytes);
    }
    if (save > offset+nbytes)
        buf->setsize(save);
    SegData[seg]->SDoffset = offset+nbytes;
    return nbytes;
}

/*******************************
 * Output a relocation entry for a segment
 * Input:
 *      seg =           where the address is going
 *      offset =        offset within seg
 *      type =          ELF relocation type RI_TYPE_XXXX
 *      index =         Related symbol table index
 *      val =           addend or displacement from address
 */

int relcnt=0;

void elf_addrel(int seg, targ_size_t offset, unsigned type,
                                        IDXSYM symidx, targ_size_t val)
{
    seg_data *segdata;
    Outbuffer *buf;
    IDXSEC secidx;

    //assert(val == 0);
    relcnt++;
    //dbg_printf("%d-elf_addrel(seg %d,offset x%x,type x%x,symidx %d,val %d)\n",
            //relcnt,seg, offset, type, symidx,val);

    assert(seg >= 0 && seg <= seg_count);
    segdata = SegData[seg];
    secidx = MAP_SEG2SECIDX(seg);
    assert(secidx != 0);

    if (segdata->SDrel == NULL)
        segdata->SDrel = new Outbuffer();
    if (segdata->SDrel->size() == 0)
    {   IDXSEC relidx;

        if (secidx == SHI_TEXT)
            relidx = SHI_RELTEXT;
        else if (secidx == SHI_DATA)
            relidx = SHI_RELDATA;
        else
        {
            // Get the section name, and make a copy because
            // elf_newsection() may reallocate the string buffer.
            char *section_name = (char *)GET_SECTION_NAME(secidx);
            int len = strlen(section_name) + 1;
            char *p = (char *)alloca(len);
            memcpy(p, section_name, len);

            relidx = elf_newsection(I64 ? ".rela" : ".rel", p, I64 ? SHT_RELA : SHT_REL, 0);
            segdata->SDrelidx = relidx;
        }

        if (I64)
        {
            /* Note that we're using Elf32_Shdr here instead of Elf64_Shdr. This is to make
             * the code a bit simpler. In obj_term(), we translate the Elf32_Shdr into the proper
             * Elf64_Shdr.
             */
            Elf32_Shdr *relsec = &SecHdrTab[relidx];
            relsec->sh_link = SHI_SYMTAB;
            relsec->sh_info = secidx;
            relsec->sh_entsize = sizeof(Elf64_Rela);
            relsec->sh_addralign = 8;
        }
        else
        {
            Elf32_Shdr *relsec = &SecHdrTab[relidx];
            relsec->sh_link = SHI_SYMTAB;
            relsec->sh_info = secidx;
            relsec->sh_entsize = sizeof(Elf32_Rel);
            relsec->sh_addralign = 4;
        }
    }

    if (I64)
    {
        Elf64_Rela rel;
        rel.r_offset = offset;          // build relocation information
        rel.r_info = ELF64_R_INFO(symidx,type);
        rel.r_addend = val;
        buf = segdata->SDrel;
        buf->write(&rel,sizeof(rel));
        segdata->SDrelcnt++;

        if (offset >= segdata->SDrelmaxoff)
            segdata->SDrelmaxoff = offset;
        else
        {   // insert numerically
            Elf64_Rela *relbuf = (Elf64_Rela *)buf->buf;
            int i = relbuf[segdata->SDrelindex].r_offset > offset ? 0 : segdata->SDrelindex;
            while (i < segdata->SDrelcnt)
            {
                if (relbuf[i].r_offset > offset)
                    break;
                i++;
            }
            assert(i != segdata->SDrelcnt);     // slide greater offsets down
            memmove(relbuf+i+1,relbuf+i,sizeof(Elf64_Rela) * (segdata->SDrelcnt - i - 1));
            *(relbuf+i) = rel;          // copy to correct location
            segdata->SDrelindex = i;    // next entry usually greater
        }
    }
    else
    {
        Elf32_Rel rel;
        rel.r_offset = offset;          // build relocation information
        rel.r_info = ELF32_R_INFO(symidx,type);
        buf = segdata->SDrel;
        buf->write(&rel,sizeof(rel));
        segdata->SDrelcnt++;

        if (offset >= segdata->SDrelmaxoff)
            segdata->SDrelmaxoff = offset;
        else
        {   // insert numerically
            Elf32_Rel *relbuf = (Elf32_Rel *)buf->buf;
            int i = relbuf[segdata->SDrelindex].r_offset > offset ? 0 : segdata->SDrelindex;
            while (i < segdata->SDrelcnt)
            {
                if (relbuf[i].r_offset > offset)
                    break;
                i++;
            }
            assert(i != segdata->SDrelcnt);     // slide greater offsets down
            memmove(relbuf+i+1,relbuf+i,sizeof(Elf32_Rel) * (segdata->SDrelcnt - i - 1));
            *(relbuf+i) = rel;          // copy to correct location
            segdata->SDrelindex = i;    // next entry usually greater
        }
    }
}

/*******************************
 * Refer to address that is in the data segment.
 * Input:
 *      seg =           where the address is going
 *      offset =        offset within seg
 *      val =           displacement from address
 *      targetdatum =   DATA, CDATA or UDATA, depending where the address is
 *      flags =         CFoff, CFseg
 * Example:
 *      int *abc = &def[3];
 *      to allocate storage:
 *              reftodatseg(DATA,offset,3 * sizeof(int *),UDATA);
 */

void reftodatseg(int seg,targ_size_t offset,targ_size_t val,
        unsigned targetdatum,int flags)
{
    Outbuffer *buf;
    int save;

    buf = SegData[seg]->SDbuf;
    save = buf->size();
    buf->setsize(offset);
#if 0
    printf("reftodatseg(seg=%d, offset=x%llx, val=x%llx,data %x, flags %x)\n",
        seg,(unsigned long long)offset,(unsigned long long)val,targetdatum,flags);
#endif
    /*if (OPT_IS_SET(OPTfwritable_strings))
    {
        elf_addrel(seg,offset,RI_TYPE_SYM32,STI_DATA,0);
    }
    else*/
    {
        int relinfo;
        targ_size_t v = 0;

        if (I64)
        {
            if (MAP_SEG2TYP(seg) == CODE && config.flags3 & CFG3pic)
            {   relinfo = R_X86_64_PC32;
                //v = -4L;
            }
            else if (MAP_SEG2SEC(targetdatum)->sh_flags & SHF_TLS)
                relinfo = config.flags3 & CFG3pic ? R_X86_64_TLSGD : R_X86_64_TPOFF32;
            else
                relinfo = R_X86_64_32;
        }
        else
        {
            if (MAP_SEG2TYP(seg) == CODE && config.flags3 & CFG3pic)
                relinfo = RI_TYPE_GOTOFF;
            else if (MAP_SEG2SEC(targetdatum)->sh_flags & SHF_TLS)
                relinfo = config.flags3 & CFG3pic ? RI_TYPE_TLS_GD : RI_TYPE_TLS_LE;
            else
                relinfo = RI_TYPE_SYM32;
        }

        elf_addrel(seg, offset, relinfo, STI_RODAT, v);
    }
    buf->write32(val);
    if (save > offset + 4)
        buf->setsize(save);
}

/*******************************
 * Refer to address that is in the code segment.
 * Only offsets are output, regardless of the memory model.
 * Used to put values in switch address tables.
 * Input:
 *      seg =           where the address is going (CODE or DATA)
 *      offset =        offset within seg
 *      val =           displacement from start of this module
 */

void reftocodseg(int seg,targ_size_t offset,targ_size_t val)
{
    Outbuffer *buf;
    int save;
    int segtyp = MAP_SEG2TYP(seg);

    //dbg_printf("reftocodseg(seg=%d, offset=x%lx, val=x%lx )\n",seg,offset,val);
    assert(seg > 0);            // COMDATs not done yet
    buf = SegData[seg]->SDbuf;
    save = buf->size();
    buf->setsize(offset);
#if 0
    if (segtyp == CODE)
    {
        val = val - funcsym_p->Soffset;
        elf_addrel(seg,offset,RI_TYPE_PC32,funcsym_p->Sxtrnnum,0);
    }
    else
#endif
    {
        val = val - funcsym_p->Soffset;
        int relinfo;
        targ_size_t v = 0;
        if (I64)
            relinfo = (config.flags3 & CFG3pic) ? R_X86_64_PC32 : R_X86_64_32;
        else
            relinfo = (config.flags3 & CFG3pic) ? RI_TYPE_GOTOFF : RI_TYPE_SYM32;
        elf_addrel(seg,offset, relinfo, funcsym_p->Sxtrnnum, v);
    }
    buf->write32(val);
    if (save > offset + 4)
        buf->setsize(save);
}

/*******************************
 * Refer to an identifier.
 * Input:
 *      segtyp =        where the address is going (CODE or DATA)
 *      offset =        offset within seg
 *      s ->            Symbol table entry for identifier
 *      val =           displacement from identifier
 *      flags =         CFselfrel: self-relative
 *                      CFseg: get segment
 *                      CFoff: get offset
 *                      CFoffset64: 64 bit fixup
 * Returns:
 *      number of bytes in reference (4 or 8)
 */

int reftoident(int seg, targ_size_t offset, Symbol *s, targ_size_t val,
        int flags)
{
    tym_t ty;
    bool external = TRUE;
    Outbuffer *buf;
    elf_u32_f32 relinfo,refseg;
    int segtyp = MAP_SEG2TYP(seg);
    //assert(val == 0);
    int retsize = (flags & CFoffset64) ? 8 : 4;
    targ_size_t v = 0;

#if 0
    printf("\nreftoident('%s' seg %d, offset x%llx, val x%llx, flags x%x)\n",
        s->Sident,seg,offset,val,flags);
    dbg_printf("Sseg = %d, Sxtrnnum = %d, retsize = %d\n",s->Sseg,s->Sxtrnnum,retsize);
    symbol_print(s);
#endif

    ty = s->ty();
    if (s->Sxtrnnum)
    {                           // identifier is defined somewhere else
        if (I64)
        {
            if (SymbolTable64[s->Sxtrnnum].st_shndx != SHT_UNDEF)
                external = FALSE;
        }
        else
        {
            if (SymbolTable[s->Sxtrnnum].st_shndx != SHT_UNDEF)
                external = FALSE;
        }
    }

    switch (s->Sclass)
    {
        case SClocstat:
            buf = SegData[seg]->SDbuf;
            if (I64)
            {
                if (s->Sfl == FLtlsdata)
                    relinfo = config.flags3 & CFG3pic ? R_X86_64_TLSGD : R_X86_64_TPOFF32;
                else
                {   relinfo = config.flags3 & CFG3pic ? R_X86_64_PC32 : R_X86_64_32;
                    if (config.flags3 & CFG3pic)
                        v = -4L;
                }
            }
            else
            {
                if (s->Sfl == FLtlsdata)
                    relinfo = config.flags3 & CFG3pic ? RI_TYPE_TLS_GD : RI_TYPE_TLS_LE;
                else
                    relinfo = config.flags3 & CFG3pic ? RI_TYPE_GOTOFF : RI_TYPE_SYM32;
            }
            if (flags & CFoffset64 && relinfo == R_X86_64_32)
            {
                relinfo = R_X86_64_64;
            }
            elf_addrel(seg,offset,relinfo,STI_RODAT,v);
            if (retsize == 8)
                buf->write64(val + s->Soffset);
            else
                buf->write32(val + s->Soffset);
            break;

        case SCcomdat:
        case_SCcomdat:
        case SCstatic:
#if 0
            if ((s->Sflags & SFLthunk) && s->Soffset)
            {                   // A thunk symbol that has be defined
                assert(s->Sseg == seg);
                val = (s->Soffset+val) - (offset+4);
                goto outaddrval;
            }
            // FALL_THROUGH
#endif

        case SCextern:
        case SCcomdef:
        case_extern:
        case SCglobal:
            if (!s->Sxtrnnum)
            {   // not in symbol table yet - class might change
                //dbg_printf("\tadding %s to fixlist\n",s->Sident);
                addtofixlist(s,offset,seg,val,flags);
                return retsize;
            }
            else
            {
                int save;
                buf = SegData[seg]->SDbuf;
                save = buf->size();
                buf->setsize(offset);
                if (flags & CFselfrel)
                {               // only for function references within code segments
                    if (!external &&            // local definition found
                         s->Sseg == seg &&      // within same code segment
                          (!(config.flags3 & CFG3pic) ||        // not position indp code
                           s->Sclass == SCstatic)) // or is pic, but declared static
                    {                   // Can use PC relative
                        //dbg_printf("\tdoing PC relative\n");
                        val = (s->Soffset+val) - (offset+4);
                    }
                    else
                    {
                        //dbg_printf("\tadding relocation\n");
                        if (I64)
                        {   relinfo = config.flags3 & CFG3pic ?  R_X86_64_PLT32 : R_X86_64_PC32;
                            elf_addrel(seg,offset, relinfo, s->Sxtrnnum, -4);
                            val = 0;
                        }
                        else
                        {   relinfo = config.flags3 & CFG3pic ?  RI_TYPE_PLT32 : RI_TYPE_PC32;
                            elf_addrel(seg,offset, relinfo, s->Sxtrnnum, 0);
                            val = (targ_size_t)-4;
                        }
                    }
                }
                else
                {       // code to code code to data, data to code, data to data refs
                    refseg = s->Sxtrnnum;       // default to name symbol table entry
                    if (s->Sclass == SCstatic)
                    {                           // offset into .data or .bss seg
                        refseg = MAP_SEG2SYMIDX(s->Sseg);
                                                // use segment symbol table entry
                        val += s->Soffset;
                        if (!(config.flags3 & CFG3pic) ||       // all static refs from normal code
                             segtyp == DATA)    // or refs from data from posi indp
                        {
                           relinfo = I64 ? R_X86_64_32 : RI_TYPE_SYM32;
                        }
                        else
                        {
                            relinfo = I64 ? R_X86_64_PC32 : RI_TYPE_GOTOFF;
                        }
                    }
                    else if (config.flags3 & CFG3pic && s == GOTsym)
                    {                   // relocation for Gbl Offset Tab
                        relinfo =  I64 ? R_X86_64_NONE : RI_TYPE_GOTPC;
                    }
                    else if (segtyp == DATA)
                    {                   // relocation from within DATA seg
                        relinfo = I64 ? R_X86_64_32 : RI_TYPE_SYM32;
                    }
                    else
                    {                   // relocation from within CODE seg
                        if (I64)
                        {   if (config.flags3 & CFG3pic)
                                relinfo = R_X86_64_GOTPCREL;
                            else
                                relinfo = (flags & CFpc32) ? R_X86_64_PC32 : R_X86_64_32;
                        }
                        else
                            relinfo = config.flags3 & CFG3pic ? RI_TYPE_GOT32 : RI_TYPE_SYM32;
                    }
                    if ((s->ty() & mTYLINK) & mTYthread)
                    {
                        if (I64)
                        {
                            if (config.flags3 & CFG3pic)
                            {
                                if (s->Sclass == SCstatic || s->Sclass == SClocstat)
                                    relinfo = R_X86_64_TLSGD;  // TLS_GD?
                                else
                                    relinfo = R_X86_64_TLSGD;
                            }
                            else
                            {
                                if (s->Sclass == SCstatic || s->Sclass == SClocstat)
                                    relinfo = R_X86_64_TPOFF32;
                                else
                                    relinfo = R_X86_64_GOTTPOFF;
                            }
                        }
                        else
                        {
                            if (config.flags3 & CFG3pic)
                            {
                                if (s->Sclass == SCstatic)
                                    relinfo = RI_TYPE_TLS_LE;  // TLS_GD?
                                else
                                    relinfo = RI_TYPE_TLS_IE;
                            }
                            else
                            {
                                if (s->Sclass == SCstatic)
                                    relinfo = RI_TYPE_TLS_LE;
                                else
                                    relinfo = RI_TYPE_TLS_IE;
                            }
                        }
                    }
                    if (flags & CFoffset64 && relinfo == R_X86_64_32)
                    {
                        relinfo = R_X86_64_64;
                    }
                    //printf("\t\t************* adding relocation\n");
                    targ_size_t v = (relinfo == R_X86_64_PC32) ? -4 : 0;
                    elf_addrel(seg,offset,relinfo,refseg,v);
                }
outaddrval:
                if (retsize == 8)
                    buf->write64(val);
                else
                    buf->write32(val);
                if (save > offset + retsize)
                    buf->setsize(save);
            }
            break;

        case SCsinline:
        case SCeinline:
            printf ("Undefined inline value <<fixme>>\n");
            //warerr(WM_undefined_inline,s->Sident);
        case SCinline:
            if (tyfunc(ty))
            {
                s->Sclass = SCextern;
                goto case_extern;
            }
            else if (config.flags2 & CFG2comdat)
                goto case_SCcomdat;     // treat as initialized common block

        default:
#ifdef DEBUG
            //symbol_print(s);
#endif
            assert(0);
    }
    return retsize;
}

/*****************************************
 * Generate far16 thunk.
 * Input:
 *      s       Symbol to generate a thunk for
 */

void obj_far16thunk(Symbol *s)
{
    //dbg_printf("obj_far16thunk('%s')\n", s->Sident);
    assert(0);
}

/**************************************
 * Mark object file as using floating point.
 */

void obj_fltused()
{
    //dbg_printf("obj_fltused()\n");
}

/************************************
 * Close and delete .OBJ file.
 */

void objfile_delete()
{
    //remove(fobjname); // delete corrupt output file
}

/**********************************
 * Terminate.
 */

void objfile_term()
{
#if TERMCODE
    mem_free(fobjname);
    fobjname = NULL;
#endif
}

/**********************************
  * Write to the object file
  */
void objfile_write(FILE *fd, void *buffer, unsigned len)
{
    fobjbuf->write(buffer, len);
}

long elf_align(FILE *fd, targ_size_t size,long foffset)
{
    long offset;
    switch (size)
    {
        case 0:
        case 1:
            return foffset;
        case 4:
            offset = (foffset + 3) & ~3;
            break;
        case 8:
            offset = (foffset + 7) & ~7;
            break;
        case 16:
            offset = (foffset + 15) & ~15;
            break;
        case 32:
            offset = (foffset + 31) & ~31;
            break;
        default:
            dbg_printf("size was %d\n",(int)size);
            assert(0);
            break;
    }
    if (offset > foffset)
        fobjbuf->writezeros(offset - foffset);
    return offset;
}

/***************************************
 * Stuff pointer to ModuleInfo in its own segment.
 */

#if MARS

void obj_moduleinfo(Symbol *scc)
{
//    if (I64) return;          // for now, until Phobos64 works

    int codeOffset, refOffset;

    /* Put in the ModuleReference. */
    {
        /* struct ModuleReference
         * {
         *      void*   next;
         *      ModuleReference* module;
         * }
         */
        const int seg = DATA;
        alignOffset(seg, NPTRSIZE);
        SegData[seg]->SDoffset = SegData[seg]->SDbuf->size();
        refOffset = SegData[seg]->SDoffset;
        SegData[seg]->SDbuf->writezeros(NPTRSIZE);
        SegData[seg]->SDoffset += NPTRSIZE;
        SegData[seg]->SDoffset += reftoident(seg, SegData[seg]->SDoffset, scc, 0, CFoffset64 | CFoff);
    }

    /* Constructor that links the ModuleReference to the head of
     * the list pointed to by _Dmoduleref
     */
    {
        /*      ret
         * codeOffset:
         *      pushad
         *      mov     EAX,&ModuleReference
         *      mov     ECX,_DmoduleRef
         *      mov     EDX,[ECX]
         *      mov     [EAX],EDX
         *      mov     [ECX],EAX
         *      popad
         *      ret
         */

        const int reltype = I64 ? R_X86_64_32 : RI_TYPE_SYM32;
        const int refsize = 4;                          // use 32 bit relocations, even in 64 bit mode

        const int seg = CODE;
        Outbuffer *buf = SegData[seg]->SDbuf;
        SegData[seg]->SDoffset = buf->size();
        codeOffset = SegData[seg]->SDoffset + 1;
        buf->writeByte(0xC3); /* ret */

        int off = 0;
        if (I32)
        {   buf->writeByte(0x60); // PUSHAD
            off = 1;
        }

        if (I64 && config.flags3 & CFG3pic)
        {   // LEA RAX,ModuleReference[RIP]
            buf->writeByte(REX | REX_W);
            buf->writeByte(0x8D);
            buf->writeByte(modregrm(0,AX,5));
            buf->write32(refOffset);
            elf_addrel(seg, codeOffset + off + 3, R_X86_64_PC32, STI_DATA, -4);

            // LEA RCX,_DmoduleRef[RIP]
            buf->writeByte(REX | REX_W);
            buf->writeByte(0x8D);
            buf->writeByte(modregrm(0,CX,5));
            buf->write32(0);
            elf_addrel(seg, codeOffset + off + 10, R_X86_64_PC32, objextern("_Dmodule_ref"), -4);
        }
        else
        {
            /* movl ModuleReference*, %eax */
            buf->writeByte(0xB8);
            buf->write32(refOffset);
            elf_addrel(seg, codeOffset + off + 1, reltype, STI_DATA, 0);

            /* movl _Dmodule_ref, %ecx */
            buf->writeByte(0xB9);
            buf->write32(0);
            elf_addrel(seg, codeOffset + off + 6, reltype, objextern("_Dmodule_ref"), 0);
        }

        buf->writeByte(0x8B); buf->writeByte(0x11); /* movl (%ecx), %edx */
        buf->writeByte(0x89); buf->writeByte(0x10); /* movl %edx, (%eax) */
        buf->writeByte(0x89); buf->writeByte(0x01); /* movl %eax, (%ecx) */

        if (I32) buf->writeByte(0x61); // POPAD
        buf->writeByte(0xC3); /* ret */
        SegData[seg]->SDoffset = buf->size();
    }

    /* Add reference to constructor into ".ctors" segment
     */
    const int seg = elf_getsegment(".ctors", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE, NPTRSIZE);

    Outbuffer *buf = SegData[seg]->SDbuf;
    buf->write32(codeOffset);
    elf_addrel(seg, SegData[seg]->SDoffset, I64 ? R_X86_64_64 : RI_TYPE_SYM32, STI_TEXT, 0);
    SegData[seg]->SDoffset += NPTRSIZE;
}

#endif

/*************************************
 */

void elfobj_gotref(symbol *s)
{
    //printf("elfobj_gotref(%x '%s', %d)\n",s,s->Sident, s->Sclass);
    switch(s->Sclass)
    {
        case SCstatic:
        case SClocstat:
            s->Sfl = FLgotoff;
            break;

        case SCextern:
        case SCglobal:
        case SCcomdat:
        case SCcomdef:
            s->Sfl = FLgot;
            break;

        default:
            break;
    }
}

#endif
#endif
