text: /**
   * Compiler implementation of the
   * $(LINK2 http://www.dlang.org, D programming language).
   *
   * Copyright:   Copyright (C) 1984-1998 by Symantec
   *              Copyright (C) 2000-2018 by The D Language Foundation, All Rights Reserved
   * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
   * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
   * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/backend/cgobj.c, backend/cgobj.c)
   */
  
  #if !HTOD && (SCPP || MARS)
  
  #include        <stdio.h>
  #include        <string.h>
  #include        <stdlib.h>
  #include        <malloc.h>
  #include        <ctype.h>
  #include        <direct.h>
  
  #include        "filespec.h"
  
  #include        "cc.h"
  #include        "global.h"
  #include        "cgcv.h"
  #include        "code.h"
  #include        "type.h"
  #include        "outbuf.h"
  
  #include        "md5.h"
  
  #if MARS
  #include        "varstats.h"
  
  struct Loc
  {
      char *filename;
      unsigned linnum;
      unsigned charnum;
  
      Loc(int y, int x)
      {
          linnum = y;
          charnum = x;
          filename = NULL;
      }
  };
  
  void error(Loc loc, const char *format, ...);
  #endif
  
  #if MARS
  // C++ name mangling is handled by front end
  #define cpp_mangle(s) ((s)->Sident)
  #endif
  
  #if TARGET_WINDOS
  
  static char __file__[] = __FILE__;      // for tassert.h
  #include        "tassert.h"
  
  #define MULTISCOPE      1       /* account for bug in MultiScope debugger
                                     where it cannot handle a line number
                                     with multiple offsets. We use a bit vector
                                     to filter out the extra offsets.
                                   */
  
  #define TOOFFSET(a,b)   (I32 ? TOLONG(a,b) : TOWORD(a,b))
  
  /**************************
   * Record types:
   */
  
  enum
  {
      RHEADR  = 0x6E,
      REGINT  = 0x70,
      REDATA  = 0x72,
      RIDATA  = 0x74,
      OVLDEF  = 0x76,
      ENDREC  = 0x78,
      BLKDEF  = 0x7A,
      BLKEND  = 0x7C,
  //  DEBSYM  = 0x7E,
      THEADR  = 0x80,
      LHEADR  = 0x82,
      PEDATA  = 0x84,
      PIDATA  = 0x86,
      COMENT  = 0x88,
      MODEND  = 0x8A,
      EXTDEF  = 0x8C,
      TYPDEF  = 0x8E,
      PUBDEF  = 0x90,
      PUB386  = 0x91,
      LOCSYM  = 0x92,
      LINNUM  = 0x94,
      LNAMES  = 0x96,
      SEGDEF  = 0x98,
      SEG386  = 0x99,
      GRPDEF  = 0x9A,
      FIXUPP  = 0x9C,
      FIX386  = 0x9D,
      LEDATA  = 0xA0,
      LED386  = 0xA1,
      LIDATA  = 0xA2,
      LID386  = 0xA3,
      LIBHED  = 0xA4,
      LIBNAM  = 0xA6,
      LIBLOC  = 0xA8,
      LIBDIC  = 0xAA,
      COMDEF  = 0xB0,
      LEXTDEF = 0xB4,
      LPUBDEF = 0xB6,
      LCOMDEF = 0xB8,
      CEXTDEF = 0xBC,
      COMDAT  = 0xC2,
      LINSYM  = 0xC4,
      ALIAS   = 0xC6,
      LLNAMES = 0xCA,
  };
  
  // Some definitions for .OBJ files. Trial and error to determine which
  // one to use when. Page #s refer to Intel spec on .OBJ files.
  
  // Values for LOCAT byte: (pg. 71)
  enum
  {
      LOCATselfrel            = 0x8000,
      LOCATsegrel             = 0xC000,
  
  // OR'd with one of the following:
      LOClobyte               = 0x0000,
      LOCbase                 = 0x0800,
      LOChibyte               = 0x1000,
      LOCloader_resolved      = 0x1400,
  
  // Unfortunately, the fixup stuff is different for EASY OMF and Microsoft
      EASY_LOCoffset          = 0x1400,          // 32 bit offset
      EASY_LOCpointer         = 0x1800,          // 48 bit seg/offset
  
      LOC32offset             = 0x2400,
      LOC32tlsoffset          = 0x2800,
      LOC32pointer            = 0x2C00,
  
      LOC16offset             = 0x0400,
      LOC16pointer            = 0x0C00,
  
      LOCxx                   = 0x3C00
  };
  
  // FDxxxx are constants for the FIXDAT byte in fixup records (pg. 72)
  
  enum
  {
      FD_F0 = 0x00,            // segment index
      FD_F1 = 0x10,            // group index
      FD_F2 = 0x20,            // external index
      FD_F4 = 0x40,            // canonic frame of LSEG that contains Location
      FD_F5 = 0x50,            // Target determines the frame
  
      FD_T0 = 0,               // segment index
      FD_T1 = 1,               // group index
      FD_T2 = 2,               // external index
      FD_T4 = 4,               // segment index, 0 displacement
      FD_T5 = 5,               // group index, 0 displacement
      FD_T6 = 6,               // external index, 0 displacement
  };
  
  /***************
   * Fixup list.
   */
  
  struct FIXUP
  {
      struct FIXUP        *FUnext;
      targ_size_t         FUoffset;       // offset from start of ledata
      unsigned short      FUlcfd;         // LCxxxx | FDxxxx
      unsigned short      FUframedatum;
      unsigned short      FUtargetdatum;
  };
  
  #define list_fixup(fl)  ((struct FIXUP *)list_ptr(fl))
  
  #define seg_is_comdat(seg) ((seg) < 0)
  
  /*****************************
   * Ledata records
   */
  
  #define LEDATAMAX (1024-14)
  
  struct Ledatarec
  {
      char header[14];                    // big enough to handle COMDAT header
      char data[LEDATAMAX];
      int lseg;                           // segment value
      unsigned i;                         // number of bytes in data
      targ_size_t offset;                 // segment offset of start of data
      struct FIXUP *fixuplist;            // fixups for this ledata
  
      // For COMDATs
      unsigned char flags;                // flags byte of COMDAT
      unsigned char alloctyp;             // allocation type of COMDAT
      unsigned char align;                // align type
      int typidx;
      int pubbase;
      int pubnamidx;
  };
  
  /*****************************
   * For defining segments.
   */
  
  #define SEG_ATTR(A,C,B,P)       (((A) << 5) | ((C) << 2) | ((B) << 1) | (P))
  
  enum
  {
  // Segment alignment A
      SEG_ALIGN0    = 0,       // absolute segment
      SEG_ALIGN1    = 1,       // byte align
      SEG_ALIGN2    = 2,       // word align
      SEG_ALIGN16   = 3,       // paragraph align
      SEG_ALIGN4K   = 4,       // 4Kb page align
      SEG_ALIGN4    = 5,       // dword align
  
  // Segment combine types C
      SEG_C_ABS     = 0,
      SEG_C_PUBLIC  = 2,
      SEG_C_STACK   = 5,
      SEG_C_COMMON  = 6,
  
  // Segment type P
      USE16 = 0,
      USE32 = 1,
  
      USE32_CODE    = (4+2),          // use32 + execute/read
      USE32_DATA    = (4+3),          // use32 + read/write
  };
  
  /*****************************
   * Line number support.
   */
  
  #define LINNUMMAX       512
  
  struct Linnum
  {
  #if MARS
          const char *filename;   // source file name
  #else
          Sfile *filptr;          // file pointer
  #endif
          int cseg;               // our internal segment number
          int seg;                // segment/public index
          int i;                  // used in data[]
          char data[LINNUMMAX];   // linnum/offset data
  };
  
  #define LINRECMAX       (2 + 255 * 2)   // room for 255 line numbers
  
  /************************************
   * State of object file.
   */
  
  struct Objstate
  {
      const char *modname;
      char *csegname;
      Outbuffer *buf;     // output buffer
  
      int fdsegattr;      // far data segment attribute
      int csegattr;       // code segment attribute
  
      int lastfardatasegi;        // SegData[] index of last far data seg
  
      int LOCoffset;
      int LOCpointer;
  
      int mlidata;
      int mpubdef;
      int mfixupp;
      int mmodend;
  
      int lnameidx;               // index of next LNAMES record
      int segidx;                 // index of next SEGDEF record
      int extidx;                 // index of next EXTDEF record
      int pubnamidx;              // index of COMDAT public name index
      Outbuffer *reset_symbuf;    // Keep pointers to reset symbols
  
      Symbol *startaddress;       // if !NULL, then Symbol is start address
  
  #ifdef DEBUG
      int fixup_count;
  #endif
  
      size_t ledatai;             // max index used in ledatas[]
  
      // Line numbers
      list_t linnum_list;
      char *linrec;               // line number record
      unsigned linreci;           // index of next avail in linrec[]
      unsigned linrecheader;      // size of line record header
      unsigned linrecnum;         // number of line record entries
      list_t linreclist;          // list of line records
      int mlinnum;
      int recseg;
      int term;
  #if MULTISCOPE
      vec_t linvec;               // bit vector of line numbers used
      vec_t offvec;               // and offsets used
  #endif
  
      int fisegi;                 // SegData[] index of FI segment
  
  #if MARS
      int fmsegi;                 // SegData[] of FM segment
      int datrefsegi;             // SegData[] of DATA pointer ref segment
      int tlsrefsegi;             // SegData[] of TLS pointer ref segment
  
      Outbuffer *ptrref_buf;      // buffer for pointer references
  #endif
  
      int tlssegi;                // SegData[] of tls segment
      int fardataidx;
  
      char pubdata[1024];
      int pubdatai;
  
      char extdata[1024];
      int extdatai;
  
      // For Obj::far16thunk
      int code16segi;             // SegData[] index
      targ_size_t CODE16offset;
  
      int fltused;
      int nullext;
  };
  
  Ledatarec **ledatas;
  size_t ledatamax;
  
  seg_data **SegData;
  static int seg_count;
  static int seg_max;
  
  static Objstate obj;
  
  STATIC void obj_defaultlib();
  STATIC void objheader (char *csegname);
  STATIC char * objmodtoseg (const char *modname);
  STATIC void obj_browse_flush();
  STATIC int obj_newfarseg (targ_size_t size,int);
  STATIC void linnum_flush(void);
  STATIC void linnum_term(void);
  STATIC void objsegdef (int attr,targ_size_t size,int segnamidx,int classnamidx);
  STATIC void obj_modend();
  STATIC void objflush_pointerRefs();
  STATIC void objfixupp (struct FIXUP *);
  STATIC void outextdata();
  STATIC void outpubdata();
  STATIC Ledatarec *ledata_new(int seg,targ_size_t offset);
  static int generate_comdat(Obj* objmod, Symbol *s, bool is_readonly_comdat);
  
  
  /*******************************
   * Output an object file data record.
   * Input:
   *      rectyp  =       record type
   *      record  ->      the data
   *      reclen  =       # of bytes in record
   */
  
  void objrecord(unsigned rectyp,const char *record,unsigned reclen)
  {   Outbuffer *o = obj.buf;
  
      //printf("rectyp = x%x, record[0] = x%x, reclen = x%x\n",rectyp,record[0],reclen);
      o->reserve(reclen + 4);
      o->writeByten(rectyp);
      o->writeWordn(reclen + 1);  // record length includes checksum
      o->writen(record,reclen);
      o->writeByten(0);           // use 0 for checksum
  }
  
  
  /**************************
   * Insert an index number.
   * Input:
   *      p -> where to put the 1 or 2 byte index
   *      index = the 15 bit index
   * Returns:
   *      # of bytes stored
   */
  
  extern void error(const char *filename, unsigned linnum, unsigned charnum, const char *format, ...);
  extern void fatal();
  
  void too_many_symbols()
  {
  #if SCPP
      err_fatal(EM_too_many_symbols, 0x7FFF);
  #else // MARS
      error(NULL, 0, 0, "more than %d symbols in object file", 0x7FFF);
      fatal();
  #endif
  }
  
  #if !DEBUG && TX86 && !defined(_MSC_VER)
  __declspec(naked) int __pascal insidx(char *p,unsigned index)
  {
  #undef AL
  #undef AH
  #undef DL
  #undef DH
      _asm
      {
          mov     EAX,index - 4[ESP]
          mov     ECX,p - 4[ESP]
          cmp     EAX,0x7F
          jae     L1
          mov     [ECX],AL
          mov     EAX,1
          ret     8
  
  
      L1:
          cmp     EAX,0x7FFF
          ja      L2
  
          mov     1[ECX],AL
          or      EAX,0x8000
          mov     [ECX],AH
          mov     EAX,2
          ret     8
      }
      L2:
          too_many_symbols();
  }
  #else
  __inline int insidx(char *p,unsigned index)
  {
      //if (index > 0x7FFF) printf("index = x%x\n",index);
      /* OFM spec says it could be <=0x7F, but that seems to cause
       * "library is corrupted" messages. Unverified. See Bugzilla 3601
       */
      if (index < 0x7F)
      {   *p = index;
          return 1;
      }
      else if (index <= 0x7FFF)
      {
          *(p + 1) = index;
          *p = (index >> 8) | 0x80;
          return 2;
      }
      else
      {   too_many_symbols();
          return 0;
      }
  }
  #endif
  
  /**************************
   * Insert a type index number.
   * Input:
   *      p -> where to put the 1 or 2 byte index
   *      index = the 15 bit index
   * Returns:
   *      # of bytes stored
   */
  
  __inline int instypidx(char *p,unsigned index)
  {
      if (index <= 127)
      {   *p = index;
          return 1;
      }
      else if (index <= 0x7FFF)
      {   *(p + 1) = index;
          *p = (index >> 8) | 0x80;
          return 2;
      }
      else                        // overflow
      {   *p = 0;                 // the linker ignores this field anyway
          return 1;
      }
  }
  
  /****************************
   * Read index.
   */
  
  #define getindex(p) ((*(p) & 0x80) \
      ? ((*(unsigned char *)(p) & 0x7F) << 8) | *((unsigned char *)(p) + 1) \
      : *(unsigned char *)(p))
  
  /*****************************
   * Returns:
   *      # of bytes stored
   */
  
  #define ONS_OHD 4               // max # of extra bytes added by obj_namestring()
  
  STATIC int obj_namestring(char *p,const char *name)
  {   unsigned len;
  
      len = strlen(name);
      if (len > 255)
      {   p[0] = 0xFF;
          p[1] = 0;
  #ifdef DEBUG
          assert(len <= 0xFFFF);
  #endif
          TOWORD(p + 2,len);
          memcpy(p + 4,name,len);
          len += ONS_OHD;
      }
      else
      {   p[0] = len;
          memcpy(p + 1,name,len);
          len++;
      }
      return len;
  }
  
  /******************************
   * Allocate a new segment.
   * Return index for the new segment.
   */
  
  seg_data *getsegment()
  {
      int seg = ++seg_count;
      if (seg_count == seg_max)
      {
          seg_max += 10;
          SegData = (seg_data **)mem_realloc(SegData, seg_max * sizeof(seg_data *));
          memset(&SegData[seg_count], 0, 10 * sizeof(seg_data *));
      }
      assert(seg_count < seg_max);
      if (SegData[seg])
          memset(SegData[seg], 0, sizeof(seg_data));
      else
          SegData[seg] = (seg_data *)mem_calloc(sizeof(seg_data));
  
      seg_data *pseg = SegData[seg];
      pseg->SDseg = seg;
      pseg->segidx = 0;
      return pseg;
  }
  
  /**************************
   * Output read only data and generate a symbol for it.
   *
   */
  
  symbol * Obj::sym_cdata(tym_t ty,char *p,int len)
  {
      symbol *s;
  
      alignOffset(CDATA, tysize(ty));
      s = symboldata(Offset(CDATA), ty);
      s->Sseg = CDATA;
      Obj::bytes(CDATA, Offset(CDATA), len, p);
      Offset(CDATA) += len;
  
      s->Sfl = FLdata; //FLextern;
      return s;
  }
  
  /**************************
   * Ouput read only data for data.
   * Output:
   *      *pseg   segment of that data
   * Returns:
   *      offset of that data
   */
  
  int Obj::data_readonly(char *p, int len, int *pseg)
  {
  #if MARS
      targ_size_t oldoff = Offset(CDATA);
      Obj::bytes(CDATA,Offset(CDATA),len,p);
      Offset(CDATA) += len;
      *pseg = CDATA;
  #else
      targ_size_t oldoff = Offset(DATA);
      Obj::bytes(DATA,Offset(DATA),len,p);
      Offset(DATA) += len;
      *pseg = DATA;
  #endif
      return oldoff;
  }
  
  int Obj::data_readonly(char *p, int len)
  {
      int pseg;
  
      return Obj::data_readonly(p, len, &pseg);
  }
  
  /*****************************
   * Get segment for readonly string literals.
   * The linker will pool strings in this section.
   * Params:
   *    sz = number of bytes per character (1, 2, or 4)
   * Returns:
   *    segment index
   */
  int Obj::string_literal_segment(unsigned sz)
  {
      assert(0);
      return 0;
  }
  
  segidx_t Obj::seg_debugT()
  {
      return DEBTYP;
  }
  
  /******************************
   * Perform initialization that applies to all .obj output files.
   * Input:
   *      filename        source file name
   *      csegname        code segment name (can be NULL)
   */
  
  Obj *Obj::init(Outbuffer *objbuf, const char *filename, const char *csegname)
  {
          //printf("Obj::init()\n");
          Obj *mobj = new Obj();
  
          Outbuffer *reset_symbuf = obj.reset_symbuf;
          if (reset_symbuf)
          {
              symbol **p = (symbol **)reset_symbuf->buf;
              const size_t n = reset_symbuf->size() / sizeof(symbol *);
              for (size_t i = 0; i < n; ++i)
                  symbol_reset(p[i]);
              reset_symbuf->setsize(0);
          }
          else
          {
              reset_symbuf = new Outbuffer(50 * sizeof(symbol *));
          }
  
          memset(&obj,0,sizeof(obj));
  
          obj.buf = objbuf;
          obj.buf->reserve(40000);
  
          obj.reset_symbuf = reset_symbuf; // reuse buffer
  
          obj.lastfardatasegi = -1;
  
          obj.mlidata = LIDATA;
          obj.mpubdef = PUBDEF;
          obj.mfixupp = FIXUPP;
          obj.mmodend = MODEND;
          obj.mlinnum = LINNUM;
  
  
          // Reset for different OBJ file formats
          if (I32)
          {   if (config.flags & CFGeasyomf)
              {   obj.LOCoffset = EASY_LOCoffset;
                  obj.LOCpointer = EASY_LOCpointer;
              }
              else
              {
                  obj.mlidata = LID386;
                  obj.mpubdef = PUB386;
                  obj.mfixupp = FIX386;
                  obj.mmodend = MODEND + 1;
                  obj.LOCoffset = LOC32offset;
                  obj.LOCpointer = LOC32pointer;
              }
              obj.fdsegattr = SEG_ATTR(SEG_ALIGN16,SEG_C_PUBLIC,0,USE32);
              obj.csegattr  = SEG_ATTR(SEG_ALIGN4, SEG_C_PUBLIC,0,USE32);
          }
          else
          {
              obj.LOCoffset  = LOC16offset;
              obj.LOCpointer = LOC16pointer;
              obj.fdsegattr = SEG_ATTR(SEG_ALIGN16,SEG_C_PUBLIC,0,USE16);
              obj.csegattr  = SEG_ATTR(SEG_ALIGN2, SEG_C_PUBLIC,0,USE16);
          }
  
          if (config.flags4 & CFG4speed && // if optimized for speed
              config.target_cpu == TARGET_80486)
              // 486 is only CPU that really benefits from alignment
              obj.csegattr  = I32 ? SEG_ATTR(SEG_ALIGN16, SEG_C_PUBLIC,0,USE32)
                                  : SEG_ATTR(SEG_ALIGN16, SEG_C_PUBLIC,0,USE16);
  
          if (!SegData)
          {   seg_max = UDATA + 10;
              SegData = (seg_data **)mem_calloc(seg_max * sizeof(seg_data *));
          }
  
          for (int i = 0; i < seg_max; i++)
          {
              if (SegData[i])
                  memset(SegData[i], 0, sizeof(seg_data));
              else
                  SegData[i] = (seg_data *)mem_calloc(sizeof(seg_data));
          }
  
          SegData[CODE]->SDseg = CODE;
          SegData[DATA]->SDseg = DATA;
          SegData[CDATA]->SDseg = CDATA;
          SegData[UDATA]->SDseg = UDATA;
  
          SegData[CODE]->segidx = CODE;
          SegData[DATA]->segidx = DATA;
          SegData[CDATA]->segidx = CDATA;
          SegData[UDATA]->segidx = UDATA;
  
          seg_count = UDATA;
  
          if (config.fulltypes)
          {
              SegData[DEBSYM]->SDseg = DEBSYM;
              SegData[DEBTYP]->SDseg = DEBTYP;
  
              SegData[DEBSYM]->segidx = DEBSYM;
              SegData[DEBTYP]->segidx = DEBTYP;
  
              seg_count = DEBTYP;
          }
  
          mobj->theadr(filename);
          obj.modname = filename;
          if (!csegname || !*csegname)            // if no code seg name supplied
              obj.csegname = objmodtoseg(obj.modname);    // generate one
          else
              obj.csegname = mem_strdup(csegname);        // our own copy
          objheader(obj.csegname);
          mobj->segment_group(0,0,0,0);             // obj seg and grp info
          ledata_new(cseg,0);             // so ledata is never NULL
          if (config.fulltypes)           // if full typing information
          {   objmod = mobj;
              cv_init();                  // initialize debug output code
          }
  
          return mobj;
  }
  
  /**************************
   * Initialize the start of object output for this particular .obj file.
   */
  
  void Obj::initfile(const char *filename,const char *csegname, const char *modname)
  {
  }
  
  /***************************
   * Fixup and terminate object file.
   */
  
  void Obj::termfile()
  {
  }
  
  /*********************************
   * Terminate package.
   */
  
  void Obj::term(const char *objfilename)
  {
          //printf("Obj::term()\n");
          list_t dl;
          unsigned long size;
  
  #if SCPP
          if (!errcnt)
  #endif
          {
              obj_defaultlib();
              objflush_pointerRefs();
              outfixlist();               // backpatches
          }
  
          if (config.fulltypes)
              cv_term();                  // write out final debug info
          outextdata();                   // finish writing EXTDEFs
          outpubdata();                   // finish writing PUBDEFs
  
          // Put out LEDATA records and associated fixups
          for (size_t i = 0; i < obj.ledatai; i++)
          {   Ledatarec *d = ledatas[i];
  
              if (d->i)                   // if any data in this record
              {   // Fill in header
                  int headersize;
                  int rectyp;
                  assert(d->lseg > 0 && d->lseg <= seg_count);
                  int lseg = SegData[d->lseg]->segidx;
                  char header[sizeof(d->header)];
  
                  if (seg_is_comdat(lseg))   // if COMDAT
                  {
                      header[0] = d->flags | (d->offset ? 1 : 0); // continuation flag
                      header[1] = d->alloctyp;
                      header[2] = d->align;
                      TOOFFSET(header + 3,d->offset);
                      headersize = 3 + intsize;
                      headersize += instypidx(header + headersize,d->typidx);
                      if ((header[1] & 0x0F) == 0)
                      {   // Group index
                          header[headersize] = (d->pubbase == DATA) ? 1 : 0;
                          headersize++;
  
                          // Segment index
                          headersize += insidx(header + headersize,d->pubbase);
                      }
                      headersize += insidx(header + headersize,d->pubnamidx);
  
                      rectyp = I32 ? COMDAT + 1 : COMDAT;
                  }
                  else
                  {
                      rectyp = LEDATA;
                      headersize = insidx(header,lseg);
                      if (intsize == LONGSIZE || d->offset & ~0xFFFFL)
                      {   if (!(config.flags & CFGeasyomf))
                              rectyp++;
                          TOLONG(header + headersize,d->offset);
                          headersize += 4;
                      }
                      else
                      {
                          TOWORD(header + headersize,d->offset);
                          headersize += 2;
                      }
                  }
                  assert(headersize <= sizeof(d->header));
  
                  // Right-justify data in d->header[]
                  memcpy(d->header + sizeof(d->header) - headersize,header,headersize);
                  //printf("objrecord(rectyp=x%02x, d=%p, p=%p, size = %d)\n",
                  //rectyp,d,d->header + (sizeof(d->header) - headersize),d->i + headersize);
  
                  objrecord(rectyp,d->header + (sizeof(d->header) - headersize),
                          d->i + headersize);
                  objfixupp(d->fixuplist);
              }
          }
  #if TERMCODE
          //list_free(&obj.ledata_list,mem_freefp);
  #endif
  
          linnum_term();
          obj_modend();
  
          size = obj.buf->size();
          obj.buf->setsize(0);            // rewind file
          Obj::theadr(obj.modname);
          objheader(obj.csegname);
          mem_free(obj.csegname);
          Obj::segment_group(SegData[CODE]->SDoffset, SegData[DATA]->SDoffset, SegData[CDATA]->SDoffset, SegData[UDATA]->SDoffset);  // do real sizes
  
          // Update any out-of-date far segment sizes
          for (size_t i = 0; i <= seg_count; i++)
          {   seg_data *f = SegData[i];
              if (f->isfarseg && f->origsize != f->SDoffset)
              {   obj.buf->setsize(f->seek);
                  objsegdef(f->attr,f->SDoffset,f->lnameidx,f->classidx);
              }
          }
          //mem_free(obj.farseg);
  
          //printf("Ledata max = %d\n", obj.ledatai);
  #if 0
          printf("Max # of fixups = %d\n",obj.fixup_count);
  #endif
  
          obj.buf->setsize(size);
  }
  
  /*****************************
   * Line number support.
   */
  
  /***************************
   * Record line number linnum at offset.
   * Params:
   *      srcpos = source file position
   *      seg = segment it corresponds to (negative for COMDAT segments)
   *      offset = offset within seg
   *      pubnamidx = public name index
   *      obj.mlinnum = LINNUM or LINSYM
   */
  
  void Obj::linnum(Srcpos srcpos,int seg,targ_size_t offset)
  {
  #if MARS
      varStats.recordLineOffset(srcpos, offset);
  #endif
  
      unsigned linnum = srcpos.Slinnum;
  
  #if 0
  #if MARS || SCPP
      printf("Obj::linnum(seg=%d, offset=0x%lx) ", seg, offset);
  #endif
      srcpos.print("");
  #endif
  
      char linos2 = config.exe == EX_OS2 && !seg_is_comdat(SegData[seg]->segidx);
  
  #if MARS
      if (!obj.term &&
          (seg_is_comdat(SegData[seg]->segidx) || (srcpos.Sfilename && srcpos.Sfilename != obj.modname)))
  #else
      if (!srcpos.Sfilptr)
          return;
      sfile_debug(&srcpos_sfile(srcpos));
      if (!obj.term && (!(srcpos_sfile(srcpos).SFflags & SFtop) || (seg_is_comdat(SegData[seg]->segidx) && !obj.term)))
  #endif
      {   // Not original source file, or a COMDAT.
          // Save data away and deal with it at close of compile.
          // It is done this way because presumably 99% of the lines
          // will be in the original source file, so we wish to minimize
          // memory consumption and maximize speed.
          list_t ll;
          struct Linnum *ln;
  
          if (linos2)
              return;             // BUG: not supported under OS/2
          for (ll = obj.linnum_list; 1; ll = list_next(ll))
          {
              if (!ll)
              {
                  ln = (struct Linnum *) mem_calloc(sizeof(struct Linnum));
  #if MARS
                  ln->filename = srcpos.Sfilename;
  #else
                  ln->filptr = *srcpos.Sfilptr;
  #endif
                  ln->cseg = seg;
                  ln->seg = obj.pubnamidx;
                  list_prepend(&obj.linnum_list,ln);
                  break;
              }
              ln = (Linnum *)list_ptr(ll);
              if (
  #if MARS
                  (ln->filename == srcpos.Sfilename) &&
  #endif
  #if SCPP
                  (ln->filptr == *srcpos.Sfilptr) &&
  #endif
                  ln->cseg == seg &&
                  ln->i < LINNUMMAX - 6)
                  break;
          }
          TOWORD(&ln->data[ln->i],linnum);
          TOOFFSET(&ln->data[ln->i + 2],offset);
          ln->i += 2 + intsize;
      }
      else
      {
          if (linos2 && obj.linreci > LINRECMAX - 8)
              obj.linrec = NULL;                  // allocate a new one
          else if (seg != obj.recseg)
              linnum_flush();
  
          if (!obj.linrec)                        // if not allocated
          {       obj.linrec = (char *) mem_calloc(LINRECMAX);
                  obj.linrec[0] = 0;              // base group / flags
                  obj.linrecheader = 1 + insidx(obj.linrec + 1,seg_is_comdat(SegData[seg]->segidx) ? obj.pubnamidx : SegData[seg]->segidx);
                  obj.linreci = obj.linrecheader;
                  obj.recseg = seg;
  #if MULTISCOPE
                  if (!obj.linvec)
                  {   obj.linvec = vec_calloc(1000);
                      obj.offvec = vec_calloc(1000);
                  }
  #endif
                  if (linos2)
                  {
                      if (!obj.linreclist)        // if first line number record
                          obj.linreci += 8;       // leave room for header
                      list_append(&obj.linreclist,obj.linrec);
                  }
  
                  // Select record type to use
                  obj.mlinnum = seg_is_comdat(SegData[seg]->segidx) ? LINSYM : LINNUM;
                  if (I32 && !(config.flags & CFGeasyomf))
                      obj.mlinnum++;
          }
          else if (obj.linreci > LINRECMAX - (2 + intsize))
          {       objrecord(obj.mlinnum,obj.linrec,obj.linreci);  // output data
                  obj.linreci = obj.linrecheader;
                  if (seg_is_comdat(SegData[seg]->segidx))        // if LINSYM record
                      obj.linrec[0] |= 1;         // continuation bit
          }
  #if MULTISCOPE
          if (linnum >= vec_numbits(obj.linvec))
              obj.linvec = vec_realloc(obj.linvec,linnum + 1000);
          if (offset >= vec_numbits(obj.offvec))
          {
              if (offset < 0xFF00)        // otherwise we overflow ph_malloc()
                  obj.offvec = vec_realloc(obj.offvec,offset * 2);
          }
          if (
  #if 1 // disallow multiple offsets per line
              !vec_testbit(linnum,obj.linvec) &&  // if linnum not already used
  #endif
              // disallow multiple lines per offset
              (offset >= 0xFF00 || !vec_testbit(offset,obj.offvec)))      // and offset not already used
  #endif
          {
  #if MULTISCOPE
              vec_setbit(linnum,obj.linvec);              // mark linnum as used
              if (offset < 0xFF00)
                  vec_setbit(offset,obj.offvec);  // mark offset as used
  #endif
              TOWORD(obj.linrec + obj.linreci,linnum);
              if (linos2)
              {   obj.linrec[obj.linreci + 2] = 1;        // source file index
                  TOLONG(obj.linrec + obj.linreci + 4,offset);
                  obj.linrecnum++;
                  obj.linreci += 8;
              }
              else
              {
                  TOOFFSET(obj.linrec + obj.linreci + 2,offset);
                  obj.linreci += 2 + intsize;
              }
          }
      }
  }
  
  /***************************
   * Flush any pending line number records.
   */
  
  STATIC void linnum_flush()
  {
      if (obj.linreclist)
      {   list_t list;
          size_t len;
  
          obj.linrec = (char *) list_ptr(obj.linreclist);
          TOWORD(obj.linrec + 6,obj.linrecnum);
          list = obj.linreclist;
          while (1)
          {   obj.linrec = (char *) list_ptr(list);
  
              list = list_next(list);
              if (list)
              {   objrecord(obj.mlinnum,obj.linrec,LINRECMAX);
                  mem_free(obj.linrec);
              }
              else
              {   objrecord(obj.mlinnum,obj.linrec,obj.linreci);
                  break;
              }
          }
          list_free(&obj.linreclist,FPNULL);
  
          // Put out File Names Table
          TOLONG(obj.linrec + 2,0);               // record no. of start of source (???)
          TOLONG(obj.linrec + 6,obj.linrecnum);   // number of primary source records
          TOLONG(obj.linrec + 10,1);              // number of source and listing files
          len = obj_namestring(obj.linrec + 14,obj.modname);
          assert(14 + len <= LINRECMAX);
          objrecord(obj.mlinnum,obj.linrec,14 + len);
  
          mem_free(obj.linrec);
          obj.linrec = NULL;
      }
      else if (obj.linrec)                        // if some line numbers to send
      {   objrecord(obj.mlinnum,obj.linrec,obj.linreci);
          mem_free(obj.linrec);
          obj.linrec = NULL;
      }
  #if MULTISCOPE
      vec_clear(obj.linvec);
      vec_clear(obj.offvec);
  #endif
  }
  
  /*************************************
   * Terminate line numbers.
   */
  
  STATIC void linnum_term()
  {   list_t ll;
  #if SCPP
      Sfile *lastfilptr = NULL;
  #endif
  #if MARS
      const char *lastfilename = NULL;
  #endif
      int csegsave = cseg;
  
      linnum_flush();
      obj.term = 1;
      while (obj.linnum_list)
      {   struct Linnum *ln;
          unsigned u;
          Srcpos srcpos;
          targ_size_t offset;
  
          ll = obj.linnum_list;
          ln = (struct Linnum *) list_ptr(ll);
  #if SCPP
          Sfile *filptr = ln->filptr;
          if (filptr != lastfilptr)
          {   Obj::theadr(filptr->SFname);
              lastfilptr = filptr;
          }
  #endif
  #if MARS
          const char *filename = ln->filename;
          if (filename != lastfilename)
          {
              if (filename)
                  objmod->theadr(filename);
              lastfilename = filename;
          }
  #endif
          while (1)
          {
              cseg = ln->cseg;
              assert(cseg > 0);
              obj.pubnamidx = ln->seg;
  #if MARS
              srcpos.Sfilename = ln->filename;
  #else
              srcpos.Sfilptr = &ln->filptr;
  #endif
              for (u = 0; u < ln->i; )
              {
                  srcpos.Slinnum = *(unsigned short *)&ln->data[u];
                  u += 2;
                  if (I32)
                      offset = *(unsigned long *)&ln->data[u];
                  else
                      offset = *(unsigned short *)&ln->data[u];
                  objmod->linnum(srcpos,cseg,offset);
                  u += intsize;
              }
              linnum_flush();
              ll = list_next(ll);
              list_subtract(&obj.linnum_list,ln);
              mem_free(ln);
          L1:
              if (!ll)
                  break;
              ln = (struct Linnum *) list_ptr(ll);
  #if SCPP
              if (filptr != ln->filptr)
  #else
              if (filename != ln->filename)
  #endif
              {   ll = list_next(ll);
                  goto L1;
              }
          }
      }
      cseg = csegsave;
      assert(cseg > 0);
  #if MULTISCOPE
      vec_free(obj.linvec);
      vec_free(obj.offvec);
  #endif
  }
  
  /*******************************
   * Set start address
   */
  
  void Obj::startaddress(Symbol *s)
  {
      obj.startaddress = s;
  }
  
  /*******************************
   * Output DOSSEG coment record.
   */
  
  void Obj::dosseg()
  {   static const char dosseg[] = { 0x80,0x9E };
  
      objrecord(COMENT,dosseg,sizeof(dosseg));
  }
  
  /*******************************
   * Embed comment record.
   */
  
  STATIC void obj_comment(unsigned char x, const char *string, size_t len)
  {
      char buf[128];
  
      char *library = (2 + len <= sizeof(buf)) ? buf : (char *) malloc(2 + len);
      assert(library);
      library[0] = 0;
      library[1] = x;
      memcpy(library + 2,string,len);
      objrecord(COMENT,library,len + 2);
      if (library != buf)
          free(library);
  }
  
  /*******************************
   * Output library name.
   * Output:
   *      name is modified
   * Returns:
   *      true if operation is supported
   */
  
  bool Obj::includelib(const char *name)
  {   const char *p;
      size_t len = strlen(name);
  
      p = filespecdotext(name);
      if (!filespeccmp(p,".lib"))
          len -= strlen(p);               // lop off .LIB extension
      obj_comment(0x9F, name, len);
      return true;
  }
  
  /*******************************
  * Output linker directive.
  * Output:
  *      directive is modified
  * Returns:
  *      true if operation is supported
  */
  
  bool Obj::linkerdirective(const char *name)
  {
      return false;
  }
  
  /**********************************
   * Do we allow zero sized objects?
   */
  
  bool Obj::allowZeroSize()
  {
      return false;
  }
  
  /**************************
   * Embed string in executable.
   */
  
  void Obj::exestr(const char *p)
  {
      obj_comment(0xA4,p, strlen(p));
  }
  
  /**************************
   * Embed string in obj.
   */
  
  void Obj::user(const char *p)
  {
      obj_comment(0xDF,p, strlen(p));
  }
  
  /*********************************
   * Put out default library name.
   */
  
  STATIC void obj_defaultlib()
  {
      char library[4];            // default library
      static const char model[MEMMODELS+1] = "SMCLV";
  
  #if MARS
      memcpy(library,"SM?",4);
  #else
      memcpy(library,"SD?",4);
  #endif
      switch (config.exe)
      {
          case EX_OS2:
              library[2] = 'F';
          case EX_OS1:
              library[1] = 'O';
              break;
          case EX_WIN32:
  #if MARS
              library[1] = 'M';
  #else
              library[1] = 'N';
  #endif
              library[2] = (config.flags4 & CFG4dllrtl) ? 'D' : 'N';
              break;
          case EX_DOSX:
          case EX_PHARLAP:
              library[2] = 'X';
              break;
          default:
              library[2] = model[config.memmodel];
              if (config.wflags & WFwindows)
                  library[1] = 'W';
              break;
      }
  
      if (!(config.flags2 & CFG2nodeflib))
      {
          objmod->includelib(configv.deflibname ? configv.deflibname : library);
      }
  }
  
  /*******************************
   * Output a weak extern record.
   * s1 is the weak extern, s2 is its default resolution.
   */
  
  void Obj::wkext(Symbol *s1,Symbol *s2)
  {
      //printf("Obj::wkext(%s)\n", s1->Sident);
      if (I32)
      {
          // Optlink crashes with weak symbols at EIP 41AFE7, 402000
          return;
      }
  
      int x2;
      if (s2)
          x2 = s2->Sxtrnnum;
      else
      {
          if (!obj.nullext)
          {
              obj.nullext = Obj::external_def("__nullext");
          }
          x2 = obj.nullext;
      }
      outextdata();
  
      char buffer[2+2+2];
      buffer[0] = 0x80;
      buffer[1] = 0xA8;
      int i = 2;
      i += insidx(&buffer[2],s1->Sxtrnnum);
      i += insidx(&buffer[i],x2);
      objrecord(COMENT,buffer,i);
  }
  
  /*******************************
   * Output a lazy extern record.
   * s1 is the lazy extern, s2 is its default resolution.
   */
  
  void Obj::lzext(Symbol *s1,Symbol *s2)
  {   char buffer[2+2+2];
      int i;
  
      outextdata();
      buffer[0] = 0x80;
      buffer[1] = 0xA9;
      i = 2;
      i += insidx(&buffer[2],s1->Sxtrnnum);
      i += insidx(&buffer[i],s2->Sxtrnnum);
      objrecord(COMENT,buffer,i);
  }
  
  /*******************************
   * Output an alias definition record.
   */
  
  void Obj::_alias(const char *n1,const char *n2)
  {   unsigned len;
      char *buffer;
  
      buffer = (char *) alloca(strlen(n1) + strlen(n2) + 2 * ONS_OHD);
      len = obj_namestring(buffer,n1);
      len += obj_namestring(buffer + len,n2);
      objrecord(ALIAS,buffer,len);
  }
  
  /*******************************
   * Output module name record.
   */
  
  void Obj::theadr(const char *modname)
  {
      //printf("Obj::theadr(%s)\n", modname);
  
      // Convert to absolute file name, so debugger can find it anywhere
      char absname[260];
      if (config.fulltypes &&
          modname[0] != '\\' && modname[0] != '/' && !(modname[0] && modname[1] == ':'))
      {
          if (getcwd(absname, sizeof(absname)))
          {
              int len = strlen(absname);
              if(absname[len - 1] != '\\' && absname[len - 1] != '/')
                  absname[len++] = '\\';
              strcpy(absname + len, modname);
              modname = absname;
          }
      }
  
      char *theadr = (char *)alloca(ONS_OHD + strlen(modname));
      int i = obj_namestring(theadr,modname);
      objrecord(THEADR,theadr,i);                 // module name record
  }
  
  /*******************************
   * Embed compiler version in .obj file.
   */
  
  void Obj::compiler()
  {
      static const char compiler[] = "\0\xDB" "Digital Mars C/C++"
          VERSION
          ;       // compiled by ...
  
      objrecord(COMENT,compiler,sizeof(compiler) - 1);
  }
  
  /*******************************
   * Output header stuff for object files.
   * Input:
   *      csegname        Name to use for code segment (NULL if use default)
   */
  
  STATIC void objheader(char *csegname)
  {
    char *nam;
    static char lnames[] =
          "\0\06DGROUP\05_TEXT\04CODE\05_DATA\04DATA\05CONST\04_BSS\03BSS\
  \07$$TYPES\06DEBTYP\011$$SYMBOLS\06DEBSYM";
  
  #define CODECLASS       4                       // code class lname index
  #define DATACLASS       6                       // data class lname index
  #define CDATACLASS      7                       // CONST class lname index
  #define BSSCLASS        9                       // BSS class lname index
  
    // Include debug segment names if inserting type information
    int lnamesize = config.fulltypes ? sizeof(lnames) - 1 : sizeof(lnames) - 1 - 32;
    int texti = 8;                                // index of _TEXT
  
    static char comment[] = {0,0x9D,'0','?','O'}; // memory model
    static char model[MEMMODELS+1] = "smclv";
    static char exten[] = {0,0xA1,1,'C','V'};     // extended format
    static char pmdeb[] = {0x80,0xA1,1,'H','L','L',0};    // IBM PM debug format
  
      if (I32)
      {   if (config.flags & CFGeasyomf)
          {
              // Indicate we're in EASY OMF (hah!) format
              static const char easy_omf[] = { 0x80,0xAA,'8','0','3','8','6' };
              objrecord(COMENT,easy_omf,sizeof(easy_omf));
          }
      }
  
    // Send out a comment record showing what memory model was used
    comment[2] = config.target_cpu + '0';
    comment[3] = model[config.memmodel];
    if (I32)
    {     if (config.exe == EX_WIN32)
              comment[3] = 'n';
          else if (config.exe == EX_OS2)
              comment[3] = 'f';
          else
              comment[3] = 'x';
    }
    objrecord(COMENT,comment,sizeof(comment));
  
      // Send out comment indicating we're using extensions to .OBJ format
      if (config.exe == EX_OS2)
          objrecord(COMENT,pmdeb,sizeof(pmdeb));
      else
          objrecord(COMENT,exten,sizeof(exten));
  
      // Change DGROUP to FLAT if we are doing flat memory model
      // (Watch out, objheader() is called twice!)
      if (config.exe & EX_flat)
      {
          if (lnames[2] != 'F')                   // do not do this twice
          {   memcpy(lnames + 1,"\04FLAT",5);
              memmove(lnames + 6,lnames + 8,sizeof(lnames) - 8);
          }
          lnamesize -= 2;
          texti -= 2;
      }
  
      // Put out segment and group names
      if (csegname)
      {
          // Replace the module name _TEXT with the new code segment name
          const size_t i = strlen(csegname);
          char *p = (char *)alloca(lnamesize + i - 5);
          memcpy(p,lnames,8);
          p[texti] = (char)i;
          texti++;
          memcpy(p + texti,csegname,i);
          memcpy(p + texti + i,lnames + texti + 5,lnamesize - (texti + 5));
          objrecord(LNAMES,p,lnamesize + i - 5);
      }
      else
          objrecord(LNAMES,lnames,lnamesize);
  }
  
  /********************************
   * Convert module name to code segment name.
   * Output:
   *      mem_malloc'd code seg name
   */
  
  STATIC char * objmodtoseg(const char *modname)
  {   char *csegname = NULL;
  
      if (LARGECODE)              // if need to add in module name
      {   int i;
          char *m;
          static const char suffix[] = "_TEXT";
  
          // Prepend the module name to the beginning of the _TEXT
          m = filespecgetroot(filespecname(modname));
          strupr(m);
          i = strlen(m);
          csegname = (char *)mem_malloc(i + sizeof(suffix));
          strcpy(csegname,m);
          strcat(csegname,suffix);
          mem_free(m);
      }
      return csegname;
  }
  
  /*********************************
   * Put out a segment definition.
   */
  
  STATIC void objsegdef(int attr,targ_size_t size,int segnamidx,int classnamidx)
  {
      unsigned reclen;
      char sd[1+4+2+2+2+1];
  
      //printf("objsegdef(attr=x%x, size=x%x, segnamidx=x%x, classnamidx=x%x)\n",
        //attr,size,segnamidx,classnamidx);
      sd[0] = attr;
      if (attr & 1 || config.flags & CFGeasyomf)
      {   TOLONG(sd + 1,size);            // store segment size
          reclen = 5;
      }
      else
      {
  #ifdef DEBUG
          assert(size <= 0xFFFF);
  #endif
          TOWORD(sd + 1,size);
          reclen = 3;
      }
      reclen += insidx(sd + reclen,segnamidx);    // segment name index
      reclen += insidx(sd + reclen,classnamidx);  // class name index
      sd[reclen] = 1;                             // overlay name index
      reclen++;
      if (attr & 1)                       // if USE32
      {
          if (config.flags & CFGeasyomf)
          {   // Translate to Pharlap format
              sd[0] &= ~1;                // turn off P bit
  
              // Translate A: 4->6
              attr &= SEG_ATTR(7,0,0,0);
              if (attr == SEG_ATTR(4,0,0,0))
                  sd[0] ^= SEG_ATTR(4 ^ 6,0,0,0);
  
              // 2 is execute/read
              // 3 is read/write
              // 4 is use32
              sd[reclen] = (classnamidx == 4) ? (4+2) : (4+3);
              reclen++;
          }
      }
      else                                // 16 bit segment
      {
  #if MARS
          assert(0);
  #else
          if (size & ~0xFFFFL)
          {   if (size == 0x10000)        // if exactly 64Kb
                  sd[0] |= 2;             // set "B" bit
              else
                  synerr(EM_seg_gt_64k,size);     // segment exceeds 64Kb
          }
  //printf("attr = %x\n", attr);
  #endif
      }
  #ifdef DEBUG
      assert(reclen <= sizeof(sd));
  #endif
      objrecord(SEGDEF + (sd[0] & 1),sd,reclen);
  }
  
  /*********************************
   * Output segment and group definitions.
   * Input:
   *      codesize        size of code segment
   *      datasize        size of initialized data segment
   *      cdatasize       size of initialized const data segment
   *      udatasize       size of uninitialized data segment
   */
  
  void Obj::segment_group(targ_size_t codesize,targ_size_t datasize,
                  targ_size_t cdatasize,targ_size_t udatasize)
  {
      int dsegattr;
      int dsymattr;
  
      // Group into DGROUP the segments CONST, _BSS and _DATA
      // For FLAT model, it's just GROUP FLAT
      static const char grpdef[] = {2,0xFF,2,0xFF,3,0xFF,4};
  
      objsegdef(obj.csegattr,codesize,3,CODECLASS);  // seg _TEXT, class CODE
  
  #if MARS
      dsegattr = SEG_ATTR(SEG_ALIGN16,SEG_C_PUBLIC,0,USE32);
      objsegdef(dsegattr,datasize,5,DATACLASS);   // [DATA]  seg _DATA, class DATA
      objsegdef(dsegattr,cdatasize,7,CDATACLASS); // [CDATA] seg CONST, class CONST
      objsegdef(dsegattr,udatasize,8,BSSCLASS);   // [UDATA] seg _BSS,  class BSS
  #else
      dsegattr = I32
            ? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32)
            : SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);
      objsegdef(dsegattr,datasize,5,DATACLASS);   // seg _DATA, class DATA
      objsegdef(dsegattr,cdatasize,7,CDATACLASS); // seg CONST, class CONST
      objsegdef(dsegattr,udatasize,8,BSSCLASS);   // seg _BSS, class BSS
  #endif
  
      obj.lnameidx = 10;                          // next lname index
      obj.segidx = 5;                             // next segment index
  
      if (config.fulltypes)
      {
          dsymattr = I32
                ? SEG_ATTR(SEG_ALIGN1,SEG_C_ABS,0,USE32)
                : SEG_ATTR(SEG_ALIGN1,SEG_C_ABS,0,USE16);
  
          if (config.exe & EX_flat)
          {   // IBM's version of CV uses dword aligned segments
              dsymattr = SEG_ATTR(SEG_ALIGN4,SEG_C_ABS,0,USE32);
          }
          else if (config.fulltypes == CV4)
          {   // Always use 32 bit segments
              dsymattr |= USE32;
              assert(!(config.flags & CFGeasyomf));
          }
          objsegdef(dsymattr,SegData[DEBSYM]->SDoffset,0x0C,0x0D);
          objsegdef(dsymattr,SegData[DEBTYP]->SDoffset,0x0A,0x0B);
          obj.lnameidx += 4;                      // next lname index
          obj.segidx += 2;                        // next segment index
      }
  
      objrecord(GRPDEF,grpdef,(config.exe & EX_flat) ? 1 : sizeof(grpdef));
  #if 0
      // Define fixup threads, we don't use them
      {   static const char thread[] = { 0,3,1,2,2,1,3,4,0x40,1,0x45,1 };
          objrecord(obj.mfixupp,thread,sizeof(thread));
      }
      // This comment appears to indicate that no more PUBDEFs, EXTDEFs,
      // or COMDEFs are coming.
      {   static const char cv[] = {0,0xA2,1};
          objrecord(COMENT,cv,sizeof(cv));
      }
  #endif
  }
  
  
  /**************************************
   * 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    number of static destructors
   *      seg     1:      user
   *              2:      lib
   *              3:      compiler
   */
  
  void Obj::staticctor(Symbol *s,int dtor,int seg)
  {
      // We need to always put out the segments in triples, so that the
      // linker will put them in the correct order.
      static char lnamector[] = "\05XIFCB\04XIFU\04XIFL\04XIFM\05XIFCE";
      static char lnamedtor[] = "\04XOFB\03XOF\04XOFE";
      static char lnamedtorf[] = "\03XOB\02XO\03XOE";
  
      symbol_debug(s);
  
      // Determine if near or far function
      assert(I32 || tyfarfunc(s->ty()));
  
      // Put out LNAMES record
      objrecord(LNAMES,lnamector,sizeof(lnamector) - 1);
  
      int dsegattr = I32
          ? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32)
          : SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);
  
      for (int i = 0; i < 5; i++)
      {   int sz;
  
          sz = (i == seg) ? 4 : 0;
  
          // Put out segment definition record
          objsegdef(dsegattr,sz,obj.lnameidx,DATACLASS);
  
          if (i == seg)
          {
              seg_data *pseg = getsegment();
              pseg->segidx = obj.segidx;
              Obj::reftoident(pseg->SDseg,0,s,0,0);     // put out function pointer
          }
  
          obj.segidx++;
          obj.lnameidx++;
      }
  
      if (dtor)
      {   // Leave space in XOF segment so that __fatexit() can insert a
          // pointer to the static destructor in XOF.
  
          // Put out LNAMES record
          if (LARGEDATA)
              objrecord(LNAMES,lnamedtorf,sizeof(lnamedtorf) - 1);
          else
              objrecord(LNAMES,lnamedtor,sizeof(lnamedtor) - 1);
  
          // Put out beginning segment
          objsegdef(dsegattr,0,obj.lnameidx,BSSCLASS);
  
          // Put out segment definition record
          objsegdef(dsegattr,4 * dtor,obj.lnameidx + 1,BSSCLASS);
  
          // Put out ending segment
          objsegdef(dsegattr,0,obj.lnameidx + 2,BSSCLASS);
  
          obj.lnameidx += 3;                      // for next time
          obj.segidx += 3;
      }
  }
  
  void Obj::staticdtor(Symbol *s)
  {
      assert(0);
  }
  
  
  /***************************************
   * Set up function to be called as static constructor on program
   * startup or static destructor on program shutdown.
   * Params:
   *      s = function symbol
   *      isCtor = true if constructor, false if destructor
   */
  
  void Obj::setModuleCtorDtor(Symbol *s, bool isCtor)
  {
      // We need to always put out the segments in triples, so that the
      // linker will put them in the correct order.
      static char lnames[4][5+4+5+1] =
      {   "\03XIB\02XI\03XIE",            // near constructor
          "\03XCB\02XC\03XCE",            // near destructor
          "\04XIFB\03XIF\04XIFE",         // far constructor
          "\04XCFB\03XCF\04XCFE",         // far destructor
      };
      // Size of each of the above strings
      static int lnamesize[4] = { 4+3+4,4+3+4,5+4+5,5+4+5 };
  
      int dsegattr;
  
      symbol_debug(s);
  #if defined(DEBUG) && SCPP
      assert(memcmp(s->Sident,"_ST",3) == 0);
  #endif
  
      // Determine if constructor or destructor
      // _STI... is a constructor, _STD... is a destructor
      int i = !isCtor;
      // Determine if near or far function
      if (tyfarfunc(s->Stype->Tty))
          i += 2;
  
      // Put out LNAMES record
      objrecord(LNAMES,lnames[i],lnamesize[i]);
  
      dsegattr = I32
          ? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32)
          : SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);
  
      // Put out beginning segment
      objsegdef(dsegattr,0,obj.lnameidx,DATACLASS);
      obj.segidx++;
  
      // Put out segment definition record
      // size is NPTRSIZE or FPTRSIZE
      objsegdef(dsegattr,(i & 2) + tysize(TYnptr),obj.lnameidx + 1,DATACLASS);
      seg_data *pseg = getsegment();
      pseg->segidx = obj.segidx;
      Obj::reftoident(pseg->SDseg,0,s,0,0);     // put out function pointer
      obj.segidx++;
  
      // Put out ending segment
      objsegdef(dsegattr,0,obj.lnameidx + 2,DATACLASS);
      obj.segidx++;
  
      obj.lnameidx += 3;                  // for next time
  }
  
  
  /***************************************
   * Stuff pointer to function in its own segment.
   * Used for static ctor and dtor lists.
   */
  
  void Obj::ehtables(Symbol *sfunc,unsigned size,Symbol *ehsym)
  {
      // We need to always put out the segments in triples, so that the
      // linker will put them in the correct order.
      static char lnames[] =
      {   "\03FIB\02FI\03FIE"             // near constructor
      };
      int i;
      int dsegattr;
      targ_size_t offset;
  
      symbol_debug(sfunc);
  
      if (obj.fisegi == 0)
      {
          // Put out LNAMES record
          objrecord(LNAMES,lnames,sizeof(lnames) - 1);
  
          dsegattr = I32
              ? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32)
              : SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);
  
          // Put out beginning segment
          objsegdef(dsegattr,0,obj.lnameidx,DATACLASS);
          obj.lnameidx++;
          obj.segidx++;
  
          // Put out segment definition record
          obj.fisegi = obj_newfarseg(0,DATACLASS);
          objsegdef(dsegattr,0,obj.lnameidx,DATACLASS);
          SegData[obj.fisegi]->attr = dsegattr;
          assert(SegData[obj.fisegi]->segidx == obj.segidx);
  
          // Put out ending segment
          objsegdef(dsegattr,0,obj.lnameidx + 1,DATACLASS);
  
          obj.lnameidx += 2;              // for next time
          obj.segidx += 2;
      }
      offset = SegData[obj.fisegi]->SDoffset;
      offset += Obj::reftoident(obj.fisegi,offset,sfunc,0,LARGECODE ? CFoff | CFseg : CFoff);   // put out function pointer
      offset += Obj::reftoident(obj.fisegi,offset,ehsym,0,0);   // pointer to data
      Obj::bytes(obj.fisegi,offset,intsize,&size);          // size of function
      SegData[obj.fisegi]->SDoffset = offset + intsize;
  }
  
  void Obj::ehsections()
  {
      assert(0);
  }
  
  /***************************************
   * Append pointer to ModuleInfo to "FM" segment.
   * The FM segment is bracketed by the empty FMB and FME segments.
   */
  
  #if MARS
  
  void Obj::moduleinfo(Symbol *scc)
  {
      // We need to always put out the segments in triples, so that the
      // linker will put them in the correct order.
      static char lnames[] =
      {   "\03FMB\02FM\03FME"
      };
  
      symbol_debug(scc);
  
      if (obj.fmsegi == 0)
      {
          // Put out LNAMES record
          objrecord(LNAMES,lnames,sizeof(lnames) - 1);
  
          int dsegattr = I32
              ? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32)
              : SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);
  
          // Put out beginning segment
          objsegdef(dsegattr,0,obj.lnameidx,DATACLASS);
          obj.lnameidx++;
          obj.segidx++;
  
          // Put out segment definition record
          obj.fmsegi = obj_newfarseg(0,DATACLASS);
          objsegdef(dsegattr,0,obj.lnameidx,DATACLASS);
          SegData[obj.fmsegi]->attr = dsegattr;
          assert(SegData[obj.fmsegi]->segidx == obj.segidx);
  
          // Put out ending segment
          objsegdef(dsegattr,0,obj.lnameidx + 1,DATACLASS);
  
          obj.lnameidx += 2;              // for next time
          obj.segidx += 2;
      }
  
      targ_size_t offset = SegData[obj.fmsegi]->SDoffset;
      offset += Obj::reftoident(obj.fmsegi,offset,scc,0,LARGECODE ? CFoff | CFseg : CFoff);     // put out function pointer
      SegData[obj.fmsegi]->SDoffset = offset;
  }
  
  #endif
  
  
  /*********************************
   * 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 (which will be a negative value to
   *      distinguish it from regular segments).
   */
  
  int Obj::comdatsize(Symbol *s, targ_size_t symsize)
  {
      return generate_comdat(this, s, false);
  }
  
  int Obj::comdat(Symbol *s)
  {
      return generate_comdat(this, s, false);
  }
  
  int Obj::readonly_comdat(Symbol *s)
  {
      s->Sseg = generate_comdat(this, s, true);
      return s->Sseg;
  }
  
  static int generate_comdat(Obj* objmod, Symbol *s, bool is_readonly_comdat)
  {   char lnames[IDMAX+IDOHD+1]; // +1 to allow room for strcpy() terminating 0
      char cextdef[2+2];
      char *p;
      size_t lnamesize;
      unsigned ti;
      int isfunc;
      tym_t ty;
  
      symbol_debug(s);
      obj.reset_symbuf->write(&s, sizeof(s));
      ty = s->ty();
      isfunc = tyfunc(ty) != 0 || is_readonly_comdat;
  
      // Put out LNAME for name of Symbol
      lnamesize = objmod->mangle(s,lnames);
      objrecord((s->Sclass == SCstatic ? LLNAMES : LNAMES),lnames,lnamesize);
  
      // Put out CEXTDEF for name of Symbol
      outextdata();
      p = cextdef;
      p += insidx(p,obj.lnameidx++);
      ti = (config.fulltypes == CVOLD) ? cv_typidx(s->Stype) : 0;
      p += instypidx(p,ti);
      objrecord(CEXTDEF,cextdef,p - cextdef);
      s->Sxtrnnum = ++obj.extidx;
  
      seg_data *pseg = getsegment();
      pseg->segidx = -obj.extidx;
      assert(pseg->SDseg > 0);
  
      // Start new LEDATA record for this COMDAT
      Ledatarec *lr = ledata_new(pseg->SDseg,0);
      lr->typidx = ti;
      lr->pubnamidx = obj.lnameidx - 1;
      if (isfunc)
      {   lr->pubbase = SegData[cseg]->segidx;
          if (s->Sclass == SCcomdat || s->Sclass == SCinline)
              lr->alloctyp = 0x10 | 0x00; // pick any instance | explicit allocation
          if (is_readonly_comdat)
          {
              assert(lr->lseg > 0 && lr->lseg <= seg_count);
              lr->flags |= 0x08;      // data in code seg
          }
          else
          {
              cseg = lr->lseg;
              assert(cseg > 0 && cseg <= seg_count);
              obj.pubnamidx = obj.lnameidx - 1;
              Offset(cseg) = 0;
              if (tyfarfunc(ty) && strcmp(s->Sident,"main") == 0)
                  lr->alloctyp |= 1;  // because MS does for unknown reasons
          }
      }
      else
      {   unsigned char atyp;
  
          switch (ty & mTYLINK)
          {   case 0:
              case mTYnear:       lr->pubbase = DATA;
  #if 0
                                  atyp = 0;       // only one instance is allowed
  #else
                                  atyp = 0x10;    // pick any (also means it is
                                                  // not searched for in a library)
  #endif
                                  break;
  
              case mTYcs:         lr->flags |= 0x08;      // data in code seg
                                  atyp = 0x11;    break;
  
              case mTYfar:        atyp = 0x12;    break;
  
              case mTYthread:     lr->pubbase = objmod->tlsseg()->segidx;
                                  atyp = 0x10;    // pick any (also means it is
                                                  // not searched for in a library)
                                  break;
  
              default:            assert(0);
          }
          lr->alloctyp = atyp;
      }
      if (s->Sclass == SCstatic)
          lr->flags |= 0x04;      // local bit (make it an "LCOMDAT")
      s->Soffset = 0;
      s->Sseg = pseg->SDseg;
      return pseg->SDseg;
  }
  
  /***********************************
   * Returns:
   *      jump table segment for function s
   */
  int Obj::jmpTableSegment(Symbol *s)
  {
      return (config.flags & CFGromable) ? cseg : DATA;
  }
  
  /**********************************
   * Reset code seg to existing seg.
   * Used after a COMDAT for a function is done.
   */
  
  void Obj::setcodeseg(int seg)
  {
      assert(0 < seg && seg <= seg_count);
      cseg = 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)
  {
      if (!name)
      {
          if (cseg != CODE)
          {
              cseg = CODE;
          }
          return cseg;
      }
  
      // Put out LNAMES record
      size_t lnamesize = strlen(name) + suffix * 5;
      char *lnames = (char *) alloca(1 + lnamesize + 1);
      lnames[0] = (char)lnamesize;
      assert(lnamesize <= (255 - 2 - sizeof(int)*3));
      strcpy(lnames + 1,name);
      if (suffix)
          strcat(lnames + 1,"_TEXT");
      objrecord(LNAMES,lnames,lnamesize + 1);
  
      cseg = obj_newfarseg(0,4);
      SegData[cseg]->attr = obj.csegattr;
      SegData[cseg]->segidx = obj.segidx;
      assert(cseg > 0);
      obj.segidx++;
      Offset(cseg) = 0;
  
      objsegdef(obj.csegattr,0,obj.lnameidx++,4);
  
      return cseg;
  }
  
  /*********************************
   * Define segment for Thread Local Storage.
   * Output:
   *      tlsseg  set to segment number for TLS segment.
   * Returns:
   *      segment for TLS segment
   */
  
  seg_data *Obj::tlsseg_bss() { return Obj::tlsseg(); }
  
  seg_data *Obj::tlsseg()
  {   //static char tlssegname[] = "\04$TLS\04$TLS";
      //static char tlssegname[] = "\05.tls$\03tls";
      static const char tlssegname[] = "\05.tls$\03tls\04.tls\010.tls$ZZZ";
  
      if (obj.tlssegi == 0)
      {   int segattr;
  
          objrecord(LNAMES,tlssegname,sizeof(tlssegname) - 1);
  
  #if MARS
          segattr = SEG_ATTR(SEG_ALIGN16,SEG_C_PUBLIC,0,USE32);
  #else
          segattr = I32
              ? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32)
              : SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);
  #endif
  
          // Put out beginning segment (.tls)
          objsegdef(segattr,0,obj.lnameidx + 2,obj.lnameidx + 1);
          obj.segidx++;
  
          // Put out .tls$ segment definition record
          obj.tlssegi = obj_newfarseg(0,obj.lnameidx + 1);
          objsegdef(segattr,0,obj.lnameidx,obj.lnameidx + 1);
          SegData[obj.tlssegi]->attr = segattr;
          SegData[obj.tlssegi]->segidx = obj.segidx;
  
          // Put out ending segment (.tls$ZZZ)
          objsegdef(segattr,0,obj.lnameidx + 3,obj.lnameidx + 1);
  
          obj.lnameidx += 4;
          obj.segidx += 2;
      }
      return SegData[obj.tlssegi];
  }
  
  seg_data *Obj::tlsseg_data()
  {
      // specific for Mach-O
      assert(0);
      return NULL;
  }
  
  /********************************
   * Define a far data segment.
   * Input:
   *      name    Name of module
   *      size    Size of the segment to be created
   * Returns:
   *      segment index of far data segment created
   *      *poffset start of the data for the far data segment
   */
  
  int Obj::fardata(char *name,targ_size_t size,targ_size_t *poffset)
  {
      static char fardataclass[] = "\010FAR_DATA";
      int len;
      int i;
      char *buffer;
  
      // See if we can use existing far segment, and just bump its size
      i = obj.lastfardatasegi;
      if (i != -1
          && (intsize != 2 || (unsigned long) SegData[i]->SDoffset + size < 0x8000)
          )
      {   *poffset = SegData[i]->SDoffset;        // BUG: should align this
          SegData[i]->SDoffset += size;
          return i;
      }
  
      // No. We need to build a new far segment
  
      if (obj.fardataidx == 0)            // if haven't put out far data lname
      {   // Put out class lname
          objrecord(LNAMES,fardataclass,sizeof(fardataclass) - 1);
          obj.fardataidx = obj.lnameidx++;
      }
  
      // Generate name based on module name
      name = strupr(filespecgetroot(filespecname(obj.modname)));
  
      // Generate name for this far segment
      len = 1 + strlen(name) + 3 + 5 + 1;
      buffer = (char *)alloca(len);
      sprintf(buffer + 1,"%s%d_DATA",name,obj.segidx);
      len = strlen(buffer + 1);
      buffer[0] = len;
      assert(len <= 255);
      objrecord(LNAMES,buffer,len + 1);
  
      mem_free(name);
  
      // Construct a new SegData[] entry
      obj.lastfardatasegi = obj_newfarseg(size,obj.fardataidx);
  
      // Generate segment definition
      objsegdef(obj.fdsegattr,size,obj.lnameidx++,obj.fardataidx);
      obj.segidx++;
  
      *poffset = 0;
      return SegData[obj.lastfardatasegi]->SDseg;
  }
  
  /************************************
   * Remember where we put a far segment so we can adjust
   * its size later.
   * Input:
   *      obj.segidx
   *      lnameidx
   * Returns:
   *      index of SegData[]
   */
  
  STATIC int obj_newfarseg(targ_size_t size,int classidx)
  {
      seg_data *f = getsegment();
      f->isfarseg = true;
      f->seek = obj.buf->size();
      f->attr = obj.fdsegattr;
      f->origsize = size;
      f->SDoffset = size;
      f->segidx = obj.segidx;
      f->lnameidx = obj.lnameidx;
      f->classidx = classidx;
      return f->SDseg;
  }
  
  /******************************
   * Convert reference to imported name.
   */
  
  void Obj::_import(elem *e)
  {
  #if MARS
      assert(0);
  #else
      Symbol *s;
      Symbol *simp;
  
      elem_debug(e);
      if ((e->Eoper == OPvar || e->Eoper == OPrelconst) &&
          (s = e->EV.sp.Vsym)->ty() & mTYimport &&
          (s->Sclass == SCextern || s->Sclass == SCinline)
         )
      {   char *name;
          char *p;
          size_t len;
          char buffer[IDMAX + IDOHD + 1];
  
          // Create import name
          len = Obj::mangle(s,buffer);
          if (buffer[0] == (char)0xFF && buffer[1] == 0)
          {   name = buffer + 4;
              len -= 4;
          }
          else
          {   name = buffer + 1;
              len -= 1;
          }
          if (config.flags4 & CFG4underscore)
          {   p = (char *) alloca(5 + len + 1);
              memcpy(p,"_imp_",5);
              memcpy(p + 5,name,len);
              p[5 + len] = 0;
          }
          else
          {   p = (char *) alloca(6 + len + 1);
              memcpy(p,"__imp_",6);
              memcpy(p + 6,name,len);
              p[6 + len] = 0;
          }
          simp = scope_search(p,SCTglobal);
          if (!simp)
          {   type *t;
  
              simp = scope_define(p,SCTglobal,SCextern);
              simp->Ssequence = 0;
              simp->Sfl = FLextern;
              simp->Simport = s;
              t = newpointer(s->Stype);
              t->Tmangle = mTYman_c;
              t->Tcount++;
              simp->Stype = t;
          }
          assert(!e->EV.sp.Voffset);
          if (e->Eoper == OPrelconst)
          {
              e->Eoper = OPvar;
              e->EV.sp.Vsym = simp;
          }
          else // OPvar
          {
              e->Eoper = OPind;
              e->E1 = el_var(simp);
              e->E2 = NULL;
          }
      }
  #endif
  }
  
  /*******************************
   * Mangle a name.
   * Returns:
   *      length of mangled name
   */
  
  size_t Obj::mangle(Symbol *s,char *dest)
  {   size_t len;
      size_t ilen;
      char *name;
      char *name2 = NULL;
  
      //printf("Obj::mangle('%s'), mangle = x%x\n",s->Sident,type_mangle(s->Stype));
  #if SCPP
      name = CPP ? cpp_mangle(s) : s->Sident;
  #elif MARS
      name = cpp_mangle(s);
  #else
      name = s->Sident;
  #endif
      len = strlen(name);                 // # of bytes in name
  
      // Use as max length the max length lib.exe can handle
      // Use 5 as length of _ + @nnn
  //    #define LIBIDMAX ((512 - 0x25 - 3 - 4) - 5)
  #define LIBIDMAX 128
      if (len > LIBIDMAX)
      //if (len > IDMAX)
      {
          size_t len2;
  
          // Attempt to compress the name
          name2 = id_compress(name, len, &len2);
  #if MARS
          if (len2 > LIBIDMAX)            // still too long
          {
              /* Form md5 digest of the name and store it in the
               * last 32 bytes of the name.
               */
              MD5_CTX mdContext;
              MD5Init(&mdContext);
              MD5Update(&mdContext, (unsigned char *)name, len);
              MD5Final(&mdContext);
              memcpy(name2, name, LIBIDMAX - 32);
              for (int i = 0; i < 16; i++)
              {   unsigned char c = mdContext.digest[i];
                  unsigned char c1 = (c >> 4) & 0x0F;
                  unsigned char c2 = c & 0x0F;
                  c1 += (c1 < 10) ? '0' : 'A' - 10;
                  name2[LIBIDMAX - 32 + i * 2] = c1;
                  c2 += (c2 < 10) ? '0' : 'A' - 10;
                  name2[LIBIDMAX - 32 + i * 2 + 1] = c2;
              }
              name = name2;
              len = LIBIDMAX;
              name[len] = 0;
              //printf("name = '%s', len = %d, strlen = %d\n", name, len, strlen(name));
          }
  #else
          if (len2 > IDMAX)               // still too long
          {
  #if SCPP
              synerr(EM_identifier_too_long, name, len - IDMAX, IDMAX);
  #elif MARS
  //          error(Loc(), "identifier %s is too long by %d characters", name, len - IDMAX);
  #else
              assert(0);
  #endif
              len = IDMAX;
          }
  #endif
          else
          {
              name = name2;
              len = len2;
          }
      }
      ilen = len;
      if (ilen > (255-2-sizeof(int)*3))
          dest += 3;
      switch (type_mangle(s->Stype))
      {   case mTYman_pas:                // if upper case
          case mTYman_for:
              memcpy(dest + 1,name,len);  // copy in name
              dest[1 + len] = 0;
              strupr(dest + 1);           // to upper case
              break;
  #if SCPP || MARS
          case mTYman_cpp:
              memcpy(dest + 1,name,len);
              break;
  #endif
          case mTYman_std:
              if (!(config.flags4 & CFG4oldstdmangle) &&
                  config.exe == EX_WIN32 && tyfunc(s->ty()) &&
                  !variadic(s->Stype))
              {
                  dest[1] = '_';
                  memcpy(dest + 2,name,len);
                  dest[1 + 1 + len] = '@';
                  itoa(type_paramsize(s->Stype),dest + 3 + len,10);
                  len = strlen(dest + 1);
                  assert(isdigit(dest[len]));
                  break;
              }
          case mTYman_c:
          case mTYman_d:
              if (config.flags4 & CFG4underscore)
              {
                  dest[1] = '_';          // leading _ in name
                  memcpy(&dest[2],name,len);      // copy in name
                  len++;
                  break;
              }
          case mTYman_sys:
              memcpy(dest + 1, name, len);        // no mangling
              dest[1 + len] = 0;
              break;
          default:
              symbol_print(s);
              assert(0);
      }
      if (ilen > (255-2-sizeof(int)*3))
      {   dest -= 3;
          dest[0] = 0xFF;
          dest[1] = 0;
  #ifdef DEBUG
          assert(len <= 0xFFFF);
  #endif
          TOWORD(dest + 2,len);
          len += 4;
      }
      else
      {   *dest = (char)len;
          len++;
      }
      if (name2)
          free(name2);
      assert(len <= IDMAX + IDOHD);
      return len;
  }
  
  /*******************************
   * Export a function name.
   */
  
  void Obj::export_symbol(Symbol *s,unsigned argsize)
  {   char *coment;
      size_t len;
  
      coment = (char *) alloca(4 + 1 + (IDMAX + IDOHD) + 1); // allow extra byte for mangling
      len = Obj::mangle(s,&coment[4]);
      assert(len <= IDMAX + IDOHD);
      coment[1] = 0xA0;                           // comment class
      coment[2] = 2;                              // why??? who knows
      if (argsize >= 64)                          // we only have a 5 bit field
          argsize = 0;                            // hope we don't need callgate
      coment[3] = (argsize + 1) >> 1;             // # words on stack
      coment[4 + len] = 0;                        // no internal name
      objrecord(COMENT,coment,4 + len + 1);       // module name record
  }
  
  /*******************************
   * 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 Obj::data_start(Symbol *sdata, targ_size_t datasize, int seg)
  {
      targ_size_t alignbytes;
      //printf("Obj::data_start(%s,size %llx,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 = SegData[seg]->SDoffset;
      if (sdata->Salignment > 0)
      {   if (SegData[seg]->SDalignment < sdata->Salignment)
              SegData[seg]->SDalignment = sdata->Salignment;
          alignbytes = ((offset + sdata->Salignment - 1) & ~(sdata->Salignment - 1)) - offset;
      }
      else
          alignbytes = _align(datasize, offset) - offset;
      sdata->Soffset = offset + alignbytes;
      SegData[seg]->SDoffset = sdata->Soffset;
      return seg;
  }
  
  void Obj::func_start(Symbol *sfunc)
  {
      //printf("Obj::func_start(%s)\n",sfunc->Sident);
      symbol_debug(sfunc);
      sfunc->Sseg = cseg;             // current code seg
      sfunc->Soffset = Offset(cseg);       // offset of start of function
  
  #if MARS
      varStats.startFunction();
  #endif
  }
  
  /*******************************
   * Update function info after codgen
   */
  
  void Obj::func_term(Symbol *sfunc)
  {
  }
  
  /********************************
   * Output a public definition.
   * Input:
   *      seg =           segment index that symbol is defined in
   *      s ->            symbol
   *      offset =        offset of name
   */
  
  STATIC void outpubdata()
  {
      if (obj.pubdatai)
      {   objrecord(obj.mpubdef,obj.pubdata,obj.pubdatai);
          obj.pubdatai = 0;
      }
  }
  
  void Obj::pubdef(int seg,Symbol *s,targ_size_t offset)
  {   unsigned reclen,len;
      char *p;
      unsigned ti;
  
      assert(offset < 100000000);
      obj.reset_symbuf->write(&s, sizeof(s));
  
      int idx = SegData[seg]->segidx;
      if (obj.pubdatai + 1 + (IDMAX + IDOHD) + 4 + 2 > sizeof(obj.pubdata) ||
          idx != getindex(obj.pubdata + 1))
          outpubdata();
      if (obj.pubdatai == 0)
      {
          obj.pubdata[0] = (seg == DATA || seg == CDATA || seg == UDATA) ? 1 : 0; // group index
          obj.pubdatai += 1 + insidx(obj.pubdata + 1,idx);        // segment index
      }
      p = &obj.pubdata[obj.pubdatai];
      len = Obj::mangle(s,p);              // mangle in name
      reclen = len + intsize;
      p += len;
      TOOFFSET(p,offset);
      p += intsize;
      ti = (config.fulltypes == CVOLD) ? cv_typidx(s->Stype) : 0;
      reclen += instypidx(p,ti);
      obj.pubdatai += reclen;
  }
  
  void Obj::pubdefsize(int seg, Symbol *s, targ_size_t offset, targ_size_t symsize)
  {
      Obj::pubdef(seg, s, offset);
  }
  
  /*******************************
   * Output an external definition.
   * Input:
   *      name -> external identifier
   * Returns:
   *      External index of the definition (1,2,...)
   */
  
  STATIC void outextdata()
  {
      if (obj.extdatai)
      {   objrecord(EXTDEF,obj.extdata,obj.extdatai);
          obj.extdatai = 0;
      }
  }
  
  int Obj::external_def(const char *name)
  {   unsigned len;
      char *e;
  
      //printf("Obj::external_def('%s', %d)\n",name,obj.extidx + 1);
      assert(name);
      len = strlen(name);                 // length of identifier
      if (obj.extdatai + len + ONS_OHD + 1 > sizeof(obj.extdata))
          outextdata();
  
      e = &obj.extdata[obj.extdatai];
      len = obj_namestring(e,name);
      e[len] = 0;                         // typidx = 0
      obj.extdatai += len + 1;
      assert(obj.extdatai <= sizeof(obj.extdata));
      return ++obj.extidx;
  }
  
  /*******************************
   * Output an external definition.
   * Input:
   *      s       Symbol to do EXTDEF on
   * Returns:
   *      External index of the definition (1,2,...)
   */
  
  int Obj::external(Symbol *s)
  {
      //printf("Obj::external('%s', %d)\n",s->Sident, obj.extidx + 1);
      symbol_debug(s);
      obj.reset_symbuf->write(&s, sizeof(s));
      if (obj.extdatai + (IDMAX + IDOHD) + 3 > sizeof(obj.extdata))
          outextdata();
  
      char *e = &obj.extdata[obj.extdatai];
      unsigned len = Obj::mangle(s,e);
      e[len] = 0;                 // typidx = 0
      obj.extdatai += len + 1;
      s->Sxtrnnum = ++obj.extidx;
      return obj.extidx;
  }
  
  /*******************************
   * Output a common block definition.
   * Input:
   *      p ->    external identifier
   *      flag    TRUE:   in default data segment
   *              FALSE:  not in default data segment
   *      size    size in bytes of each elem
   *      count   number of elems
   * Returns:
   *      External index of the definition (1,2,...)
   */
  
  // Helper for Obj::common_block()
  
  static unsigned storelength(unsigned long length,unsigned i)
  {
      obj.extdata[i] = length;
      if (length >= 128)  // Microsoft docs say 129, but their linker
                          // won't take >=128, so accommodate it
      {   obj.extdata[i] = 129;
  #ifdef DEBUG
          assert(length <= 0xFFFF);
  #endif
          TOWORD(obj.extdata + i + 1,length);
          if (length >= 0x10000)
          {   obj.extdata[i] = 132;
              obj.extdata[i + 3] = length >> 16;
  
              // Only 386 can generate lengths this big
              if (I32 && length >= 0x1000000)
              {   obj.extdata[i] = 136;
                  obj.extdata[i + 4] = length >> 24;
                  i += 4;
              }
              else
                  i += 3;
          }
          else
              i += 2;
      }
      return i + 1;               // index past where we stuffed length
  }
  
  int Obj::common_block(Symbol *s,targ_size_t size,targ_size_t count)
  {
      return common_block(s, 0, size, count);
  }
  
  int Obj::common_block(Symbol *s,int flag,targ_size_t size,targ_size_t count)
  { register unsigned i;
    unsigned long length;
    unsigned ti;
  
      //printf("Obj::common_block('%s',%d,%d,%d, %d)\n",s->Sident,flag,size,count, obj.extidx + 1);
      obj.reset_symbuf->write(&s, sizeof(s));
      outextdata();               // borrow the extdata[] storage
      i = Obj::mangle(s,obj.extdata);
  
      ti = (config.fulltypes == CVOLD) ? cv_typidx(s->Stype) : 0;
      i += instypidx(obj.extdata + i,ti);
  
    if (flag)                             // if in default data segment
    {
          //printf("NEAR comdef\n");
          obj.extdata[i] = 0x62;
          length = (unsigned long) size * count;
          assert(I32 || length <= 0x10000);
          i = storelength(length,i + 1);
    }
    else
    {
          //printf("FAR comdef\n");
          obj.extdata[i] = 0x61;
          i = storelength((unsigned long) size,i + 1);
          i = storelength((unsigned long) count,i);
    }
    assert(i <= arraysize(obj.extdata));
    objrecord(COMDEF,obj.extdata,i);
    return ++obj.extidx;
  }
  
  /***************************************
   * 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);
      //pseg->SDoffset += count;
  }
  
  /***************************************
   * Output an iterated data block of 0s.
   * (uninitialized data only)
   */
  
  void Obj::lidata(int seg,targ_size_t offset,targ_size_t count)
  {   int i;
      unsigned reclen;
      static char zero[20];
      char data[20];
      char *di;
  
      //printf("Obj::lidata(seg = %d, offset = x%x, count = %d)\n", seg, offset, count);
  
      SegData[seg]->SDoffset += count;
  
      if (seg == UDATA)
          return;
      int idx = SegData[seg]->segidx;
  
  Lagain:
      if (count <= sizeof(zero))          // if shorter to use ledata
      {
          Obj::bytes(seg,offset,count,zero);
          return;
      }
  
      if (seg_is_comdat(idx))
      {
          while (count > sizeof(zero))
          {
              Obj::bytes(seg,offset,sizeof(zero),zero);
              offset += sizeof(zero);
              count -= sizeof(zero);
          }
          Obj::bytes(seg,offset,count,zero);
          return;
      }
  
      i = insidx(data,idx);
      di = data + i;
      TOOFFSET(di,offset);
  
      if (config.flags & CFGeasyomf)
      {
          if (count >= 0x8000)            // repeat count can only go to 32k
          {
              TOWORD(di + 4,(unsigned short)(count / 0x8000));
              TOWORD(di + 4 + 2,1);               // 1 data block follows
              TOWORD(di + 4 + 2 + 2,0x8000);      // repeat count
              TOWORD(di + 4 + 2 + 2 + 2,0);       // block count
              TOWORD(di + 4 + 2 + 2 + 2 + 2,1);   // 1 byte of 0
              reclen = i + 4 + 5 * 2;
              objrecord(obj.mlidata,data,reclen);
  
              offset += (count & ~0x7FFFL);
              count &= 0x7FFF;
              goto Lagain;
          }
          else
          {
              TOWORD(di + 4,(unsigned short)count);       // repeat count
              TOWORD(di + 4 + 2,0);                       // block count
              TOWORD(di + 4 + 2 + 2,1);                   // 1 byte of 0
              reclen = i + 4 + 2 + 2 + 2;
              objrecord(obj.mlidata,data,reclen);
          }
      }
      else
      {
          TOOFFSET(di + intsize,count);
          TOWORD(di + intsize * 2,0);     // block count
          TOWORD(di + intsize * 2 + 2,1); // repeat 1 byte of 0s
          reclen = i + (I32 ? 12 : 8);
          objrecord(obj.mlidata,data,reclen);
      }
      assert(reclen <= sizeof(data));
  }
  
  /****************************
   * Output a MODEND record.
   */
  
  STATIC void obj_modend()
  {
      if (obj.startaddress)
      {   char mdata[10];
          int i;
          unsigned framedatum,targetdatum;
          unsigned char fd;
          targ_size_t offset;
          int external;           // !=0 if identifier is defined externally
          tym_t ty;
          Symbol *s = obj.startaddress;
  
          // Turn startaddress into a fixup.
          // Borrow heavilly from Obj::reftoident()
  
          obj.reset_symbuf->write(&s, sizeof(s));
          symbol_debug(s);
          offset = 0;
          ty = s->ty();
  
          switch (s->Sclass)
          {
              case SCcomdat:
              case_SCcomdat:
              case SCextern:
              case SCcomdef:
                  if (s->Sxtrnnum)                // identifier is defined somewhere else
                      external = s->Sxtrnnum;
                  else
                  {
                   Ladd:
                      s->Sclass = SCextern;
                      external = objmod->external(s);
                      outextdata();
                  }
                  break;
              case SCinline:
                  if (config.flags2 & CFG2comdat)
                      goto case_SCcomdat; // treat as initialized common block
              case SCsinline:
              case SCstatic:
              case SCglobal:
                  if (s->Sseg == UNKNOWN)
                      goto Ladd;
                  if (seg_is_comdat(SegData[s->Sseg]->segidx))   // if in comdat
                      goto case_SCcomdat;
              case SClocstat:
                  external = 0;           // identifier is static or global
                                              // and we know its offset
                  offset += s->Soffset;
                  break;
              default:
                  //symbol_print(s);
                  assert(0);
          }
  
          if (external)
          {   fd = FD_T2;
              targetdatum = external;
              switch (s->Sfl)
              {
                  case FLextern:
                      if (!(ty & (mTYcs | mTYthread)))
                          goto L1;
                  case FLfunc:
                  case FLfardata:
                  case FLcsdata:
                  case FLtlsdata:
                      if (config.exe & EX_flat)
                      {   fd |= FD_F1;
                          framedatum = 1;
                      }
                      else
                      {
                  //case FLtlsdata:
                          fd |= FD_F2;
                          framedatum = targetdatum;
                      }
                      break;
                  default:
                      goto L1;
              }
          }
          else
          {
              fd = FD_T0;                 // target is always a segment
              targetdatum = SegData[s->Sseg]->segidx;
              assert(targetdatum != -1);
              switch (s->Sfl)
              {
                  case FLextern:
                      if (!(ty & (mTYcs | mTYthread)))
                          goto L1;
                  case FLfunc:
                  case FLfardata:
                  case FLcsdata:
                  case FLtlsdata:
                      if (config.exe & EX_flat)
                      {   fd |= FD_F1;
                          framedatum = 1;
                      }
                      else
                      {
                  //case FLtlsdata:
                          fd |= FD_F0;
                          framedatum = targetdatum;
                      }
                      break;
                  default:
                  L1:
                      fd |= FD_F1;
                      framedatum = DGROUPIDX;
                      //if (flags == CFseg)
                      {   fd = FD_F1 | FD_T1;     // target is DGROUP
                          targetdatum = DGROUPIDX;
                      }
                      break;
              }
          }
  
          // Write the fixup into mdata[]
          mdata[0] = 0xC1;
          mdata[1] = fd;
          i = 2 + insidx(&mdata[2],framedatum);
          i += insidx(&mdata[i],targetdatum);
          TOOFFSET(mdata + i,offset);
  
          objrecord(obj.mmodend,mdata,i + intsize);       // write mdata[] to .OBJ file
      }
      else
      {   static const char modend[] = {0};
  
          objrecord(obj.mmodend,modend,sizeof(modend));
      }
  }
  
  /****************************
   * Output the fixups in list fl.
   */
  
  STATIC void objfixupp(struct FIXUP *f)
  {
    unsigned i,j,k;
    targ_size_t locat;
    struct FIXUP *fn;
  
  #if 1   // store in one record
    char data[1024];
  
    i = 0;
    for (; f; f = fn)
    {     unsigned char fd;
  
          if (i >= sizeof(data) - (3 + 2 + 2))    // if not enough room
          {   objrecord(obj.mfixupp,data,i);
              i = 0;
          }
  
          //printf("f = %p, offset = x%x\n",f,f->FUoffset);
          assert(f->FUoffset < 1024);
          locat = (f->FUlcfd & 0xFF00) | f->FUoffset;
          data[i+0] = locat >> 8;
          data[i+1] = locat;
          data[i+2] = fd = f->FUlcfd;
          k = i;
          i += 3 + insidx(&data[i+3],f->FUframedatum);
          //printf("FUframedatum = x%x\n", f->FUframedatum);
          if ((fd >> 4) == (fd & 3) && f->FUframedatum == f->FUtargetdatum)
          {
              data[k + 2] = (fd & 15) | FD_F5;
          }
          else
          {   i += insidx(&data[i],f->FUtargetdatum);
              //printf("FUtargetdatum = x%x\n", f->FUtargetdatum);
          }
          //printf("[%d]: %02x %02x %02x\n", k, data[k + 0] & 0xFF, data[k + 1] & 0xFF, data[k + 2] & 0xFF);
          fn = f->FUnext;
          mem_ffree(f);
    }
    assert(i <= sizeof(data));
    if (i)
        objrecord(obj.mfixupp,data,i);
  #else   // store in multiple records
    for (; fl; fl = list_next(fl))
    {
          char data[7];
  
          assert(f->FUoffset < 1024);
          locat = (f->FUlcfd & 0xFF00) | f->FUoffset;
          data[0] = locat >> 8;
          data[1] = locat;
          data[2] = f->FUlcfd;
          i = 3 + insidx(&data[3],f->FUframedatum);
          i += insidx(&data[i],f->FUtargetdatum);
          objrecord(obj.mfixupp,data,i);
    }
  #endif
  }
  
  
  /***************************
   * Add a new fixup to the fixup list.
   * Write things out if we overflow the list.
   */
  
  STATIC void addfixup(Ledatarec *lr, targ_size_t offset,unsigned lcfd,
          unsigned framedatum,unsigned targetdatum)
  {   struct FIXUP *f;
  
      assert(offset < 0x1024);
  #ifdef DEBUG
      assert(targetdatum <= 0x7FFF);
      assert(framedatum <= 0x7FFF);
  #endif
      f = (struct FIXUP *) mem_fmalloc(sizeof(struct FIXUP));
      //printf("f = %p, offset = x%x\n",f,offset);
      f->FUoffset = offset;
      f->FUlcfd = lcfd;
      f->FUframedatum = framedatum;
      f->FUtargetdatum = targetdatum;
      f->FUnext = lr->fixuplist;  // link f into list
      lr->fixuplist = f;
  #ifdef DEBUG
      obj.fixup_count++;                  // gather statistics
  #endif
  }
  
  
  /*********************************
   * Open up a new ledata record.
   * Input:
   *      seg     segment number data is in
   *      offset  starting offset of start of data for this record
   */
  
  STATIC Ledatarec *ledata_new(int seg,targ_size_t offset)
  {
  
      //printf("ledata_new(seg = %d, offset = x%lx)\n",seg,offset);
      assert(seg > 0 && seg <= seg_count);
  
      if (obj.ledatai == ledatamax)
      {
          size_t o = ledatamax;
          ledatamax = o * 2 + 100;
          ledatas = (Ledatarec **)mem_realloc(ledatas, ledatamax * sizeof(Ledatarec *));
          memset(ledatas + o, 0, (ledatamax - o) * sizeof(Ledatarec *));
      }
      Ledatarec *lr = ledatas[obj.ledatai];
      if (!lr)
      {   lr = (Ledatarec *) mem_malloc(sizeof(Ledatarec));
          ledatas[obj.ledatai] = lr;
      }
      memset(lr, 0, sizeof(Ledatarec));
      ledatas[obj.ledatai] = lr;
      obj.ledatai++;
  
      lr->lseg = seg;
      lr->offset = offset;
  
      if (seg_is_comdat(SegData[seg]->segidx) && offset)      // if continuation of an existing COMDAT
      {
          Ledatarec *d = SegData[seg]->ledata;
          if (d)
          {
              if (d->lseg == seg)                 // found existing COMDAT
              {   lr->flags = d->flags;
                  lr->alloctyp = d->alloctyp;
                  lr->align = d->align;
                  lr->typidx = d->typidx;
                  lr->pubbase = d->pubbase;
                  lr->pubnamidx = d->pubnamidx;
              }
          }
      }
      SegData[seg]->ledata = lr;
      return lr;
  }
  
  /***********************************
   * Append byte to segment.
   */
  
  void Obj::write_byte(seg_data *pseg, unsigned byte)
  {
      Obj::_byte(pseg->SDseg, pseg->SDoffset, byte);
      pseg->SDoffset++;
  }
  
  /************************************
   * Output byte to object file.
   */
  
  void Obj::_byte(int seg,targ_size_t offset,unsigned byte)
  {
      Ledatarec *lr = SegData[seg]->ledata;
      if (!lr)
          goto L2;
  
      if (
           lr->i > LEDATAMAX - 1 ||       // if it'll overflow
           offset < lr->offset || // underflow
           offset > lr->offset + lr->i
       )
      {
          // Try to find an existing ledata
          for (size_t i = obj.ledatai; i; )
          {   Ledatarec *d = ledatas[--i];
              if (seg == d->lseg &&       // segments match
                  offset >= d->offset &&
                  offset + 1 <= d->offset + LEDATAMAX &&
                  offset <= d->offset + d->i
                 )
              {
                  lr = SegData[seg]->ledata = d;
                  goto L1;
              }
          }
  L2:
          lr = ledata_new(seg,offset);
  L1:     ;
      }
  
      unsigned i = offset - lr->offset;
      if (lr->i <= i)
          lr->i = i + 1;
      lr->data[i] = byte;           // 1st byte of data
  }
  
  /***********************************
   * Append bytes to segment.
   */
  
  void Obj::write_bytes(seg_data *pseg, unsigned nbytes, void *p)
  {
      Obj::bytes(pseg->SDseg, pseg->SDoffset, nbytes, p);
      pseg->SDoffset += nbytes;
  }
  
  /************************************
   * Output bytes to object file.
   * Returns:
   *      nbytes
   */
  
  unsigned Obj::bytes(int seg,targ_size_t offset,unsigned nbytes, void *p)
  {   unsigned n = nbytes;
  
      //dbg_printf("Obj::bytes(seg=%d, offset=x%lx, nbytes=x%x, p=%p)\n",seg,offset,nbytes,p);
      Ledatarec *lr = SegData[seg]->ledata;
      if (!lr)
          lr = ledata_new(seg, offset);
   L1:
      if (
           lr->i + nbytes > LEDATAMAX ||  // or it'll overflow
           offset < lr->offset ||         // underflow
           offset > lr->offset + lr->i
       )
      {
          while (nbytes)
          {   Obj::_byte(seg,offset,*(char *)p);
              offset++;
              p = ((char *)p) + 1;
              nbytes--;
              lr = SegData[seg]->ledata;
              if (lr->i + nbytes <= LEDATAMAX)
                  goto L1;
          }
      }
      else
      {
          unsigned i = offset - lr->offset;
          if (lr->i < i + nbytes)
              lr->i = i + nbytes;
          memcpy(lr->data + i,p,nbytes);
      }
      return n;
  }
  
  /************************************
   * Output word of data. (Two words if segment:offset pair.)
   * Input:
   *      seg     CODE, DATA, CDATA, UDATA
   *      offset  offset of start of data
   *      data    word of data
   *      lcfd    LCxxxx | FDxxxx
   *      if (FD_F2 | FD_T6)
   *              idx1 = external Symbol #
   *      else
   *              idx1 = frame datum
   *              idx2 = target datum
   */
  
  void Obj::ledata(int seg,targ_size_t offset,targ_size_t data,
          unsigned lcfd,unsigned idx1,unsigned idx2)
  {
      unsigned size;                      // number of bytes to output
  
      unsigned ptrsize = tysize(TYfptr);
  
      if ((lcfd & LOCxx) == obj.LOCpointer)
          size = ptrsize;
      else if ((lcfd & LOCxx) == LOCbase)
          size = 2;
      else
          size = tysize(TYnptr);
  
      Ledatarec *lr = SegData[seg]->ledata;
      if (!lr)
           lr = ledata_new(seg, offset);
      assert(seg == lr->lseg);
      if (
           lr->i + size > LEDATAMAX ||    // if it'll overflow
           offset < lr->offset || // underflow
           offset > lr->offset + lr->i
       )
      {
          // Try to find an existing ledata
  //dbg_printf("seg = %d, offset = x%lx, size = %d\n",seg,offset,size);
          for (size_t i = obj.ledatai; i; )
          {   Ledatarec *d = ledatas[--i];
  
  //dbg_printf("d: seg = %d, offset = x%lx, i = x%x\n",d->lseg,d->offset,d->i);
              if (seg == d->lseg &&       // segments match
                  offset >= d->offset &&
                  offset + size <= d->offset + LEDATAMAX &&
                  offset <= d->offset + d->i
                 )
              {
  //dbg_printf("match\n");
                  lr = SegData[seg]->ledata = d;
                  goto L1;
              }
          }
          lr = ledata_new(seg,offset);
  L1:     ;
      }
  
      unsigned i = offset - lr->offset;
      if (lr->i < i + size)
          lr->i = i + size;
      if (size == 2 || !I32)
          TOWORD(lr->data + i,data);
      else
          TOLONG(lr->data + i,data);
      if (size == ptrsize)         // if doing a seg:offset pair
          TOWORD(lr->data + i + tysize(TYnptr),0);        // segment portion
      addfixup(lr, offset - lr->offset,lcfd,idx1,idx2);
  }
  
  /************************************
   * Output long word of data.
   * Input:
   *      seg     CODE, DATA, CDATA, UDATA
   *      offset  offset of start of data
   *      data    long word of data
   *   Present only if size == 2:
   *      lcfd    LCxxxx | FDxxxx
   *      if (FD_F2 | FD_T6)
   *              idx1 = external Symbol #
   *      else
   *              idx1 = frame datum
   *              idx2 = target datum
   */
  
  void Obj::write_long(int seg,targ_size_t offset,unsigned data,
          unsigned lcfd,unsigned idx1,unsigned idx2)
  {
      unsigned sz = tysize(TYfptr);
      Ledatarec *lr = SegData[seg]->ledata;
      if (!lr)
           lr = ledata_new(seg, offset);
      if (
           lr->i + sz > LEDATAMAX || // if it'll overflow
           offset < lr->offset || // underflow
           offset > lr->offset + lr->i
         )
          lr = ledata_new(seg,offset);
      unsigned i = offset - lr->offset;
      if (lr->i < i + sz)
          lr->i = i + sz;
      TOLONG(lr->data + i,data);
      if (I32)                              // if 6 byte far pointers
          TOWORD(lr->data + i + LONGSIZE,0);              // fill out seg
      addfixup(lr, offset - lr->offset,lcfd,idx1,idx2);
  }
  
  /*******************************
   * 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:
   *              Obj::reftodatseg(DATA,offset,3 * sizeof(int *),UDATA);
   */
  
  void Obj::reftodatseg(int seg,targ_size_t offset,targ_size_t val,
          unsigned targetdatum,int flags)
  {
      assert(flags);
  
      if (flags == 0 || flags & CFoff)
      {
          // The frame datum is always 1, which is DGROUP
          Obj::ledata(seg,offset,val,
              LOCATsegrel | obj.LOCoffset | FD_F1 | FD_T4,DGROUPIDX,SegData[targetdatum]->segidx);
          offset += intsize;
      }
  
      if (flags & CFseg)
      {
  #if 0
          if (config.wflags & WFdsnedgroup)
              warerr(WM_ds_ne_dgroup);
  #endif
          Obj::ledata(seg,offset,0,
              LOCATsegrel | LOCbase | FD_F1 | FD_T5,DGROUPIDX,DGROUPIDX);
      }
  }
  
  /*******************************
   * Refer to address that is in a far segment.
   * Input:
   *      seg =           where the address is going
   *      offset =        offset within seg
   *      val =           displacement from address
   *      farseg =        far segment index
   *      flags =         CFoff, CFseg
   */
  
  void Obj::reftofarseg(int seg,targ_size_t offset,targ_size_t val,
          int farseg,int flags)
  {
      assert(flags);
  
      int idx = SegData[farseg]->segidx;
      if (flags == 0 || flags & CFoff)
      {
          Obj::ledata(seg,offset,val,
              LOCATsegrel | obj.LOCoffset | FD_F0 | FD_T4,idx,idx);
          offset += intsize;
      }
  
      if (flags & CFseg)
      {
          Obj::ledata(seg,offset,0,
              LOCATsegrel | LOCbase | FD_F0 | FD_T4,idx,idx);
      }
  }
  
  /*******************************
   * 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 Obj::reftocodeseg(int seg,targ_size_t offset,targ_size_t val)
  {   unsigned framedatum;
      unsigned lcfd;
  
      int idx = SegData[cseg]->segidx;
      if (seg_is_comdat(idx))             // if comdat
      {   idx = -idx;
          framedatum = idx;
          lcfd = (LOCATsegrel | obj.LOCoffset) | (FD_F2 | FD_T6);
      }
      else if (config.exe & EX_flat)
      {   framedatum = 1;
          lcfd = (LOCATsegrel | obj.LOCoffset) | (FD_F1 | FD_T4);
      }
      else
      {   framedatum = idx;
          lcfd = (LOCATsegrel | obj.LOCoffset) | (FD_F0 | FD_T4);
      }
  
      Obj::ledata(seg,offset,val,lcfd,framedatum,idx);
  }
  
  /*******************************
   * Refer to an identifier.
   * Input:
   *      seg =           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
   * Returns:
   *      number of bytes in reference (2 or 4)
   * Example:
   *      extern int def[];
   *      int *abc = &def[3];
   *      to allocate storage:
   *              Obj::reftodatseg(DATA,offset,3 * sizeof(int *),UDATA);
   */
  
  int Obj::reftoident(int seg,targ_size_t offset,Symbol *s,targ_size_t val,
          int flags)
  {
      unsigned targetdatum;       // which datum the symbol is in
      unsigned framedatum;
      int     lc;
      int     external;           // !=0 if identifier is defined externally
      int numbytes;
      tym_t ty;
  
  #if 0
      printf("Obj::reftoident('%s' seg %d, offset x%lx, val x%lx, flags x%x)\n",
          s->Sident,seg,offset,val,flags);
      printf("Sseg = %d, Sxtrnnum = %d\n",s->Sseg,s->Sxtrnnum);
      symbol_print(s);
  #endif
      assert(seg > 0);
  
      ty = s->ty();
      while (1)
      {
          switch (flags & (CFseg | CFoff))
          {   case 0:
                  // Select default
                  flags |= CFoff;
                  if (tyfunc(ty))
                  {
                      if (tyfarfunc(ty))
                          flags |= CFseg;
                  }
                  else // DATA
                  {
                      if (LARGEDATA)
                          flags |= CFseg;
                  }
                  continue;
              case CFoff:
                  if (I32)
                  {
  #if 1
                      if (ty & mTYthread)
                      {   lc = LOC32tlsoffset;
                      }
                      else
  #endif
                          lc = obj.LOCoffset;
                  }
                  else
                  {
                      // The 'loader_resolved' offset is required for VCM
                      // and Windows support. A fixup of this type is
                      // relocated by the linker to point to a 'thunk'.
                      lc = (tyfarfunc(ty)
                            && !(flags & CFselfrel))
                              ? LOCloader_resolved : obj.LOCoffset;
                  }
                  numbytes = tysize(TYnptr);
                  break;
              case CFseg:
                  lc = LOCbase;
                  numbytes = 2;
                  break;
              case CFoff | CFseg:
                  lc = obj.LOCpointer;
                  numbytes = tysize(TYfptr);
                  break;
          }
          break;
      }
  
      switch (s->Sclass)
      {
          case SCcomdat:
          case_SCcomdat:
          case SCextern:
          case SCcomdef:
              if (s->Sxtrnnum)            // identifier is defined somewhere else
              {   external = s->Sxtrnnum;
  #ifdef DEBUG
                  if (external > obj.extidx)
                  {
                      printf("obj.extidx = %d\n", obj.extidx);
                      symbol_print(s);
                  }
  #endif
                  assert(external <= obj.extidx);
              }
              else
              {   // Don't know yet, worry about it later
               Ladd:
                  size_t byteswritten = addtofixlist(s,offset,seg,val,flags);
                  assert(byteswritten == numbytes);
                  return numbytes;
              }
              break;
          case SCinline:
              if (config.flags2 & CFG2comdat)
                  goto case_SCcomdat;     // treat as initialized common block
          case SCsinline:
          case SCstatic:
          case SCglobal:
              if (s->Sseg == UNKNOWN)
                  goto Ladd;
              if (seg_is_comdat(SegData[s->Sseg]->segidx))
                  goto case_SCcomdat;
          case SClocstat:
              external = 0;               // identifier is static or global
                                          // and we know its offset
              if (flags & CFoff)
                  val += s->Soffset;
              break;
          default:
              symbol_print(s);
              assert(0);
      }
  
      lc |= (flags & CFselfrel) ? LOCATselfrel : LOCATsegrel;
      if (external)
      {   lc |= FD_T6;
          targetdatum = external;
          switch (s->Sfl)
          {
              case FLextern:
                  if (!(ty & (mTYcs | mTYthread)))
                      goto L1;
              case FLfunc:
              case FLfardata:
              case FLcsdata:
              case FLtlsdata:
                  if (config.exe & EX_flat)
                  {   lc |= FD_F1;
                      framedatum = 1;
                  }
                  else
                  {
              //case FLtlsdata:
                      lc |= FD_F2;
                      framedatum = targetdatum;
                  }
                  break;
              default:
                  goto L1;
          }
      }
      else
      {
          lc |= FD_T4;                    // target is always a segment
          targetdatum = SegData[s->Sseg]->segidx;
          assert(s->Sseg != UNKNOWN);
          switch (s->Sfl)
          {
              case FLextern:
                  if (!(ty & (mTYcs | mTYthread)))
                      goto L1;
              case FLfunc:
              case FLfardata:
              case FLcsdata:
              case FLtlsdata:
                  if (config.exe & EX_flat)
                  {   lc |= FD_F1;
                      framedatum = 1;
                  }
                  else
                  {
              //case FLtlsdata:
                      lc |= FD_F0;
                      framedatum = targetdatum;
                  }
                  break;
              default:
              L1:
                  lc |= FD_F1;
                  framedatum = DGROUPIDX;
                  if (flags == CFseg)
                  {   lc = LOCATsegrel | LOCbase | FD_F1 | FD_T5;
                      targetdatum = DGROUPIDX;
                  }
  #if 0
                  if (flags & CFseg && config.wflags & WFdsnedgroup)
                      warerr(WM_ds_ne_dgroup);
  #endif
                  break;
          }
      }
  
      Obj::ledata(seg,offset,val,lc,framedatum,targetdatum);
      return numbytes;
  }
  
  /*****************************************
   * Generate far16 thunk.
   * Input:
   *      s       Symbol to generate a thunk for
   */
  
  void Obj::far16thunk(Symbol *s)
  {
      static unsigned char cod32_1[] =
      {
          0x55,                           //      PUSH    EBP
          0x8B,0xEC,                      //      MOV     EBP,ESP
          0x83,0xEC,0x04,                 //      SUB     ESP,4
          0x53,                           //      PUSH    EBX
          0x57,                           //      PUSH    EDI
          0x56,                           //      PUSH    ESI
          0x06,                           //      PUSH    ES
          0x8C,0xD2,                      //      MOV     DX,SS
          0x80,0xE2,0x03,                 //      AND     DL,3
          0x80,0xCA,0x07,                 //      OR      DL,7
          0x89,0x65,0xFC,                 //      MOV     -4[EBP],ESP
          0x8C,0xD0,                      //      MOV     AX,SS
          0x66,0x3D, // 0x00,0x00 */      /*      CMP     AX,seg FLAT:_DATA
      };
      static unsigned char cod32_2[] =
      {   0x0F,0x85,0x10,0x00,0x00,0x00,  //      JNE     L1
          0x8B,0xC4,                      //      MOV     EAX,ESP
          0x66,0x3D,0x00,0x08,            //      CMP     AX,2048
          0x0F,0x83,0x04,0x00,0x00,0x00,  //      JAE     L1
          0x66,0x33,0xC0,                 //      XOR     AX,AX
          0x94,                           //      XCHG    ESP,EAX
                                          // L1:
          0x55,                           //      PUSH    EBP
          0x8B,0xC4,                      //      MOV     EAX,ESP
          0x16,                           //      PUSH    SS
          0x50,                           //      PUSH    EAX
          0x8D,0x75,0x08,                 //      LEA     ESI,8[EBP]
          0x81,0xEC,0x00,0x00,0x00,0x00,  //      SUB     ESP,numparam
          0x8B,0xFC,                      //      MOV     EDI,ESP
          0xB9,0x00,0x00,0x00,0x00,       //      MOV     ECX,numparam
          0x66,0xF3,0xA4,                 //      REP     MOVSB
          0x8B,0xC4,                      //      MOV     EAX,ESP
          0xC1,0xC8,0x10,                 //      ROR     EAX,16
          0x66,0xC1,0xE0,0x03,            //      SHL     AX,3
          0x0A,0xC2,                      //      OR      AL,DL
          0xC1,0xC0,0x10,                 //      ROL     EAX,16
          0x50,                           //      PUSH    EAX
          0x66,0x0F,0xB2,0x24,0x24,       //      LSS     SP,[ESP]
          0x66,0xEA, // 0,0,0,0, */       /*      JMPF    L3
      };
      static unsigned char cod32_3[] =
      {                                   // L2:
          0xC1,0xE0,0x10,                 //      SHL     EAX,16
          0x0F,0xAC,0xD0,0x10,            //      SHRD    EAX,EDX,16
          0x0F,0xB7,0xE4,                 //      MOVZX   ESP,SP
          0x0F,0xB2,0x24,0x24,            //      LSS     ESP,[ESP]
          0x5D,                           //      POP     EBP
          0x8B,0x65,0xFC,                 //      MOV     ESP,-4[EBP]
          0x07,                           //      POP     ES
          0x5E,                           //      POP     ESI
          0x5F,                           //      POP     EDI
          0x5B,                           //      POP     EBX
          0xC9,                           //      LEAVE
          0xC2,0x00,0x00                  //      RET     numparam
      };
  
      unsigned numparam = 24;
      targ_size_t L2offset;
      int idx;
  
      s->Sclass = SCstatic;
      s->Sseg = cseg;             // identifier is defined in code segment
      s->Soffset = Offset(cseg);
  
      // Store numparam into right places
      assert((numparam & 0xFFFF) == numparam);    // 2 byte value
      TOWORD(&cod32_2[32],numparam);
      TOWORD(&cod32_2[32 + 7],numparam);
      TOWORD(&cod32_3[sizeof(cod32_3) - 2],numparam);
  
      //------------------------------------------
      // Generate CODE16 segment if it isn't there already
      if (obj.code16segi == 0)
      {
          // Define CODE16 segment for far16 thunks
  
          static char lname[] = { "\06CODE16" };
  
          // Put out LNAMES record
          objrecord(LNAMES,lname,sizeof(lname) - 1);
  
          obj.code16segi = obj_newfarseg(0,4);
          obj.CODE16offset = 0;
  
          // class CODE
          unsigned attr = SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);
          SegData[obj.code16segi]->attr = attr;
          objsegdef(attr,0,obj.lnameidx++,4);
          obj.segidx++;
      }
  
      //------------------------------------------
      // Output the 32 bit thunk
  
      Obj::bytes(cseg,Offset(cseg),sizeof(cod32_1),cod32_1);
      Offset(cseg) += sizeof(cod32_1);
  
      // Put out fixup for SEG FLAT:_DATA
      Obj::ledata(cseg,Offset(cseg),0,LOCATsegrel|LOCbase|FD_F1|FD_T4,
          DGROUPIDX,DATA);
      Offset(cseg) += 2;
  
      Obj::bytes(cseg,Offset(cseg),sizeof(cod32_2),cod32_2);
      Offset(cseg) += sizeof(cod32_2);
  
      // Put out fixup to CODE16 part of thunk
      Obj::ledata(cseg,Offset(cseg),obj.CODE16offset,LOCATsegrel|LOC16pointer|FD_F0|FD_T4,
          SegData[obj.code16segi]->segidx,
          SegData[obj.code16segi]->segidx);
      Offset(cseg) += 4;
  
      L2offset = Offset(cseg);
      Obj::bytes(cseg,Offset(cseg),sizeof(cod32_3),cod32_3);
      Offset(cseg) += sizeof(cod32_3);
  
      s->Ssize = Offset(cseg) - s->Soffset;            // size of thunk
  
      //------------------------------------------
      // Output the 16 bit thunk
  
      Obj::_byte(obj.code16segi,obj.CODE16offset++,0x9A);       //      CALLF   function
  
      // Make function external
      idx = Obj::external(s);                         // use Pascal name mangling
  
      // Output fixup for function
      Obj::ledata(obj.code16segi,obj.CODE16offset,0,LOCATsegrel|LOC16pointer|FD_F2|FD_T6,
          idx,idx);
      obj.CODE16offset += 4;
  
      Obj::bytes(obj.code16segi,obj.CODE16offset,3,"\x66\x67\xEA");    // JMPF L2
      obj.CODE16offset += 3;
  
      Obj::ledata(obj.code16segi,obj.CODE16offset,L2offset,
          LOCATsegrel | LOC32pointer | FD_F1 | FD_T4,
          DGROUPIDX,
          SegData[cseg]->segidx);
      obj.CODE16offset += 6;
  
      SegData[obj.code16segi]->SDoffset = obj.CODE16offset;
  }
  
  /**************************************
   * Mark object file as using floating point.
   */
  
  void Obj::fltused()
  {
      if (!obj.fltused)
      {
          obj.fltused = 1;
          if (!(config.flags3 & CFG3wkfloat))
              Obj::external_def("__fltused");
      }
  }
  
  symbol *Obj::tlv_bootstrap()
  {
      // specific for Mach-O
      assert(0);
      return NULL;
  }
  
  void Obj::gotref(Symbol *s)
  {
  }
  
  /*****************************************
   * write a reference to a mutable pointer into the object file
   * Params:
   *      s    = symbol that contains the pointer
   *      soff = offset of the pointer inside the Symbol's memory
   */
  
  void Obj::write_pointerRef(Symbol* s, unsigned soff)
  {
      if (!obj.ptrref_buf)
          obj.ptrref_buf = new Outbuffer;
  
      // defer writing pointer references until the symbols are written out
      obj.ptrref_buf->write(&s, sizeof(s));
      obj.ptrref_buf->write32(soff);
  }
  
  /*****************************************
   * flush a single pointer reference saved by write_pointerRef
   * to the object file
   * Params:
   *      s    = symbol that contains the pointer
   *      soff = offset of the pointer inside the Symbol's memory
   */
  STATIC void objflush_pointerRef(Symbol* s, unsigned soff)
  {
      bool isTls = (s->Sfl == FLtlsdata);
      int &segi = isTls ? obj.tlsrefsegi : obj.datrefsegi;
      symbol_debug(s);
  
      if (segi == 0)
      {
          // We need to always put out the segments in triples, so that the
          // linker will put them in the correct order.
          static char lnames_dat[] = { "\03DPB\02DP\03DPE" };
          static char lnames_tls[] = { "\03TPB\02TP\03TPE" };
          char* lnames = isTls ? lnames_tls : lnames_dat;
          // Put out LNAMES record
          objrecord(LNAMES,lnames,sizeof(lnames_dat) - 1);
  
          int dsegattr = obj.csegattr;
  
          // Put out beginning segment
          objsegdef(dsegattr,0,obj.lnameidx,CODECLASS);
          obj.lnameidx++;
          obj.segidx++;
  
          // Put out segment definition record
          segi = obj_newfarseg(0,CODECLASS);
          objsegdef(dsegattr,0,obj.lnameidx,CODECLASS);
          SegData[segi]->attr = dsegattr;
          assert(SegData[segi]->segidx == obj.segidx);
  
          // Put out ending segment
          objsegdef(dsegattr,0,obj.lnameidx + 1,CODECLASS);
  
          obj.lnameidx += 2;              // for next time
          obj.segidx += 2;
      }
  
      targ_size_t offset = SegData[segi]->SDoffset;
      offset += objmod->reftoident(segi, offset, s, soff, CFoff);
      SegData[segi]->SDoffset = offset;
  }
  
  /*****************************************
   * flush all pointer references saved by write_pointerRef
   * to the object file
   */
  STATIC void objflush_pointerRefs()
  {
      if (!obj.ptrref_buf)
          return;
  
      unsigned char *p = obj.ptrref_buf->buf;
      unsigned char *end = obj.ptrref_buf->p;
      while (p < end)
      {
          Symbol* s = *(Symbol**)p;
          p += sizeof(s);
          unsigned soff = *(unsigned*)p;
          p += sizeof(soff);
          objflush_pointerRef(s, soff);
      }
      obj.ptrref_buf->reset();
  }
  
  #endif
  #endif
  