1 /**
2  * Compiler implementation of the
3  * $(LINK2 http://www.dlang.org, D programming language).
4  *
5  * Copyright:   Copyright (C) 1984-1998 by Symantec
6  *              Copyright (C) 2000-2020 by The D Language Foundation, All Rights Reserved
7  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
8  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/backend/cgobj.d, backend/cgobj.d)
10  */
11 
12 module dmd.backend.cgobj;
13 
14 version (SCPP)
15     version = COMPILE;
16 version (MARS)
17     version = COMPILE;
18 
19 version (COMPILE)
20 {
21 
22 import core.stdc.ctype;
23 import core.stdc.stdio;
24 import core.stdc.stdlib;
25 import core.stdc.string;
26 
27 import dmd.backend.barray;
28 import dmd.backend.cc;
29 import dmd.backend.cdef;
30 import dmd.backend.cgcv;
31 import dmd.backend.code;
32 import dmd.backend.code_x86;
33 import dmd.backend.dlist;
34 import dmd.backend.dvec;
35 import dmd.backend.el;
36 import dmd.backend.md5;
37 import dmd.backend.mem;
38 import dmd.backend.global;
39 import dmd.backend.obj;
40 import dmd.backend.oper;
41 import dmd.backend.outbuf;
42 import dmd.backend.rtlsym;
43 import dmd.backend.ty;
44 import dmd.backend.type;
45 
46 extern (C++):
47 
48 nothrow:
49 
50 version (SCPP)
51 {
52     import filespec;
53     import msgs2;
54     import scopeh;
55 
56     extern(C) char* strupr(char*);
57     extern(C) char* itoa(int,char*,int);
58     extern(C) char* getcwd(char*,size_t);
59 }
60 
61 version (MARS)
62 {
63     import dmd.backend.dvarstats;
64 
65     //import dmd.backend.filespec;
66     char *filespecdotext(const(char)* filespec);
67     char *filespecgetroot(const(char)* name);
68     char *filespecname(const(char)* filespec);
69 
70     version (Windows)
71     {
72         extern (C) int stricmp(const(char)*, const(char)*) pure nothrow @nogc;
73         alias filespeccmp = stricmp;
74     }
75     else
76         alias filespeccmp = strcmp;
77 
78     extern(C) char* strupr(char*);
79     extern(C) char* itoa(int,char*,int);
80     extern(C) char* getcwd(char*,size_t);
81 
82 struct Loc
83 {
84     char *filename;
85     uint linnum;
86     uint charnum;
87 
88     this(int y, int x)
89     {
90         linnum = y;
91         charnum = x;
92         filename = null;
93     }
94 }
95 
96 static if (__VERSION__ < 2092)
97     void error(Loc loc, const(char)* format, ...);
98 else
99     pragma(printf) void error(Loc loc, const(char)* format, ...);
100 }
101 
102 version (MARS)
103 {
104 // C++ name mangling is handled by front end
105 const(char)* cpp_mangle(Symbol* s) { return s.Sident.ptr; }
106 }
107 
108 static if (TARGET_WINDOS)
109 {
110 
111 enum MULTISCOPE = 1;            /* account for bug in MultiScope debugger
112                                    where it cannot handle a line number
113                                    with multiple offsets. We use a bit vector
114                                    to filter out the extra offsets.
115                                  */
116 
117 extern (C) void TOOFFSET(void* p, targ_size_t value);
118 
119 void TOWORD(void* a, uint b)
120 {
121     *cast(ushort*)a = cast(ushort)b;
122 }
123 
124 void TOLONG(void* a, uint b)
125 {
126     *cast(uint*)a = b;
127 }
128 
129 
130 /**************************
131  * Record types:
132  */
133 
134 enum
135 {
136     RHEADR  = 0x6E,
137     REGINT  = 0x70,
138     REDATA  = 0x72,
139     RIDATA  = 0x74,
140     OVLDEF  = 0x76,
141     ENDREC  = 0x78,
142     BLKDEF  = 0x7A,
143     BLKEND  = 0x7C,
144 //  DEBSYM  = 0x7E,
145     THEADR  = 0x80,
146     LHEADR  = 0x82,
147     PEDATA  = 0x84,
148     PIDATA  = 0x86,
149     COMENT  = 0x88,
150     MODEND  = 0x8A,
151     EXTDEF  = 0x8C,
152     TYPDEF  = 0x8E,
153     PUBDEF  = 0x90,
154     PUB386  = 0x91,
155     LOCSYM  = 0x92,
156     LINNUM  = 0x94,
157     LNAMES  = 0x96,
158     SEGDEF  = 0x98,
159     SEG386  = 0x99,
160     GRPDEF  = 0x9A,
161     FIXUPP  = 0x9C,
162     FIX386  = 0x9D,
163     LEDATA  = 0xA0,
164     LED386  = 0xA1,
165     LIDATA  = 0xA2,
166     LID386  = 0xA3,
167     LIBHED  = 0xA4,
168     LIBNAM  = 0xA6,
169     LIBLOC  = 0xA8,
170     LIBDIC  = 0xAA,
171     COMDEF  = 0xB0,
172     LEXTDEF = 0xB4,
173     LPUBDEF = 0xB6,
174     LCOMDEF = 0xB8,
175     CEXTDEF = 0xBC,
176     COMDAT  = 0xC2,
177     LINSYM  = 0xC4,
178     ALIAS   = 0xC6,
179     LLNAMES = 0xCA,
180 }
181 
182 // Some definitions for .OBJ files. Trial and error to determine which
183 // one to use when. Page #s refer to Intel spec on .OBJ files.
184 
185 // Values for LOCAT byte: (pg. 71)
186 enum
187 {
188     LOCATselfrel            = 0x8000,
189     LOCATsegrel             = 0xC000,
190 
191 // OR'd with one of the following:
192     LOClobyte               = 0x0000,
193     LOCbase                 = 0x0800,
194     LOChibyte               = 0x1000,
195     LOCloader_resolved      = 0x1400,
196 
197 // Unfortunately, the fixup stuff is different for EASY OMF and Microsoft
198     EASY_LOCoffset          = 0x1400,          // 32 bit offset
199     EASY_LOCpointer         = 0x1800,          // 48 bit seg/offset
200 
201     LOC32offset             = 0x2400,
202     LOC32tlsoffset          = 0x2800,
203     LOC32pointer            = 0x2C00,
204 
205     LOC16offset             = 0x0400,
206     LOC16pointer            = 0x0C00,
207 
208     LOCxx                   = 0x3C00
209 }
210 
211 // FDxxxx are constants for the FIXDAT byte in fixup records (pg. 72)
212 
213 enum
214 {
215     FD_F0 = 0x00,            // segment index
216     FD_F1 = 0x10,            // group index
217     FD_F2 = 0x20,            // external index
218     FD_F4 = 0x40,            // canonic frame of LSEG that contains Location
219     FD_F5 = 0x50,            // Target determines the frame
220 
221     FD_T0 = 0,               // segment index
222     FD_T1 = 1,               // group index
223     FD_T2 = 2,               // external index
224     FD_T4 = 4,               // segment index, 0 displacement
225     FD_T5 = 5,               // group index, 0 displacement
226     FD_T6 = 6,               // external index, 0 displacement
227 }
228 
229 /***************
230  * Fixup list.
231  */
232 
233 struct FIXUP
234 {
235     FIXUP              *FUnext;
236     targ_size_t         FUoffset;       // offset from start of ledata
237     ushort              FUlcfd;         // LCxxxx | FDxxxx
238     ushort              FUframedatum;
239     ushort              FUtargetdatum;
240 }
241 
242 FIXUP* list_fixup(list_t fl) { return cast(FIXUP *)list_ptr(fl); }
243 
244 int seg_is_comdat(int seg) { return seg < 0; }
245 
246 /*****************************
247  * Ledata records
248  */
249 
250 enum LEDATAMAX = 1024-14;
251 
252 struct Ledatarec
253 {
254     ubyte[14] header;           // big enough to handle COMDAT header
255     ubyte[LEDATAMAX] data;
256     int lseg;                   // segment value
257     uint i;                     // number of bytes in data
258     targ_size_t offset;         // segment offset of start of data
259     FIXUP *fixuplist;           // fixups for this ledata
260 
261     // For COMDATs
262     ubyte flags;                // flags byte of COMDAT
263     ubyte alloctyp;             // allocation type of COMDAT
264     ubyte _align;               // align type
265     int typidx;
266     int pubbase;
267     int pubnamidx;
268 }
269 
270 /*****************************
271  * For defining segments.
272  */
273 
274 uint SEG_ATTR(uint A, uint C, uint B, uint P)
275 {
276     return (A << 5) | (C << 2) | (B << 1) | P;
277 }
278 
279 enum
280 {
281 // Segment alignment A
282     SEG_ALIGN0    = 0,       // absolute segment
283     SEG_ALIGN1    = 1,       // byte align
284     SEG_ALIGN2    = 2,       // word align
285     SEG_ALIGN16   = 3,       // paragraph align
286     SEG_ALIGN4K   = 4,       // 4Kb page align
287     SEG_ALIGN4    = 5,       // dword align
288 
289 // Segment combine types C
290     SEG_C_ABS     = 0,
291     SEG_C_PUBLIC  = 2,
292     SEG_C_STACK   = 5,
293     SEG_C_COMMON  = 6,
294 
295 // Segment type P
296     USE16 = 0,
297     USE32 = 1,
298 
299     USE32_CODE    = (4+2),          // use32 + execute/read
300     USE32_DATA    = (4+3),          // use32 + read/write
301 }
302 
303 /*****************************
304  * Line number support.
305  */
306 
307 struct Linnum
308 {
309 version (MARS)
310         const(char)* filename;  // source file name
311 else
312         Sfile *filptr;          // file pointer
313 
314         int cseg;               // our internal segment number
315         int seg;                // segment/public index
316         Outbuffer data;         // linnum/offset data
317 
318         void reset() nothrow
319         {
320             data.reset();
321         }
322 }
323 
324 /*****************************
325  */
326 struct PtrRef
327 {
328   align(4):
329     Symbol* sym;
330     uint offset;
331 }
332 
333 enum LINRECMAX = 2 + 255 * 2;   // room for 255 line numbers
334 
335 /************************************
336  * State of object file.
337  */
338 
339 struct Objstate
340 {
341     const(char)* modname;
342     char *csegname;
343     Outbuffer *buf;     // output buffer
344 
345     int fdsegattr;      // far data segment attribute
346     int csegattr;       // code segment attribute
347 
348     int lastfardatasegi;        // SegData[] index of last far data seg
349 
350     int LOCoffset;
351     int LOCpointer;
352 
353     int mlidata;
354     int mpubdef;
355     int mfixupp;
356     int mmodend;
357 
358     int lnameidx;               // index of next LNAMES record
359     int segidx;                 // index of next SEGDEF record
360     int extidx;                 // index of next EXTDEF record
361     int pubnamidx;              // index of COMDAT public name index
362 
363     Symbol *startaddress;       // if !null, then Symbol is start address
364 
365     debug
366     int fixup_count;
367 
368     // Line numbers
369     char *linrec;               // line number record
370     uint linreci;               // index of next avail in linrec[]
371     uint linrecheader;          // size of line record header
372     uint linrecnum;             // number of line record entries
373     int mlinnum;
374     int recseg;
375     int term;
376 static if (MULTISCOPE)
377 {
378     vec_t linvec;               // bit vector of line numbers used
379     vec_t offvec;               // and offsets used
380 }
381 
382     int fisegi;                 // SegData[] index of FI segment
383 
384 version (MARS)
385 {
386     int fmsegi;                 // SegData[] of FM segment
387     int datrefsegi;             // SegData[] of DATA pointer ref segment
388     int tlsrefsegi;             // SegData[] of TLS pointer ref segment
389 }
390 
391     int tlssegi;                // SegData[] of tls segment
392     int fardataidx;
393 
394     char[1024] pubdata;
395     int pubdatai;
396 
397     char[1024] extdata;
398     int extdatai;
399 
400     // For OmfObj_far16thunk
401     int code16segi;             // SegData[] index
402     targ_size_t CODE16offset;
403 
404     int fltused;
405     int nullext;
406 
407     // The rest don't get re-zeroed for each object file, they get reset
408 
409     Rarray!(Ledatarec*) ledatas;
410     Barray!(Symbol*) resetSymbols;  // reset symbols
411     Rarray!(Linnum) linnum_list;
412     Barray!(char*) linreclist;  // array of line records
413 version (MARS)
414 {
415     Barray!PtrRef ptrrefs;      // buffer for pointer references
416 }
417 }
418 
419 __gshared
420 {
421     Rarray!(seg_data*) SegData;
422     Objstate obj;
423 }
424 
425 
426 /*******************************
427  * Output an object file data record.
428  * Input:
429  *      rectyp  =       record type
430  *      record  .      the data
431  *      reclen  =       # of bytes in record
432  */
433 
434 void objrecord(uint rectyp, const(char)* record, uint reclen)
435 {
436     Outbuffer *o = obj.buf;
437 
438     //printf("rectyp = x%x, record[0] = x%x, reclen = x%x\n",rectyp,record[0],reclen);
439     o.reserve(reclen + 4);
440     o.writeByten(cast(ubyte)rectyp);
441     o.write16n(reclen + 1);  // record length includes checksum
442     o.writen(record,reclen);
443     o.writeByten(0);           // use 0 for checksum
444 }
445 
446 
447 /**************************
448  * Insert an index number.
449  * Input:
450  *      p . where to put the 1 or 2 byte index
451  *      index = the 15 bit index
452  * Returns:
453  *      # of bytes stored
454  */
455 
456 void error(const(char)* filename, uint linnum, uint charnum, const(char)* format, ...);
457 void fatal();
458 
459 void too_many_symbols()
460 {
461 version (SCPP)
462     err_fatal(EM_too_many_symbols, 0x7FFF);
463 else // MARS
464 {
465     error(null, 0, 0, "more than %d symbols in object file", 0x7FFF);
466     fatal();
467 }
468 }
469 
470 version (X86) version (DigitalMars)
471     version = X86ASM;
472 
473 version (X86ASM)
474 {
475 int insidx(char *p,uint index)
476 {
477     asm nothrow
478     {
479         naked                           ;
480         mov     EAX,[ESP+8]             ; // index
481         mov     ECX,[ESP+4]             ; // p
482 
483         cmp     EAX,0x7F                ;
484         jae     L1                      ;
485         mov     [ECX],AL                ;
486         mov     EAX,1                   ;
487         ret                             ;
488 
489 
490     L1:                                 ;
491         cmp     EAX,0x7FFF              ;
492         ja      L2                      ;
493 
494         mov     [ECX+1],AL              ;
495         or      EAX,0x8000              ;
496         mov     [ECX],AH                ;
497         mov     EAX,2                   ;
498         ret                             ;
499     }
500     L2:
501         too_many_symbols();
502 }
503 }
504 else
505 {
506 int insidx(char *p,uint index)
507 {
508     //if (index > 0x7FFF) printf("index = x%x\n",index);
509     /* OFM spec says it could be <=0x7F, but that seems to cause
510      * "library is corrupted" messages. Unverified. See Bugzilla 3601
511      */
512     if (index < 0x7F)
513     {
514         *p = cast(char)index;
515         return 1;
516     }
517     else if (index <= 0x7FFF)
518     {
519         *(p + 1) = cast(char)index;
520         *p = cast(char)((index >> 8) | 0x80);
521         return 2;
522     }
523     else
524     {
525         too_many_symbols();
526         return 0;
527     }
528 }
529 }
530 
531 /**************************
532  * Insert a type index number.
533  * Input:
534  *      p . where to put the 1 or 2 byte index
535  *      index = the 15 bit index
536  * Returns:
537  *      # of bytes stored
538  */
539 
540 int instypidx(char *p,uint index)
541 {
542     if (index <= 127)
543     {   *p = cast(char)index;
544         return 1;
545     }
546     else if (index <= 0x7FFF)
547     {   *(p + 1) = cast(char)index;
548         *p = cast(char)((index >> 8) | 0x80);
549         return 2;
550     }
551     else                        // overflow
552     {   *p = 0;                 // the linker ignores this field anyway
553         return 1;
554     }
555 }
556 
557 /****************************
558  * Read index.
559  */
560 
561 int getindex(ubyte* p)
562 {
563     return ((*p & 0x80)
564     ? ((*p & 0x7F) << 8) | *(p + 1)
565     : *p);
566 }
567 
568 /*****************************
569  * Returns:
570  *      # of bytes stored
571  */
572 
573 enum ONS_OHD = 4;               // max # of extra bytes added by obj_namestring()
574 
575 private int obj_namestring(char *p,const(char)* name)
576 {   uint len;
577 
578     len = cast(uint)strlen(name);
579     if (len > 255)
580     {   p[0] = 0xFF;
581         p[1] = 0;
582         debug assert(len <= 0xFFFF);
583         TOWORD(p + 2,len);
584         memcpy(p + 4,name,len);
585         len += ONS_OHD;
586     }
587     else
588     {   p[0] = cast(char)len;
589         memcpy(p + 1,name,len);
590         len++;
591     }
592     return len;
593 }
594 
595 /******************************
596  * Allocate a new segment.
597  * Return index for the new segment.
598  */
599 
600 seg_data *getsegment()
601 {
602     const int seg = cast(int)SegData.length;
603     seg_data** ppseg = SegData.push();
604 
605     seg_data* pseg = *ppseg;
606     if (!pseg)
607     {
608         pseg = cast(seg_data *)mem_calloc(seg_data.sizeof);
609         //printf("test2: SegData[%d] = %p\n", seg, SegData[seg]);
610         SegData[seg] = pseg;
611     }
612     else
613         memset(pseg, 0, seg_data.sizeof);
614 
615     pseg.SDseg = seg;
616     pseg.segidx = 0;
617     return pseg;
618 }
619 
620 /**************************
621  * Output read only data and generate a symbol for it.
622  *
623  */
624 
625 Symbol * OmfObj_sym_cdata(tym_t ty,char *p,int len)
626 {
627     Symbol *s;
628 
629     alignOffset(CDATA, tysize(ty));
630     s = symboldata(Offset(CDATA), ty);
631     s.Sseg = CDATA;
632     OmfObj_bytes(CDATA, Offset(CDATA), len, p);
633     Offset(CDATA) += len;
634 
635     s.Sfl = FLdata; //FLextern;
636     return s;
637 }
638 
639 /**************************
640  * Ouput read only data for data.
641  * Output:
642  *      *pseg   segment of that data
643  * Returns:
644  *      offset of that data
645  */
646 
647 int OmfObj_data_readonly(char *p, int len, int *pseg)
648 {
649 version (MARS)
650 {
651     targ_size_t oldoff = Offset(CDATA);
652     OmfObj_bytes(CDATA,Offset(CDATA),len,p);
653     Offset(CDATA) += len;
654     *pseg = CDATA;
655 }
656 else
657 {
658     targ_size_t oldoff = Offset(DATA);
659     OmfObj_bytes(DATA,Offset(DATA),len,p);
660     Offset(DATA) += len;
661     *pseg = DATA;
662 }
663     return cast(int)oldoff;
664 }
665 
666 int OmfObj_data_readonly(char *p, int len)
667 {
668     int pseg;
669 
670     return OmfObj_data_readonly(p, len, &pseg);
671 }
672 
673 /*****************************
674  * Get segment for readonly string literals.
675  * The linker will pool strings in this section.
676  * Params:
677  *    sz = number of bytes per character (1, 2, or 4)
678  * Returns:
679  *    segment index
680  */
681 int OmfObj_string_literal_segment(uint sz)
682 {
683     assert(0);
684 }
685 
686 segidx_t OmfObj_seg_debugT()
687 {
688     return DEBTYP;
689 }
690 
691 /******************************
692  * Perform initialization that applies to all .obj output files.
693  * Input:
694  *      filename        source file name
695  *      csegname        code segment name (can be null)
696  */
697 
698 Obj OmfObj_init(Outbuffer *objbuf, const(char)* filename, const(char)* csegname)
699 {
700         //printf("OmfObj_init()\n");
701         Obj mobj = cast(Obj)mem_calloc(__traits(classInstanceSize, Obj));
702 
703         // Zero obj up to ledatas
704         memset(&obj,0,obj.ledatas.offsetof);
705 
706         obj.ledatas.reset();    // recycle the memory used by ledatas
707 
708         foreach (s; obj.resetSymbols)
709             symbol_reset(s);
710         obj.resetSymbols.reset();
711 
712         obj.buf = objbuf;
713         obj.buf.reserve(40_000);
714 
715         obj.lastfardatasegi = -1;
716 
717         obj.mlidata = LIDATA;
718         obj.mpubdef = PUBDEF;
719         obj.mfixupp = FIXUPP;
720         obj.mmodend = MODEND;
721         obj.mlinnum = LINNUM;
722 
723 
724         // Reset for different OBJ file formats
725         if (I32)
726         {   if (config.flags & CFGeasyomf)
727             {   obj.LOCoffset = EASY_LOCoffset;
728                 obj.LOCpointer = EASY_LOCpointer;
729             }
730             else
731             {
732                 obj.mlidata = LID386;
733                 obj.mpubdef = PUB386;
734                 obj.mfixupp = FIX386;
735                 obj.mmodend = MODEND + 1;
736                 obj.LOCoffset = LOC32offset;
737                 obj.LOCpointer = LOC32pointer;
738             }
739             obj.fdsegattr = SEG_ATTR(SEG_ALIGN16,SEG_C_PUBLIC,0,USE32);
740             obj.csegattr  = SEG_ATTR(SEG_ALIGN4, SEG_C_PUBLIC,0,USE32);
741         }
742         else
743         {
744             obj.LOCoffset  = LOC16offset;
745             obj.LOCpointer = LOC16pointer;
746             obj.fdsegattr = SEG_ATTR(SEG_ALIGN16,SEG_C_PUBLIC,0,USE16);
747             obj.csegattr  = SEG_ATTR(SEG_ALIGN2, SEG_C_PUBLIC,0,USE16);
748         }
749 
750         if (config.flags4 & CFG4speed && // if optimized for speed
751             config.target_cpu == TARGET_80486)
752             // 486 is only CPU that really benefits from alignment
753             obj.csegattr  = I32 ? SEG_ATTR(SEG_ALIGN16, SEG_C_PUBLIC,0,USE32)
754                                 : SEG_ATTR(SEG_ALIGN16, SEG_C_PUBLIC,0,USE16);
755 
756         SegData.reset();       // recycle memory
757         getsegment();           // element 0 is reserved
758 
759         getsegment();
760         getsegment();
761         getsegment();
762         getsegment();
763 
764         SegData[CODE].SDseg = CODE;
765         SegData[DATA].SDseg = DATA;
766         SegData[CDATA].SDseg = CDATA;
767         SegData[UDATA].SDseg = UDATA;
768 
769         SegData[CODE].segidx = CODE;
770         SegData[DATA].segidx = DATA;
771         SegData[CDATA].segidx = CDATA;
772         SegData[UDATA].segidx = UDATA;
773 
774         if (config.fulltypes)
775         {
776             getsegment();
777             getsegment();
778 
779             SegData[DEBSYM].SDseg = DEBSYM;
780             SegData[DEBTYP].SDseg = DEBTYP;
781 
782             SegData[DEBSYM].segidx = DEBSYM;
783             SegData[DEBTYP].segidx = DEBTYP;
784         }
785 
786         OmfObj_theadr(filename);
787         obj.modname = filename;
788         if (!csegname || !*csegname)            // if no code seg name supplied
789             obj.csegname = objmodtoseg(obj.modname);    // generate one
790         else
791             obj.csegname = mem_strdup(csegname);        // our own copy
792         objheader(obj.csegname);
793         OmfObj_segment_group(0,0,0,0);             // obj seg and grp info
794         ledata_new(cseg,0);             // so ledata is never null
795         if (config.fulltypes)           // if full typing information
796         {   objmod = mobj;
797             cv_init();                  // initialize debug output code
798         }
799 
800         return mobj;
801 }
802 
803 /**************************
804  * Initialize the start of object output for this particular .obj file.
805  */
806 
807 void OmfObj_initfile(const(char)* filename,const(char)* csegname, const(char)* modname)
808 {
809 }
810 
811 /***************************
812  * Fixup and terminate object file.
813  */
814 
815 void OmfObj_termfile()
816 {
817 }
818 
819 /*********************************
820  * Terminate package.
821  */
822 
823 void OmfObj_term(const(char)* objfilename)
824 {
825         //printf("OmfObj_term()\n");
826         list_t dl;
827         uint size;
828 
829 version (SCPP)
830 {
831         if (!errcnt)
832         {
833             obj_defaultlib();
834             objflush_pointerRefs();
835             outfixlist();               // backpatches
836         }
837 }
838 else
839 {
840         obj_defaultlib();
841         objflush_pointerRefs();
842         outfixlist();               // backpatches
843 }
844         if (config.fulltypes)
845             cv_term();                  // write out final debug info
846         outextdata();                   // finish writing EXTDEFs
847         outpubdata();                   // finish writing PUBDEFs
848 
849         // Put out LEDATA records and associated fixups
850         for (size_t i = 0; i < obj.ledatas.length; i++)
851         {   Ledatarec *d = obj.ledatas[i];
852 
853             if (d.i)                   // if any data in this record
854             {   // Fill in header
855                 int headersize;
856                 int rectyp;
857                 assert(d.lseg > 0 && d.lseg < SegData.length);
858                 int lseg = SegData[d.lseg].segidx;
859                 char[(d.header).sizeof] header = void;
860 
861                 if (seg_is_comdat(lseg))   // if COMDAT
862                 {
863                     header[0] = d.flags | (d.offset ? 1 : 0); // continuation flag
864                     header[1] = d.alloctyp;
865                     header[2] = d._align;
866                     TOOFFSET(header.ptr + 3,d.offset);
867                     headersize = 3 + _tysize[TYint];
868                     headersize += instypidx(header.ptr + headersize,d.typidx);
869                     if ((header[1] & 0x0F) == 0)
870                     {   // Group index
871                         header[headersize] = (d.pubbase == DATA) ? 1 : 0;
872                         headersize++;
873 
874                         // Segment index
875                         headersize += insidx(header.ptr + headersize,d.pubbase);
876                     }
877                     headersize += insidx(header.ptr + headersize,d.pubnamidx);
878 
879                     rectyp = I32 ? COMDAT + 1 : COMDAT;
880                 }
881                 else
882                 {
883                     rectyp = LEDATA;
884                     headersize = insidx(header.ptr,lseg);
885                     if (_tysize[TYint] == LONGSIZE || d.offset & ~0xFFFFL)
886                     {   if (!(config.flags & CFGeasyomf))
887                             rectyp++;
888                         TOLONG(header.ptr + headersize,cast(uint)d.offset);
889                         headersize += 4;
890                     }
891                     else
892                     {
893                         TOWORD(header.ptr + headersize,cast(uint)d.offset);
894                         headersize += 2;
895                     }
896                 }
897                 assert(headersize <= (d.header).sizeof);
898 
899                 // Right-justify data in d.header[]
900                 memcpy(d.header.ptr + (d.header).sizeof - headersize,header.ptr,headersize);
901                 //printf("objrecord(rectyp=x%02x, d=%p, p=%p, size = %d)\n",
902                 //rectyp,d,d.header.ptr + ((d.header).sizeof - headersize),d.i + headersize);
903 
904                 objrecord(rectyp,cast(char*)d.header.ptr + ((d.header).sizeof - headersize),
905                         d.i + headersize);
906                 objfixupp(d.fixuplist);
907             }
908         }
909 
910 static if (TERMCODE)
911 {
912         //list_free(&obj.ledata_list,mem_freefp);
913 }
914 
915         linnum_term();
916         obj_modend();
917 
918         size = cast(uint)obj.buf.length();
919         obj.buf.reset();            // rewind file
920         OmfObj_theadr(obj.modname);
921         objheader(obj.csegname);
922         mem_free(obj.csegname);
923         OmfObj_segment_group(SegData[CODE].SDoffset, SegData[DATA].SDoffset, SegData[CDATA].SDoffset, SegData[UDATA].SDoffset);  // do real sizes
924 
925         // Update any out-of-date far segment sizes
926         for (size_t i = 0; i < SegData.length; i++)
927         {
928             seg_data* f = SegData[i];
929             if (f.isfarseg && f.origsize != f.SDoffset)
930             {   obj.buf.setsize(cast(int)f.seek);
931                 objsegdef(f.attr,f.SDoffset,f.lnameidx,f.classidx);
932             }
933         }
934         //mem_free(obj.farseg);
935 
936         //printf("Ledata max = %d\n", obj.ledatas.length);
937         //printf("Max # of fixups = %d\n",obj.fixup_count);
938 
939         obj.buf.setsize(size);
940 }
941 
942 /*****************************
943  * Line number support.
944  */
945 
946 /***************************
947  * Record line number linnum at offset.
948  * Params:
949  *      srcpos = source file position
950  *      seg = segment it corresponds to (negative for COMDAT segments)
951  *      offset = offset within seg
952  *      pubnamidx = public name index
953  *      obj.mlinnum = LINNUM or LINSYM
954  */
955 
956 void OmfObj_linnum(Srcpos srcpos,int seg,targ_size_t offset)
957 {
958 version (MARS)
959     varStats_recordLineOffset(srcpos, offset);
960 
961     uint linnum = srcpos.Slinnum;
962 
963 static if (0)
964 {
965     printf("OmfObj_linnum(seg=%d, offset=0x%x) ", seg, cast(int)offset);
966     srcpos.print("");
967 }
968 
969     char linos2 = config.exe == EX_OS2 && !seg_is_comdat(SegData[seg].segidx);
970 
971 version (MARS)
972 {
973     bool cond = (!obj.term &&
974         (seg_is_comdat(SegData[seg].segidx) || (srcpos.Sfilename && srcpos.Sfilename != obj.modname)));
975 }
976 else
977 {
978     if (!srcpos.Sfilptr)
979         return;
980     sfile_debug(*srcpos.Sfilptr);
981     bool cond = !obj.term &&
982                 (!(srcpos_sfile(srcpos).SFflags & SFtop) || (seg_is_comdat(SegData[seg].segidx) && !obj.term));
983 }
984     if (cond)
985     {
986         // Not original source file, or a COMDAT.
987         // Save data away and deal with it at close of compile.
988         // It is done this way because presumably 99% of the lines
989         // will be in the original source file, so we wish to minimize
990         // memory consumption and maximize speed.
991 
992         if (linos2)
993             return;             // BUG: not supported under OS/2
994 
995         Linnum* ln;
996         foreach (ref rln; obj.linnum_list)
997         {
998             version (MARS)
999                 bool cond2 = rln.filename == srcpos.Sfilename;
1000             else version (SCPP)
1001                 bool cond2 = rln.filptr == *srcpos.Sfilptr;
1002 
1003             if (cond2 &&
1004                 rln.cseg == seg)
1005             {
1006                 ln = &rln;      // found existing entry with room
1007                 goto L1;
1008             }
1009         }
1010         // Create new entry
1011         ln = obj.linnum_list.push();
1012         version (MARS)
1013             ln.filename = srcpos.Sfilename;
1014         else
1015             ln.filptr = *srcpos.Sfilptr;
1016 
1017         ln.cseg = seg;
1018         ln.seg = obj.pubnamidx;
1019         ln.reset();
1020 
1021     L1:
1022         //printf("offset = x%x, line = %d\n", (int)offset, linnum);
1023         ln.data.write16(linnum);
1024         if (_tysize[TYint] == 2)
1025             ln.data.write16(cast(int)offset);
1026         else
1027             ln.data.write32(cast(int)offset);
1028     }
1029     else
1030     {
1031         if (linos2 && obj.linreci > LINRECMAX - 8)
1032             obj.linrec = null;                  // allocate a new one
1033         else if (seg != obj.recseg)
1034             linnum_flush();
1035 
1036         if (!obj.linrec)                        // if not allocated
1037         {
1038             obj.linrec = cast(char* ) mem_calloc(LINRECMAX);
1039             obj.linrec[0] = 0;              // base group / flags
1040             obj.linrecheader = 1 + insidx(obj.linrec + 1,seg_is_comdat(SegData[seg].segidx) ? obj.pubnamidx : SegData[seg].segidx);
1041             obj.linreci = obj.linrecheader;
1042             obj.recseg = seg;
1043 static if (MULTISCOPE)
1044 {
1045             if (!obj.linvec)
1046             {
1047                 obj.linvec = vec_calloc(1000);
1048                 obj.offvec = vec_calloc(1000);
1049             }
1050 }
1051             if (linos2)
1052             {
1053                 if (obj.linreclist.length == 0)  // if first line number record
1054                     obj.linreci += 8;       // leave room for header
1055                 obj.linreclist.push(obj.linrec);
1056             }
1057 
1058             // Select record type to use
1059             obj.mlinnum = seg_is_comdat(SegData[seg].segidx) ? LINSYM : LINNUM;
1060             if (I32 && !(config.flags & CFGeasyomf))
1061                 obj.mlinnum++;
1062         }
1063         else if (obj.linreci > LINRECMAX - (2 + _tysize[TYint]))
1064         {
1065             objrecord(obj.mlinnum,obj.linrec,obj.linreci);  // output data
1066             obj.linreci = obj.linrecheader;
1067             if (seg_is_comdat(SegData[seg].segidx))        // if LINSYM record
1068                 obj.linrec[0] |= 1;         // continuation bit
1069         }
1070 static if (MULTISCOPE)
1071 {
1072         if (linnum >= vec_numbits(obj.linvec))
1073             obj.linvec = vec_realloc(obj.linvec,linnum + 1000);
1074         if (offset >= vec_numbits(obj.offvec))
1075         {
1076             if (offset < 0xFF00)        // otherwise we overflow ph_malloc()
1077                 obj.offvec = vec_realloc(obj.offvec,cast(uint)offset * 2);
1078         }
1079         bool cond3 =
1080             // disallow multiple offsets per line
1081             !vec_testbit(linnum,obj.linvec) &&  // if linnum not already used
1082 
1083             // disallow multiple lines per offset
1084             (offset >= 0xFF00 || !vec_testbit(cast(uint)offset,obj.offvec));      // and offset not already used
1085 }
1086 else
1087         enum cond3 = true;
1088 
1089         if (cond3)
1090         {
1091 static if (MULTISCOPE)
1092 {
1093             vec_setbit(linnum,obj.linvec);              // mark linnum as used
1094             if (offset < 0xFF00)
1095                 vec_setbit(cast(uint)offset,obj.offvec);  // mark offset as used
1096 }
1097             TOWORD(obj.linrec + obj.linreci,linnum);
1098             if (linos2)
1099             {
1100                 obj.linrec[obj.linreci + 2] = 1;        // source file index
1101                 TOLONG(obj.linrec + obj.linreci + 4,cast(uint)offset);
1102                 obj.linrecnum++;
1103                 obj.linreci += 8;
1104             }
1105             else
1106             {
1107                 TOOFFSET(obj.linrec + obj.linreci + 2,offset);
1108                 obj.linreci += 2 + _tysize[TYint];
1109             }
1110         }
1111     }
1112 }
1113 
1114 /***************************
1115  * Flush any pending line number records.
1116  */
1117 
1118 private void linnum_flush()
1119 {
1120     if (obj.linreclist.length)
1121     {
1122         obj.linrec = obj.linreclist[0];
1123         TOWORD(obj.linrec + 6,obj.linrecnum);
1124 
1125         foreach (i; 0 .. obj.linreclist.length - 1)
1126         {
1127             obj.linrec = obj.linreclist[i];
1128             objrecord(obj.mlinnum, obj.linrec, LINRECMAX);
1129             mem_free(obj.linrec);
1130         }
1131         obj.linrec = obj.linreclist[obj.linreclist.length - 1];
1132         objrecord(obj.mlinnum,obj.linrec,obj.linreci);
1133         obj.linreclist.reset();
1134 
1135         // Put out File Names Table
1136         TOLONG(obj.linrec + 2,0);               // record no. of start of source (???)
1137         TOLONG(obj.linrec + 6,obj.linrecnum);   // number of primary source records
1138         TOLONG(obj.linrec + 10,1);              // number of source and listing files
1139         const len = obj_namestring(obj.linrec + 14,obj.modname);
1140         assert(14 + len <= LINRECMAX);
1141         objrecord(obj.mlinnum,obj.linrec,cast(uint)(14 + len));
1142 
1143         mem_free(obj.linrec);
1144         obj.linrec = null;
1145     }
1146     else if (obj.linrec)                        // if some line numbers to send
1147     {
1148         objrecord(obj.mlinnum,obj.linrec,obj.linreci);
1149         mem_free(obj.linrec);
1150         obj.linrec = null;
1151     }
1152 static if (MULTISCOPE)
1153 {
1154     vec_clear(obj.linvec);
1155     vec_clear(obj.offvec);
1156 }
1157 }
1158 
1159 /*************************************
1160  * Terminate line numbers.
1161  */
1162 
1163 private void linnum_term()
1164 {
1165 version (SCPP)
1166     Sfile *lastfilptr = null;
1167 
1168 version (MARS)
1169     const(char)* lastfilename = null;
1170 
1171     const csegsave = cseg;
1172 
1173     linnum_flush();
1174     obj.term = 1;
1175 
1176     foreach (ref ln; obj.linnum_list)
1177     {
1178         version (SCPP)
1179         {
1180             Sfile *filptr = ln.filptr;
1181             if (filptr != lastfilptr)
1182             {
1183                 if (lastfilptr == null && strcmp(filptr.SFname,obj.modname))
1184                     OmfObj_theadr(filptr.SFname);
1185                 lastfilptr = filptr;
1186             }
1187         }
1188         version (MARS)
1189         {
1190             const(char)* filename = ln.filename;
1191             if (filename != lastfilename)
1192             {
1193                 if (filename)
1194                     objmod.theadr(filename);
1195                 lastfilename = filename;
1196             }
1197         }
1198         cseg = ln.cseg;
1199         assert(cseg > 0);
1200         obj.pubnamidx = ln.seg;
1201 
1202         Srcpos srcpos;
1203         version (MARS)
1204             srcpos.Sfilename = ln.filename;
1205         else
1206             srcpos.Sfilptr = &ln.filptr;
1207 
1208         const slice = ln.data[];
1209         const pend = slice.ptr + slice.length;
1210         for (const(ubyte)* p = slice.ptr; p < pend; )
1211         {
1212             srcpos.Slinnum = *cast(ushort *)p;
1213             p += 2;
1214             targ_size_t offset;
1215             if (I32)
1216             {
1217                 offset = *cast(uint *)p;
1218                 p += 4;
1219             }
1220             else
1221             {
1222                 offset = *cast(ushort *)p;
1223                 p += 2;
1224             }
1225             OmfObj_linnum(srcpos,cseg,offset);
1226         }
1227         linnum_flush();
1228     }
1229 
1230     obj.linnum_list.reset();
1231     cseg = csegsave;
1232     assert(cseg > 0);
1233 static if (MULTISCOPE)
1234 {
1235     vec_free(obj.linvec);
1236     vec_free(obj.offvec);
1237 }
1238 }
1239 
1240 /*******************************
1241  * Set start address
1242  */
1243 
1244 void OmfObj_startaddress(Symbol *s)
1245 {
1246     obj.startaddress = s;
1247 }
1248 
1249 /*******************************
1250  * Output DOSSEG coment record.
1251  */
1252 
1253 void OmfObj_dosseg()
1254 {
1255     static immutable char[2] dosseg = [ 0x80,0x9E ];
1256 
1257     objrecord(COMENT, dosseg.ptr, dosseg.sizeof);
1258 }
1259 
1260 /*******************************
1261  * Embed comment record.
1262  */
1263 
1264 private void obj_comment(ubyte x, const(char)* string, size_t len)
1265 {
1266     char[128] buf = void;
1267 
1268     char *library = (2 + len <= buf.sizeof) ? buf.ptr : cast(char *) malloc(2 + len);
1269     assert(library);
1270     library[0] = 0;
1271     library[1] = x;
1272     memcpy(library + 2,string,len);
1273     objrecord(COMENT,library,cast(uint)(len + 2));
1274     if (library != buf.ptr)
1275         free(library);
1276 }
1277 
1278 /*******************************
1279  * Output library name.
1280  * Output:
1281  *      name is modified
1282  * Returns:
1283  *      true if operation is supported
1284  */
1285 
1286 bool OmfObj_includelib(const(char)* name)
1287 {
1288     const(char)* p;
1289     size_t len = strlen(name);
1290 
1291     p = filespecdotext(name);
1292     if (!filespeccmp(p,".lib"))
1293         len -= strlen(p);               // lop off .LIB extension
1294     obj_comment(0x9F, name, len);
1295     return true;
1296 }
1297 
1298 /*******************************
1299 * Output linker directive.
1300 * Output:
1301 *      directive is modified
1302 * Returns:
1303 *      true if operation is supported
1304 */
1305 
1306 bool OmfObj_linkerdirective(const(char)* name)
1307 {
1308     return false;
1309 }
1310 
1311 /**********************************
1312  * Do we allow zero sized objects?
1313  */
1314 
1315 bool OmfObj_allowZeroSize()
1316 {
1317     return false;
1318 }
1319 
1320 /**************************
1321  * Embed string in executable.
1322  */
1323 
1324 void OmfObj_exestr(const(char)* p)
1325 {
1326     obj_comment(0xA4,p, strlen(p));
1327 }
1328 
1329 /**************************
1330  * Embed string in obj.
1331  */
1332 
1333 void OmfObj_user(const(char)* p)
1334 {
1335     obj_comment(0xDF,p, strlen(p));
1336 }
1337 
1338 /*********************************
1339  * Put out default library name.
1340  */
1341 
1342 private void obj_defaultlib()
1343 {
1344     char[4] library;            // default library
1345     static immutable char[5+1] model = "SMCLV";
1346 
1347 version (MARS)
1348     memcpy(library.ptr,"SM?".ptr,4);
1349 else
1350     memcpy(library.ptr,"SD?".ptr,4);
1351 
1352     switch (config.exe)
1353     {
1354         case EX_OS2:
1355             library[2] = 'F';
1356             goto case;
1357 
1358         case EX_OS1:
1359             library[1] = 'O';
1360             break;
1361         case EX_WIN32:
1362 version (MARS)
1363             library[1] = 'M';
1364 else
1365             library[1] = 'N';
1366 
1367             library[2] = (config.flags4 & CFG4dllrtl) ? 'D' : 'N';
1368             break;
1369         case EX_DOSX:
1370         case EX_PHARLAP:
1371             library[2] = 'X';
1372             break;
1373         default:
1374             library[2] = model[config.memmodel];
1375             if (config.wflags & WFwindows)
1376                 library[1] = 'W';
1377             break;
1378     }
1379 
1380     if (!(config.flags2 & CFG2nodeflib))
1381     {
1382         objmod.includelib(configv.deflibname ? configv.deflibname : library.ptr);
1383     }
1384 }
1385 
1386 /*******************************
1387  * Output a weak extern record.
1388  * s1 is the weak extern, s2 is its default resolution.
1389  */
1390 
1391 void OmfObj_wkext(Symbol *s1,Symbol *s2)
1392 {
1393     //printf("OmfObj_wkext(%s)\n", s1.Sident.ptr);
1394     if (I32)
1395     {
1396         // Optlink crashes with weak symbols at EIP 41AFE7, 402000
1397         return;
1398     }
1399 
1400     int x2;
1401     if (s2)
1402         x2 = s2.Sxtrnnum;
1403     else
1404     {
1405         if (!obj.nullext)
1406         {
1407             obj.nullext = OmfObj_external_def("__nullext");
1408         }
1409         x2 = obj.nullext;
1410     }
1411     outextdata();
1412 
1413     char[2+2+2] buffer = void;
1414     buffer[0] = 0x80;
1415     buffer[1] = 0xA8;
1416     int i = 2;
1417     i += insidx(&buffer[2],s1.Sxtrnnum);
1418     i += insidx(&buffer[i],x2);
1419     objrecord(COMENT,buffer.ptr,i);
1420 }
1421 
1422 /*******************************
1423  * Output a lazy extern record.
1424  * s1 is the lazy extern, s2 is its default resolution.
1425  */
1426 
1427 void OmfObj_lzext(Symbol *s1,Symbol *s2)
1428 {
1429     char[2+2+2] buffer = void;
1430     int i;
1431 
1432     outextdata();
1433     buffer[0] = 0x80;
1434     buffer[1] = 0xA9;
1435     i = 2;
1436     i += insidx(&buffer[2],s1.Sxtrnnum);
1437     i += insidx(&buffer[i],s2.Sxtrnnum);
1438     objrecord(COMENT,buffer.ptr,i);
1439 }
1440 
1441 /*******************************
1442  * Output an alias definition record.
1443  */
1444 
1445 void OmfObj_alias(const(char)* n1,const(char)* n2)
1446 {
1447     uint len;
1448     char* buffer;
1449 
1450     buffer = cast(char *) alloca(strlen(n1) + strlen(n2) + 2 * ONS_OHD);
1451     len = obj_namestring(buffer,n1);
1452     len += obj_namestring(buffer + len,n2);
1453     objrecord(ALIAS,buffer,len);
1454 }
1455 
1456 /*******************************
1457  * Output module name record.
1458  */
1459 
1460 void OmfObj_theadr(const(char)* modname)
1461 {
1462     //printf("OmfObj_theadr(%s)\n", modname);
1463 
1464     // Convert to absolute file name, so debugger can find it anywhere
1465     char[260] absname = void;
1466     if (config.fulltypes &&
1467         modname[0] != '\\' && modname[0] != '/' && !(modname[0] && modname[1] == ':'))
1468     {
1469         if (getcwd(absname.ptr, absname.sizeof))
1470         {
1471             int len = cast(int)strlen(absname.ptr);
1472             if(absname[len - 1] != '\\' && absname[len - 1] != '/')
1473                 absname[len++] = '\\';
1474             strcpy(absname.ptr + len, modname);
1475             modname = absname.ptr;
1476         }
1477     }
1478 
1479     char *theadr = cast(char *)alloca(ONS_OHD + strlen(modname));
1480     int i = obj_namestring(theadr,modname);
1481     objrecord(THEADR,theadr,i);                 // module name record
1482 }
1483 
1484 /*******************************
1485  * Embed compiler version in .obj file.
1486  */
1487 
1488 void OmfObj_compiler()
1489 {
1490     const(char)* compiler = "\0\xDB" ~ "Digital Mars C/C++"
1491         ~ VERSION
1492         ;       // compiled by ...
1493 
1494     objrecord(COMENT,compiler,cast(uint)strlen(compiler));
1495 }
1496 
1497 /*******************************
1498  * Output header stuff for object files.
1499  * Input:
1500  *      csegname        Name to use for code segment (null if use default)
1501  */
1502 
1503 enum CODECLASS  = 4;    // code class lname index
1504 enum DATACLASS  = 6;    // data class lname index
1505 enum CDATACLASS = 7;    // CONST class lname index
1506 enum BSSCLASS   = 9;    // BSS class lname index
1507 
1508 private void objheader(char *csegname)
1509 {
1510   char *nam;
1511     __gshared char[78] lnames =
1512         "\0\06DGROUP\05_TEXT\04CODE\05_DATA\04DATA\05CONST\04_BSS\03BSS" ~
1513         "\07$$TYPES\06DEBTYP\011$$SYMBOLS\06DEBSYM";
1514     assert(lnames[lnames.length - 2] == 'M');
1515 
1516     // Include debug segment names if inserting type information
1517     int lnamesize = config.fulltypes ? lnames.sizeof - 1 : lnames.sizeof - 1 - 32;
1518     int texti = 8;                                // index of _TEXT
1519 
1520     __gshared char[5] comment = [0,0x9D,'0','?','O']; // memory model
1521     __gshared char[5+1] model = "smclv";
1522     __gshared char[5] exten = [0,0xA1,1,'C','V'];     // extended format
1523     __gshared char[7] pmdeb = [0x80,0xA1,1,'H','L','L',0];    // IBM PM debug format
1524 
1525     if (I32)
1526     {
1527         if (config.flags & CFGeasyomf)
1528         {
1529             // Indicate we're in EASY OMF (hah!) format
1530             static immutable char[7] easy_omf = [ 0x80,0xAA,'8','0','3','8','6' ];
1531             objrecord(COMENT,easy_omf.ptr,easy_omf.sizeof);
1532         }
1533     }
1534 
1535     // Send out a comment record showing what memory model was used
1536     comment[2] = cast(char)(config.target_cpu + '0');
1537     comment[3] = model[config.memmodel];
1538     if (I32)
1539     {
1540         if (config.exe == EX_WIN32)
1541             comment[3] = 'n';
1542         else if (config.exe == EX_OS2)
1543             comment[3] = 'f';
1544         else
1545             comment[3] = 'x';
1546     }
1547     objrecord(COMENT,comment.ptr,comment.sizeof);
1548 
1549     // Send out comment indicating we're using extensions to .OBJ format
1550     if (config.exe == EX_OS2)
1551         objrecord(COMENT, pmdeb.ptr, pmdeb.sizeof);
1552     else
1553         objrecord(COMENT, exten.ptr, exten.sizeof);
1554 
1555     // Change DGROUP to FLAT if we are doing flat memory model
1556     // (Watch out, objheader() is called twice!)
1557     if (config.exe & EX_flat)
1558     {
1559         if (lnames[2] != 'F')                   // do not do this twice
1560         {
1561             memcpy(lnames.ptr + 1, "\04FLAT".ptr, 5);
1562             memmove(lnames.ptr + 6, lnames.ptr + 8, lnames.sizeof - 8);
1563         }
1564         lnamesize -= 2;
1565         texti -= 2;
1566     }
1567 
1568     // Put out segment and group names
1569     if (csegname)
1570     {
1571         // Replace the module name _TEXT with the new code segment name
1572         const size_t i = strlen(csegname);
1573         char *p = cast(char *)alloca(lnamesize + i - 5);
1574         memcpy(p,lnames.ptr,8);
1575         p[texti] = cast(char)i;
1576         texti++;
1577         memcpy(p + texti,csegname,i);
1578         memcpy(p + texti + i,lnames.ptr + texti + 5,lnamesize - (texti + 5));
1579         objrecord(LNAMES,p,cast(uint)(lnamesize + i - 5));
1580     }
1581     else
1582         objrecord(LNAMES,lnames.ptr,lnamesize);
1583 }
1584 
1585 /********************************
1586  * Convert module name to code segment name.
1587  * Output:
1588  *      mem_malloc'd code seg name
1589  */
1590 
1591 private char*  objmodtoseg(const(char)* modname)
1592 {
1593     char* csegname = null;
1594 
1595     if (LARGECODE)              // if need to add in module name
1596     {
1597         int i;
1598         char* m;
1599         static immutable char[6] suffix = "_TEXT";
1600 
1601         // Prepend the module name to the beginning of the _TEXT
1602         m = filespecgetroot(filespecname(modname));
1603         strupr(m);
1604         i = cast(int)strlen(m);
1605         csegname = cast(char *)mem_malloc(i + suffix.sizeof);
1606         strcpy(csegname,m);
1607         strcat(csegname,suffix.ptr);
1608         mem_free(m);
1609     }
1610     return csegname;
1611 }
1612 
1613 /*********************************
1614  * Put out a segment definition.
1615  */
1616 
1617 private void objsegdef(int attr,targ_size_t size,int segnamidx,int classnamidx)
1618 {
1619     uint reclen;
1620     char[1+4+2+2+2+1] sd = void;
1621 
1622     //printf("objsegdef(attr=x%x, size=x%x, segnamidx=x%x, classnamidx=x%x)\n",
1623       //attr,size,segnamidx,classnamidx);
1624     sd[0] = cast(char)attr;
1625     if (attr & 1 || config.flags & CFGeasyomf)
1626     {
1627         TOLONG(sd.ptr + 1, cast(uint)size);          // store segment size
1628         reclen = 5;
1629     }
1630     else
1631     {
1632         debug
1633         assert(size <= 0xFFFF);
1634 
1635         TOWORD(sd.ptr + 1,cast(uint)size);
1636         reclen = 3;
1637     }
1638     reclen += insidx(sd.ptr + reclen,segnamidx);    // segment name index
1639     reclen += insidx(sd.ptr + reclen,classnamidx);  // class name index
1640     sd[reclen] = 1;                             // overlay name index
1641     reclen++;
1642     if (attr & 1)                       // if USE32
1643     {
1644         if (config.flags & CFGeasyomf)
1645         {
1646             // Translate to Pharlap format
1647             sd[0] &= ~1;                // turn off P bit
1648 
1649             // Translate A: 4.6
1650             attr &= SEG_ATTR(7,0,0,0);
1651             if (attr == SEG_ATTR(4,0,0,0))
1652                 sd[0] ^= SEG_ATTR(4 ^ 6,0,0,0);
1653 
1654             // 2 is execute/read
1655             // 3 is read/write
1656             // 4 is use32
1657             sd[reclen] = (classnamidx == 4) ? (4+2) : (4+3);
1658             reclen++;
1659         }
1660     }
1661     else                                // 16 bit segment
1662     {
1663 version (MARS)
1664         assert(0);
1665 else
1666 {
1667         if (size & ~0xFFFFL)
1668         {
1669             if (size == 0x10000)        // if exactly 64Kb
1670                 sd[0] |= 2;             // set "B" bit
1671             else
1672                 synerr(EM_seg_gt_64k,size);     // segment exceeds 64Kb
1673         }
1674 //printf("attr = %x\n", attr);
1675 }
1676     }
1677     debug
1678     assert(reclen <= sd.sizeof);
1679 
1680     objrecord(SEGDEF + (sd[0] & 1),sd.ptr,reclen);
1681 }
1682 
1683 /*********************************
1684  * Output segment and group definitions.
1685  * Input:
1686  *      codesize        size of code segment
1687  *      datasize        size of initialized data segment
1688  *      cdatasize       size of initialized const data segment
1689  *      udatasize       size of uninitialized data segment
1690  */
1691 
1692 void OmfObj_segment_group(targ_size_t codesize,targ_size_t datasize,
1693                 targ_size_t cdatasize,targ_size_t udatasize)
1694 {
1695     int dsegattr;
1696     int dsymattr;
1697 
1698     // Group into DGROUP the segments CONST, _BSS and _DATA
1699     // For FLAT model, it's just GROUP FLAT
1700     static immutable char[7] grpdef = [2,0xFF,2,0xFF,3,0xFF,4];
1701 
1702     objsegdef(obj.csegattr,codesize,3,CODECLASS);  // seg _TEXT, class CODE
1703 
1704 version (MARS)
1705 {
1706     dsegattr = SEG_ATTR(SEG_ALIGN16,SEG_C_PUBLIC,0,USE32);
1707     objsegdef(dsegattr,datasize,5,DATACLASS);   // [DATA]  seg _DATA, class DATA
1708     objsegdef(dsegattr,cdatasize,7,CDATACLASS); // [CDATA] seg CONST, class CONST
1709     objsegdef(dsegattr,udatasize,8,BSSCLASS);   // [UDATA] seg _BSS,  class BSS
1710 }
1711 else
1712 {
1713     dsegattr = I32
1714           ? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32)
1715           : SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);
1716     objsegdef(dsegattr,datasize,5,DATACLASS);   // seg _DATA, class DATA
1717     objsegdef(dsegattr,cdatasize,7,CDATACLASS); // seg CONST, class CONST
1718     objsegdef(dsegattr,udatasize,8,BSSCLASS);   // seg _BSS, class BSS
1719 }
1720 
1721     obj.lnameidx = 10;                          // next lname index
1722     obj.segidx = 5;                             // next segment index
1723 
1724     if (config.fulltypes)
1725     {
1726         dsymattr = I32
1727               ? SEG_ATTR(SEG_ALIGN1,SEG_C_ABS,0,USE32)
1728               : SEG_ATTR(SEG_ALIGN1,SEG_C_ABS,0,USE16);
1729 
1730         if (config.exe & EX_flat)
1731         {
1732             // IBM's version of CV uses dword aligned segments
1733             dsymattr = SEG_ATTR(SEG_ALIGN4,SEG_C_ABS,0,USE32);
1734         }
1735         else if (config.fulltypes == CV4)
1736         {
1737             // Always use 32 bit segments
1738             dsymattr |= USE32;
1739             assert(!(config.flags & CFGeasyomf));
1740         }
1741         objsegdef(dsymattr,SegData[DEBSYM].SDoffset,0x0C,0x0D);
1742         objsegdef(dsymattr,SegData[DEBTYP].SDoffset,0x0A,0x0B);
1743         obj.lnameidx += 4;                      // next lname index
1744         obj.segidx += 2;                        // next segment index
1745     }
1746 
1747     objrecord(GRPDEF,grpdef.ptr,(config.exe & EX_flat) ? 1 : grpdef.sizeof);
1748 static if (0)
1749 {
1750     // Define fixup threads, we don't use them
1751     {
1752         static immutable char[12] thread = [ 0,3,1,2,2,1,3,4,0x40,1,0x45,1 ];
1753         objrecord(obj.mfixupp,thread.ptr,thread.sizeof);
1754     }
1755     // This comment appears to indicate that no more PUBDEFs, EXTDEFs,
1756     // or COMDEFs are coming.
1757     {
1758         static immutable char[3] cv = [0,0xA2,1];
1759         objrecord(COMENT,cv.ptr,cv.sizeof);
1760     }
1761 }
1762 }
1763 
1764 
1765 /**************************************
1766  * Symbol is the function that calls the static constructors.
1767  * Put a pointer to it into a special segment that the startup code
1768  * looks at.
1769  * Input:
1770  *      s       static constructor function
1771  *      dtor    number of static destructors
1772  *      seg     1:      user
1773  *              2:      lib
1774  *              3:      compiler
1775  */
1776 
1777 void OmfObj_staticctor(Symbol *s,int dtor,int seg)
1778 {
1779     // We need to always put out the segments in triples, so that the
1780     // linker will put them in the correct order.
1781     static immutable char[28] lnamector = "\05XIFCB\04XIFU\04XIFL\04XIFM\05XIFCE";
1782     static immutable char[15] lnamedtor = "\04XOFB\03XOF\04XOFE";
1783     static immutable char[12] lnamedtorf = "\03XOB\02XO\03XOE";
1784 
1785     symbol_debug(s);
1786 
1787     // Determine if near or far function
1788     assert(I32 || tyfarfunc(s.ty()));
1789 
1790     // Put out LNAMES record
1791     objrecord(LNAMES,lnamector.ptr,lnamector.sizeof - 1);
1792 
1793     int dsegattr = I32
1794         ? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32)
1795         : SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);
1796 
1797     for (int i = 0; i < 5; i++)
1798     {
1799         int sz;
1800 
1801         sz = (i == seg) ? 4 : 0;
1802 
1803         // Put out segment definition record
1804         objsegdef(dsegattr,sz,obj.lnameidx,DATACLASS);
1805 
1806         if (i == seg)
1807         {
1808             seg_data *pseg = getsegment();
1809             pseg.segidx = obj.segidx;
1810             OmfObj_reftoident(pseg.SDseg,0,s,0,0);     // put out function pointer
1811         }
1812 
1813         obj.segidx++;
1814         obj.lnameidx++;
1815     }
1816 
1817     if (dtor)
1818     {
1819         // Leave space in XOF segment so that __fatexit() can insert a
1820         // pointer to the static destructor in XOF.
1821 
1822         // Put out LNAMES record
1823         if (LARGEDATA)
1824             objrecord(LNAMES,lnamedtorf.ptr,lnamedtorf.sizeof - 1);
1825         else
1826             objrecord(LNAMES,lnamedtor.ptr,lnamedtor.sizeof - 1);
1827 
1828         // Put out beginning segment
1829         objsegdef(dsegattr,0,obj.lnameidx,BSSCLASS);
1830 
1831         // Put out segment definition record
1832         objsegdef(dsegattr,4 * dtor,obj.lnameidx + 1,BSSCLASS);
1833 
1834         // Put out ending segment
1835         objsegdef(dsegattr,0,obj.lnameidx + 2,BSSCLASS);
1836 
1837         obj.lnameidx += 3;                      // for next time
1838         obj.segidx += 3;
1839     }
1840 }
1841 
1842 void OmfObj_staticdtor(Symbol *s)
1843 {
1844     assert(0);
1845 }
1846 
1847 
1848 /***************************************
1849  * Set up function to be called as static constructor on program
1850  * startup or static destructor on program shutdown.
1851  * Params:
1852  *      s = function symbol
1853  *      isCtor = true if constructor, false if destructor
1854  */
1855 
1856 void OmfObj_setModuleCtorDtor(Symbol *s, bool isCtor)
1857 {
1858     // We need to always put out the segments in triples, so that the
1859     // linker will put them in the correct order.
1860     static immutable char[5+4+5+1][4] lnames =
1861     [   "\03XIB\02XI\03XIE",            // near constructor
1862         "\03XCB\02XC\03XCE",            // near destructor
1863         "\04XIFB\03XIF\04XIFE",         // far constructor
1864         "\04XCFB\03XCF\04XCFE",         // far destructor
1865     ];
1866     // Size of each of the above strings
1867     static immutable int[4] lnamesize = [ 4+3+4,4+3+4,5+4+5,5+4+5 ];
1868 
1869     int dsegattr;
1870 
1871     symbol_debug(s);
1872 
1873 version (SCPP)
1874     debug assert(memcmp(s.Sident.ptr,"_ST".ptr,3) == 0);
1875 
1876     // Determine if constructor or destructor
1877     // _STI... is a constructor, _STD... is a destructor
1878     int i = !isCtor;
1879     // Determine if near or far function
1880     if (tyfarfunc(s.Stype.Tty))
1881         i += 2;
1882 
1883     // Put out LNAMES record
1884     objrecord(LNAMES,lnames[i].ptr,lnamesize[i]);
1885 
1886     dsegattr = I32
1887         ? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32)
1888         : SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);
1889 
1890     // Put out beginning segment
1891     objsegdef(dsegattr,0,obj.lnameidx,DATACLASS);
1892     obj.segidx++;
1893 
1894     // Put out segment definition record
1895     // size is NPTRSIZE or FPTRSIZE
1896     objsegdef(dsegattr,(i & 2) + tysize(TYnptr),obj.lnameidx + 1,DATACLASS);
1897     seg_data *pseg = getsegment();
1898     pseg.segidx = obj.segidx;
1899     OmfObj_reftoident(pseg.SDseg,0,s,0,0);     // put out function pointer
1900     obj.segidx++;
1901 
1902     // Put out ending segment
1903     objsegdef(dsegattr,0,obj.lnameidx + 2,DATACLASS);
1904     obj.segidx++;
1905 
1906     obj.lnameidx += 3;                  // for next time
1907 }
1908 
1909 
1910 /***************************************
1911  * Stuff pointer to function in its own segment.
1912  * Used for static ctor and dtor lists.
1913  */
1914 
1915 void OmfObj_ehtables(Symbol *sfunc,uint size,Symbol *ehsym)
1916 {
1917     // We need to always put out the segments in triples, so that the
1918     // linker will put them in the correct order.
1919     static immutable char[12] lnames =
1920        "\03FIB\02FI\03FIE";             // near constructor
1921     int i;
1922     int dsegattr;
1923     targ_size_t offset;
1924 
1925     symbol_debug(sfunc);
1926 
1927     if (obj.fisegi == 0)
1928     {
1929         // Put out LNAMES record
1930         objrecord(LNAMES,lnames.ptr,lnames.sizeof - 1);
1931 
1932         dsegattr = I32
1933             ? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32)
1934             : SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);
1935 
1936         // Put out beginning segment
1937         objsegdef(dsegattr,0,obj.lnameidx,DATACLASS);
1938         obj.lnameidx++;
1939         obj.segidx++;
1940 
1941         // Put out segment definition record
1942         obj.fisegi = obj_newfarseg(0,DATACLASS);
1943         objsegdef(dsegattr,0,obj.lnameidx,DATACLASS);
1944         SegData[obj.fisegi].attr = dsegattr;
1945         assert(SegData[obj.fisegi].segidx == obj.segidx);
1946 
1947         // Put out ending segment
1948         objsegdef(dsegattr,0,obj.lnameidx + 1,DATACLASS);
1949 
1950         obj.lnameidx += 2;              // for next time
1951         obj.segidx += 2;
1952     }
1953     offset = SegData[obj.fisegi].SDoffset;
1954     offset += OmfObj_reftoident(obj.fisegi,offset,sfunc,0,LARGECODE ? CFoff | CFseg : CFoff);   // put out function pointer
1955     offset += OmfObj_reftoident(obj.fisegi,offset,ehsym,0,0);   // pointer to data
1956     OmfObj_bytes(obj.fisegi,offset,_tysize[TYint],&size);          // size of function
1957     SegData[obj.fisegi].SDoffset = offset + _tysize[TYint];
1958 }
1959 
1960 void OmfObj_ehsections()
1961 {
1962     assert(0);
1963 }
1964 
1965 /***************************************
1966  * Append pointer to ModuleInfo to "FM" segment.
1967  * The FM segment is bracketed by the empty FMB and FME segments.
1968  */
1969 
1970 version (MARS)
1971 {
1972 
1973 void OmfObj_moduleinfo(Symbol *scc)
1974 {
1975     // We need to always put out the segments in triples, so that the
1976     // linker will put them in the correct order.
1977     static immutable char[12] lnames =
1978         "\03FMB\02FM\03FME";
1979 
1980     symbol_debug(scc);
1981 
1982     if (obj.fmsegi == 0)
1983     {
1984         // Put out LNAMES record
1985         objrecord(LNAMES,lnames.ptr,lnames.sizeof - 1);
1986 
1987         int dsegattr = I32
1988             ? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32)
1989             : SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);
1990 
1991         // Put out beginning segment
1992         objsegdef(dsegattr,0,obj.lnameidx,DATACLASS);
1993         obj.lnameidx++;
1994         obj.segidx++;
1995 
1996         // Put out segment definition record
1997         obj.fmsegi = obj_newfarseg(0,DATACLASS);
1998         objsegdef(dsegattr,0,obj.lnameidx,DATACLASS);
1999         SegData[obj.fmsegi].attr = dsegattr;
2000         assert(SegData[obj.fmsegi].segidx == obj.segidx);
2001 
2002         // Put out ending segment
2003         objsegdef(dsegattr,0,obj.lnameidx + 1,DATACLASS);
2004 
2005         obj.lnameidx += 2;              // for next time
2006         obj.segidx += 2;
2007     }
2008 
2009     targ_size_t offset = SegData[obj.fmsegi].SDoffset;
2010     offset += OmfObj_reftoident(obj.fmsegi,offset,scc,0,LARGECODE ? CFoff | CFseg : CFoff);     // put out function pointer
2011     SegData[obj.fmsegi].SDoffset = offset;
2012 }
2013 
2014 }
2015 
2016 
2017 /*********************************
2018  * Setup for Symbol s to go into a COMDAT segment.
2019  * Output (if s is a function):
2020  *      cseg            segment index of new current code segment
2021  *      Coffset         starting offset in cseg
2022  * Returns:
2023  *      "segment index" of COMDAT (which will be a negative value to
2024  *      distinguish it from regular segments).
2025  */
2026 
2027 int OmfObj_comdatsize(Symbol *s, targ_size_t symsize)
2028 {
2029     return generate_comdat(s, false);
2030 }
2031 
2032 int OmfObj_comdat(Symbol *s)
2033 {
2034     return generate_comdat(s, false);
2035 }
2036 
2037 int OmfObj_readonly_comdat(Symbol *s)
2038 {
2039     s.Sseg = generate_comdat(s, true);
2040     return s.Sseg;
2041 }
2042 
2043 static int generate_comdat(Symbol *s, bool is_readonly_comdat)
2044 {
2045     char[IDMAX+IDOHD+1] lnames = void; // +1 to allow room for strcpy() terminating 0
2046     char[2+2] cextdef = void;
2047     char *p;
2048     size_t lnamesize;
2049     uint ti;
2050     int isfunc;
2051     tym_t ty;
2052 
2053     symbol_debug(s);
2054     obj.resetSymbols.push(s);
2055     ty = s.ty();
2056     isfunc = tyfunc(ty) != 0 || is_readonly_comdat;
2057 
2058     // Put out LNAME for name of Symbol
2059     lnamesize = OmfObj_mangle(s,lnames.ptr);
2060     objrecord((s.Sclass == SCstatic ? LLNAMES : LNAMES),lnames.ptr,cast(uint)lnamesize);
2061 
2062     // Put out CEXTDEF for name of Symbol
2063     outextdata();
2064     p = cextdef.ptr;
2065     p += insidx(p,obj.lnameidx++);
2066     ti = (config.fulltypes == CVOLD) ? cv_typidx(s.Stype) : 0;
2067     p += instypidx(p,ti);
2068     objrecord(CEXTDEF,cextdef.ptr,cast(uint)(p - cextdef.ptr));
2069     s.Sxtrnnum = ++obj.extidx;
2070 
2071     seg_data *pseg = getsegment();
2072     pseg.segidx = -obj.extidx;
2073     assert(pseg.SDseg > 0);
2074 
2075     // Start new LEDATA record for this COMDAT
2076     Ledatarec *lr = ledata_new(pseg.SDseg,0);
2077     lr.typidx = ti;
2078     lr.pubnamidx = obj.lnameidx - 1;
2079     if (isfunc)
2080     {   lr.pubbase = SegData[cseg].segidx;
2081         if (s.Sclass == SCcomdat || s.Sclass == SCinline)
2082             lr.alloctyp = 0x10 | 0x00; // pick any instance | explicit allocation
2083         if (is_readonly_comdat)
2084         {
2085             assert(lr.lseg > 0 && lr.lseg < SegData.length);
2086             lr.flags |= 0x08;      // data in code seg
2087         }
2088         else
2089         {
2090             cseg = lr.lseg;
2091             assert(cseg > 0 && cseg < SegData.length);
2092             obj.pubnamidx = obj.lnameidx - 1;
2093             Offset(cseg) = 0;
2094             if (tyfarfunc(ty) && strcmp(s.Sident.ptr,"main") == 0)
2095                 lr.alloctyp |= 1;  // because MS does for unknown reasons
2096         }
2097     }
2098     else
2099     {
2100         ubyte atyp;
2101 
2102         switch (ty & mTYLINK)
2103         {
2104             case 0:
2105             case mTYnear:       lr.pubbase = DATA;
2106 static if (0)
2107                                 atyp = 0;       // only one instance is allowed
2108 else
2109                                 atyp = 0x10;    // pick any (also means it is
2110                                                 // not searched for in a library)
2111 
2112                                 break;
2113 
2114             case mTYcs:         lr.flags |= 0x08;      // data in code seg
2115                                 atyp = 0x11;    break;
2116 
2117             case mTYfar:        atyp = 0x12;    break;
2118 
2119             case mTYthread:     lr.pubbase = OmfObj_tlsseg().segidx;
2120                                 atyp = 0x10;    // pick any (also means it is
2121                                                 // not searched for in a library)
2122                                 break;
2123 
2124             default:            assert(0);
2125         }
2126         lr.alloctyp = atyp;
2127     }
2128     if (s.Sclass == SCstatic)
2129         lr.flags |= 0x04;      // local bit (make it an "LCOMDAT")
2130     s.Soffset = 0;
2131     s.Sseg = pseg.SDseg;
2132     return pseg.SDseg;
2133 }
2134 
2135 /***********************************
2136  * Returns:
2137  *      jump table segment for function s
2138  */
2139 int OmfObj_jmpTableSegment(Symbol *s)
2140 {
2141     return (config.flags & CFGromable) ? cseg : DATA;
2142 }
2143 
2144 /**********************************
2145  * Reset code seg to existing seg.
2146  * Used after a COMDAT for a function is done.
2147  */
2148 
2149 void OmfObj_setcodeseg(int seg)
2150 {
2151     assert(0 < seg && seg < SegData.length);
2152     cseg = seg;
2153 }
2154 
2155 /********************************
2156  * Define a new code segment.
2157  * Input:
2158  *      name            name of segment, if null then revert to default
2159  *      suffix  0       use name as is
2160  *              1       append "_TEXT" to name
2161  * Output:
2162  *      cseg            segment index of new current code segment
2163  *      Coffset         starting offset in cseg
2164  * Returns:
2165  *      segment index of newly created code segment
2166  */
2167 
2168 int OmfObj_codeseg(const char *name,int suffix)
2169 {
2170     if (!name)
2171     {
2172         if (cseg != CODE)
2173         {
2174             cseg = CODE;
2175         }
2176         return cseg;
2177     }
2178 
2179     // Put out LNAMES record
2180     size_t lnamesize = strlen(name) + suffix * 5;
2181     char *lnames = cast(char *) alloca(1 + lnamesize + 1);
2182     lnames[0] = cast(char)lnamesize;
2183     assert(lnamesize <= (255 - 2 - int.sizeof*3));
2184     strcpy(lnames + 1,name);
2185     if (suffix)
2186         strcat(lnames + 1,"_TEXT");
2187     objrecord(LNAMES,lnames,cast(uint)(lnamesize + 1));
2188 
2189     cseg = obj_newfarseg(0,4);
2190     SegData[cseg].attr = obj.csegattr;
2191     SegData[cseg].segidx = obj.segidx;
2192     assert(cseg > 0);
2193     obj.segidx++;
2194     Offset(cseg) = 0;
2195 
2196     objsegdef(obj.csegattr,0,obj.lnameidx++,4);
2197 
2198     return cseg;
2199 }
2200 
2201 /*********************************
2202  * Define segment for Thread Local Storage.
2203  * Output:
2204  *      tlsseg  set to segment number for TLS segment.
2205  * Returns:
2206  *      segment for TLS segment
2207  */
2208 
2209 seg_data* OmfObj_tlsseg_bss() { return OmfObj_tlsseg(); }
2210 
2211 seg_data* OmfObj_tlsseg()
2212 {
2213     //static char tlssegname[] = "\04$TLS\04$TLS";
2214     //static char tlssegname[] = "\05.tls$\03tls";
2215     static immutable char[25] tlssegname = "\05.tls$\03tls\04.tls\010.tls$ZZZ";
2216 
2217     assert(tlssegname[tlssegname.length - 5] == '$');
2218 
2219     if (obj.tlssegi == 0)
2220     {
2221         int segattr;
2222 
2223         objrecord(LNAMES,tlssegname.ptr,tlssegname.sizeof - 1);
2224 
2225 version (MARS)
2226         segattr = SEG_ATTR(SEG_ALIGN16,SEG_C_PUBLIC,0,USE32);
2227 else
2228         segattr = I32
2229             ? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32)
2230             : SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);
2231 
2232 
2233         // Put out beginning segment (.tls)
2234         objsegdef(segattr,0,obj.lnameidx + 2,obj.lnameidx + 1);
2235         obj.segidx++;
2236 
2237         // Put out .tls$ segment definition record
2238         obj.tlssegi = obj_newfarseg(0,obj.lnameidx + 1);
2239         objsegdef(segattr,0,obj.lnameidx,obj.lnameidx + 1);
2240         SegData[obj.tlssegi].attr = segattr;
2241         SegData[obj.tlssegi].segidx = obj.segidx;
2242 
2243         // Put out ending segment (.tls$ZZZ)
2244         objsegdef(segattr,0,obj.lnameidx + 3,obj.lnameidx + 1);
2245 
2246         obj.lnameidx += 4;
2247         obj.segidx += 2;
2248     }
2249     return SegData[obj.tlssegi];
2250 }
2251 
2252 seg_data *OmfObj_tlsseg_data()
2253 {
2254     // specific for Mach-O
2255     assert(0);
2256 }
2257 
2258 /********************************
2259  * Define a far data segment.
2260  * Input:
2261  *      name    Name of module
2262  *      size    Size of the segment to be created
2263  * Returns:
2264  *      segment index of far data segment created
2265  *      *poffset start of the data for the far data segment
2266  */
2267 
2268 int OmfObj_fardata(char *name,targ_size_t size,targ_size_t *poffset)
2269 {
2270     static immutable char[10] fardataclass = "\010FAR_DATA";
2271     int len;
2272     int i;
2273     char *buffer;
2274 
2275     // See if we can use existing far segment, and just bump its size
2276     i = obj.lastfardatasegi;
2277     if (i != -1
2278         && (_tysize[TYint] != 2 || cast(uint) SegData[i].SDoffset + size < 0x8000)
2279         )
2280     {   *poffset = SegData[i].SDoffset;        // BUG: should align this
2281         SegData[i].SDoffset += size;
2282         return i;
2283     }
2284 
2285     // No. We need to build a new far segment
2286 
2287     if (obj.fardataidx == 0)            // if haven't put out far data lname
2288     {   // Put out class lname
2289         objrecord(LNAMES,fardataclass.ptr,fardataclass.sizeof - 1);
2290         obj.fardataidx = obj.lnameidx++;
2291     }
2292 
2293     // Generate name based on module name
2294     name = strupr(filespecgetroot(filespecname(obj.modname)));
2295 
2296     // Generate name for this far segment
2297     len = 1 + cast(int)strlen(name) + 3 + 5 + 1;
2298     buffer = cast(char *)alloca(len);
2299     sprintf(buffer + 1,"%s%d_DATA",name,obj.segidx);
2300     len = cast(int)strlen(buffer + 1);
2301     buffer[0] = cast(char)len;
2302     assert(len <= 255);
2303     objrecord(LNAMES,buffer,len + 1);
2304 
2305     mem_free(name);
2306 
2307     // Construct a new SegData[] entry
2308     obj.lastfardatasegi = obj_newfarseg(size,obj.fardataidx);
2309 
2310     // Generate segment definition
2311     objsegdef(obj.fdsegattr,size,obj.lnameidx++,obj.fardataidx);
2312     obj.segidx++;
2313 
2314     *poffset = 0;
2315     return SegData[obj.lastfardatasegi].SDseg;
2316 }
2317 
2318 /************************************
2319  * Remember where we put a far segment so we can adjust
2320  * its size later.
2321  * Input:
2322  *      obj.segidx
2323  *      lnameidx
2324  * Returns:
2325  *      index of SegData[]
2326  */
2327 
2328 private int obj_newfarseg(targ_size_t size,int classidx)
2329 {
2330     seg_data *f = getsegment();
2331     f.isfarseg = true;
2332     f.seek = cast(int)obj.buf.length();
2333     f.attr = obj.fdsegattr;
2334     f.origsize = size;
2335     f.SDoffset = size;
2336     f.segidx = obj.segidx;
2337     f.lnameidx = obj.lnameidx;
2338     f.classidx = classidx;
2339     return f.SDseg;
2340 }
2341 
2342 /******************************
2343  * Convert reference to imported name.
2344  */
2345 
2346 void OmfObj_import(elem *e)
2347 {
2348 version (MARS)
2349     assert(0);
2350 else
2351 {
2352     Symbol *s;
2353     Symbol *simp;
2354 
2355     elem_debug(e);
2356     if ((e.Eoper == OPvar || e.Eoper == OPrelconst) &&
2357         (s = e.EV.Vsym).ty() & mTYimport &&
2358         (s.Sclass == SCextern || s.Sclass == SCinline)
2359        )
2360     {
2361         char* name;
2362         char* p;
2363         size_t len;
2364         char[IDMAX + IDOHD + 1] buffer = void;
2365 
2366         // Create import name
2367         len = OmfObj_mangle(s,buffer.ptr);
2368         if (buffer[0] == cast(char)0xFF && buffer[1] == 0)
2369         {   name = buffer.ptr + 4;
2370             len -= 4;
2371         }
2372         else
2373         {   name = buffer.ptr + 1;
2374             len -= 1;
2375         }
2376         if (config.flags4 & CFG4underscore)
2377         {   p = cast(char *) alloca(5 + len + 1);
2378             memcpy(p,"_imp_".ptr,5);
2379             memcpy(p + 5,name,len);
2380             p[5 + len] = 0;
2381         }
2382         else
2383         {   p = cast(char *) alloca(6 + len + 1);
2384             memcpy(p,"__imp_".ptr,6);
2385             memcpy(p + 6,name,len);
2386             p[6 + len] = 0;
2387         }
2388         simp = scope_search(p,SCTglobal);
2389         if (!simp)
2390         {   type *t;
2391 
2392             simp = scope_define(p,SCTglobal,SCextern);
2393             simp.Ssequence = 0;
2394             simp.Sfl = FLextern;
2395             simp.Simport = s;
2396             t = newpointer(s.Stype);
2397             t.Tmangle = mTYman_c;
2398             t.Tcount++;
2399             simp.Stype = t;
2400         }
2401         assert(!e.EV.Voffset);
2402         if (e.Eoper == OPrelconst)
2403         {
2404             e.Eoper = OPvar;
2405             e.EV.Vsym = simp;
2406         }
2407         else // OPvar
2408         {
2409             e.Eoper = OPind;
2410             e.EV.E1 = el_var(simp);
2411             e.EV.E2 = null;
2412         }
2413     }
2414 }
2415 }
2416 
2417 /*******************************
2418  * Mangle a name.
2419  * Returns:
2420  *      length of mangled name
2421  */
2422 
2423 size_t OmfObj_mangle(Symbol *s,char *dest)
2424 {   size_t len;
2425     size_t ilen;
2426     const(char)* name;
2427     char *name2 = null;
2428 
2429     //printf("OmfObj_mangle('%s'), mangle = x%x\n",s.Sident.ptr,type_mangle(s.Stype));
2430 version (SCPP)
2431     name = CPP ? cpp_mangle(s) : s.Sident.ptr;
2432 else version (MARS)
2433     name = cast(char*)cpp_mangle(s);
2434 else
2435     static assert(0);
2436 
2437     len = strlen(name);                 // # of bytes in name
2438 
2439     // Use as max length the max length lib.exe can handle
2440     // Use 5 as length of _ + @nnn
2441 //    enum LIBIDMAX = ((512 - 0x25 - 3 - 4) - 5);
2442     enum LIBIDMAX = 128;
2443     if (len > LIBIDMAX)
2444     //if (len > IDMAX)
2445     {
2446         size_t len2;
2447 
2448         // Attempt to compress the name
2449         name2 = id_compress(name, cast(int)len, &len2);
2450 version (MARS)
2451 {
2452         if (len2 > LIBIDMAX)            // still too long
2453         {
2454             /* Form md5 digest of the name and store it in the
2455              * last 32 bytes of the name.
2456              */
2457             MD5_CTX mdContext;
2458             MD5Init(&mdContext);
2459             MD5Update(&mdContext, cast(ubyte *)name, cast(uint)len);
2460             MD5Final(&mdContext);
2461             memcpy(name2, name, LIBIDMAX - 32);
2462             for (int i = 0; i < 16; i++)
2463             {   ubyte c = mdContext.digest[i];
2464                 ubyte c1 = (c >> 4) & 0x0F;
2465                 ubyte c2 = c & 0x0F;
2466                 c1 += (c1 < 10) ? '0' : 'A' - 10;
2467                 name2[LIBIDMAX - 32 + i * 2] = c1;
2468                 c2 += (c2 < 10) ? '0' : 'A' - 10;
2469                 name2[LIBIDMAX - 32 + i * 2 + 1] = c2;
2470             }
2471             len = LIBIDMAX;
2472             name2[len] = 0;
2473             name = name2;
2474             //printf("name = '%s', len = %d, strlen = %d\n", name, len, strlen(name));
2475         }
2476         else
2477         {
2478             name = name2;
2479             len = len2;
2480         }
2481 }
2482 else
2483 {
2484         if (len2 > IDMAX)               // still too long
2485         {
2486 version (SCPP)
2487             synerr(EM_identifier_too_long, name, len - IDMAX, IDMAX);
2488 else version (MARS)
2489 {
2490 //          error(Loc(), "identifier %s is too long by %d characters", name, len - IDMAX);
2491 }
2492 else
2493             assert(0);
2494 
2495             len = IDMAX;
2496         }
2497         else
2498         {
2499             name = name2;
2500             len = len2;
2501         }
2502 }
2503     }
2504     ilen = len;
2505     if (ilen > (255-2-int.sizeof*3))
2506         dest += 3;
2507     switch (type_mangle(s.Stype))
2508     {
2509         case mTYman_pas:                // if upper case
2510         case mTYman_for:
2511             memcpy(dest + 1,name,len);  // copy in name
2512             dest[1 + len] = 0;
2513             strupr(dest + 1);           // to upper case
2514             break;
2515 
2516         case mTYman_cpp:
2517             memcpy(dest + 1,name,len);
2518             break;
2519 
2520         case mTYman_std:
2521             if (!(config.flags4 & CFG4oldstdmangle) &&
2522                 config.exe == EX_WIN32 && tyfunc(s.ty()) &&
2523                 !variadic(s.Stype))
2524             {
2525                 dest[1] = '_';
2526                 memcpy(dest + 2,name,len);
2527                 dest[1 + 1 + len] = '@';
2528                 itoa(type_paramsize(s.Stype),dest + 3 + len,10);
2529                 len = strlen(dest + 1);
2530                 assert(isdigit(dest[len]));
2531                 break;
2532             }
2533             goto case;
2534 
2535         case mTYman_c:
2536         case mTYman_d:
2537             if (config.flags4 & CFG4underscore)
2538             {
2539                 dest[1] = '_';          // leading _ in name
2540                 memcpy(&dest[2],name,len);      // copy in name
2541                 len++;
2542                 break;
2543             }
2544             goto case;
2545 
2546         case mTYman_sys:
2547             memcpy(dest + 1, name, len);        // no mangling
2548             dest[1 + len] = 0;
2549             break;
2550         default:
2551             symbol_print(s);
2552             assert(0);
2553     }
2554     if (ilen > (255-2-int.sizeof*3))
2555     {
2556         dest -= 3;
2557         dest[0] = 0xFF;
2558         dest[1] = 0;
2559         debug
2560         assert(len <= 0xFFFF);
2561 
2562         TOWORD(dest + 2,cast(uint)len);
2563         len += 4;
2564     }
2565     else
2566     {
2567         *dest = cast(char)len;
2568         len++;
2569     }
2570     if (name2)
2571         free(name2);
2572     assert(len <= IDMAX + IDOHD);
2573     return len;
2574 }
2575 
2576 /*******************************
2577  * Export a function name.
2578  */
2579 
2580 void OmfObj_export_symbol(Symbol* s, uint argsize)
2581 {
2582     char* coment;
2583     size_t len;
2584 
2585     coment = cast(char *) alloca(4 + 1 + (IDMAX + IDOHD) + 1); // allow extra byte for mangling
2586     len = OmfObj_mangle(s,&coment[4]);
2587     assert(len <= IDMAX + IDOHD);
2588     coment[1] = 0xA0;                           // comment class
2589     coment[2] = 2;                              // why??? who knows
2590     if (argsize >= 64)                          // we only have a 5 bit field
2591         argsize = 0;                            // hope we don't need callgate
2592     coment[3] = cast(char)((argsize + 1) >> 1); // # words on stack
2593     coment[4 + len] = 0;                        // no internal name
2594     objrecord(COMENT,coment,cast(uint)(4 + len + 1));       // module name record
2595 }
2596 
2597 /*******************************
2598  * Update data information about symbol
2599  *      align for output and assign segment
2600  *      if not already specified.
2601  *
2602  * Input:
2603  *      sdata           data symbol
2604  *      datasize        output size
2605  *      seg             default seg if not known
2606  * Returns:
2607  *      actual seg
2608  */
2609 
2610 int OmfObj_data_start(Symbol *sdata, targ_size_t datasize, int seg)
2611 {
2612     targ_size_t alignbytes;
2613     //printf("OmfObj_data_start(%s,size %llx,seg %d)\n",sdata.Sident.ptr,datasize,seg);
2614     //symbol_print(sdata);
2615 
2616     if (sdata.Sseg == UNKNOWN) // if we don't know then there
2617         sdata.Sseg = seg;      // wasn't any segment override
2618     else
2619         seg = sdata.Sseg;
2620     targ_size_t offset = SegData[seg].SDoffset;
2621     if (sdata.Salignment > 0)
2622     {
2623         if (SegData[seg].SDalignment < sdata.Salignment)
2624             SegData[seg].SDalignment = sdata.Salignment;
2625         alignbytes = ((offset + sdata.Salignment - 1) & ~(sdata.Salignment - 1)) - offset;
2626     }
2627     else
2628         alignbytes = _align(datasize, offset) - offset;
2629     sdata.Soffset = offset + alignbytes;
2630     SegData[seg].SDoffset = sdata.Soffset;
2631     return seg;
2632 }
2633 
2634 void OmfObj_func_start(Symbol *sfunc)
2635 {
2636     //printf("OmfObj_func_start(%s)\n",sfunc.Sident.ptr);
2637     symbol_debug(sfunc);
2638     sfunc.Sseg = cseg;             // current code seg
2639     sfunc.Soffset = Offset(cseg);       // offset of start of function
2640 
2641 version (MARS)
2642     varStats_startFunction();
2643 }
2644 
2645 /*******************************
2646  * Update function info after codgen
2647  */
2648 
2649 void OmfObj_func_term(Symbol *sfunc)
2650 {
2651 }
2652 
2653 /********************************
2654  * Output a public definition.
2655  * Input:
2656  *      seg =           segment index that symbol is defined in
2657  *      s .            symbol
2658  *      offset =        offset of name
2659  */
2660 
2661 private void outpubdata()
2662 {
2663     if (obj.pubdatai)
2664     {
2665         objrecord(obj.mpubdef,obj.pubdata.ptr,obj.pubdatai);
2666         obj.pubdatai = 0;
2667     }
2668 }
2669 
2670 void OmfObj_pubdef(int seg,Symbol *s,targ_size_t offset)
2671 {
2672     uint reclen, len;
2673     char* p;
2674     uint ti;
2675 
2676     assert(offset < 100_000_000);
2677     obj.resetSymbols.push(s);
2678 
2679     int idx = SegData[seg].segidx;
2680     if (obj.pubdatai + 1 + (IDMAX + IDOHD) + 4 + 2 > obj.pubdata.sizeof ||
2681         idx != getindex(cast(ubyte*)obj.pubdata.ptr + 1))
2682         outpubdata();
2683     if (obj.pubdatai == 0)
2684     {
2685         obj.pubdata[0] = (seg == DATA || seg == CDATA || seg == UDATA) ? 1 : 0; // group index
2686         obj.pubdatai += 1 + insidx(obj.pubdata.ptr + 1,idx);        // segment index
2687     }
2688     p = &obj.pubdata[obj.pubdatai];
2689     len = cast(uint)OmfObj_mangle(s,p);              // mangle in name
2690     reclen = len + _tysize[TYint];
2691     p += len;
2692     TOOFFSET(p,offset);
2693     p += _tysize[TYint];
2694     ti = (config.fulltypes == CVOLD) ? cv_typidx(s.Stype) : 0;
2695     reclen += instypidx(p,ti);
2696     obj.pubdatai += reclen;
2697 }
2698 
2699 void OmfObj_pubdefsize(int seg, Symbol *s, targ_size_t offset, targ_size_t symsize)
2700 {
2701     OmfObj_pubdef(seg, s, offset);
2702 }
2703 
2704 /*******************************
2705  * Output an external definition.
2706  * Input:
2707  *      name . external identifier
2708  * Returns:
2709  *      External index of the definition (1,2,...)
2710  */
2711 
2712 private void outextdata()
2713 {
2714     if (obj.extdatai)
2715     {
2716         objrecord(EXTDEF, obj.extdata.ptr, obj.extdatai);
2717         obj.extdatai = 0;
2718     }
2719 }
2720 
2721 int OmfObj_external_def(const(char)* name)
2722 {
2723     uint len;
2724     char *e;
2725 
2726     //printf("OmfObj_external_def('%s', %d)\n",name,obj.extidx + 1);
2727     assert(name);
2728     len = cast(uint)strlen(name);                 // length of identifier
2729     if (obj.extdatai + len + ONS_OHD + 1 > obj.extdata.sizeof)
2730         outextdata();
2731 
2732     e = &obj.extdata[obj.extdatai];
2733     len = obj_namestring(e,name);
2734     e[len] = 0;                         // typidx = 0
2735     obj.extdatai += len + 1;
2736     assert(obj.extdatai <= obj.extdata.sizeof);
2737     return ++obj.extidx;
2738 }
2739 
2740 /*******************************
2741  * Output an external definition.
2742  * Input:
2743  *      s       Symbol to do EXTDEF on
2744  * Returns:
2745  *      External index of the definition (1,2,...)
2746  */
2747 
2748 int OmfObj_external(Symbol *s)
2749 {
2750     //printf("OmfObj_external('%s', %d)\n",s.Sident.ptr, obj.extidx + 1);
2751     symbol_debug(s);
2752     obj.resetSymbols.push(s);
2753     if (obj.extdatai + (IDMAX + IDOHD) + 3 > obj.extdata.sizeof)
2754         outextdata();
2755 
2756     char *e = &obj.extdata[obj.extdatai];
2757     uint len = cast(uint)OmfObj_mangle(s,e);
2758     e[len] = 0;                 // typidx = 0
2759     obj.extdatai += len + 1;
2760     s.Sxtrnnum = ++obj.extidx;
2761     return obj.extidx;
2762 }
2763 
2764 /*******************************
2765  * Output a common block definition.
2766  * Input:
2767  *      p .    external identifier
2768  *      flag    TRUE:   in default data segment
2769  *              FALSE:  not in default data segment
2770  *      size    size in bytes of each elem
2771  *      count   number of elems
2772  * Returns:
2773  *      External index of the definition (1,2,...)
2774  */
2775 
2776 // Helper for OmfObj_common_block()
2777 
2778 static uint storelength(uint length,uint i)
2779 {
2780     obj.extdata[i] = cast(char)length;
2781     if (length >= 128)  // Microsoft docs say 129, but their linker
2782                         // won't take >=128, so accommodate it
2783     {   obj.extdata[i] = 129;
2784         debug
2785         assert(length <= 0xFFFF);
2786 
2787         TOWORD(obj.extdata.ptr + i + 1,length);
2788         if (length >= 0x10000)
2789         {   obj.extdata[i] = 132;
2790             obj.extdata[i + 3] = cast(char)(length >> 16);
2791 
2792             // Only 386 can generate lengths this big
2793             if (I32 && length >= 0x1000000)
2794             {   obj.extdata[i] = 136;
2795                 obj.extdata[i + 4] = length >> 24;
2796                 i += 4;
2797             }
2798             else
2799                 i += 3;
2800         }
2801         else
2802             i += 2;
2803     }
2804     return i + 1;               // index past where we stuffed length
2805 }
2806 
2807 int OmfObj_common_block(Symbol *s,targ_size_t size,targ_size_t count)
2808 {
2809     return OmfObj_common_block(s, 0, size, count);
2810 }
2811 
2812 int OmfObj_common_block(Symbol *s,int flag,targ_size_t size,targ_size_t count)
2813 {
2814   uint i;
2815   uint length;
2816   uint ti;
2817 
2818     //printf("OmfObj_common_block('%s',%d,%d,%d, %d)\n",s.Sident.ptr,flag,size,count, obj.extidx + 1);
2819     obj.resetSymbols.push(s);
2820     outextdata();               // borrow the extdata[] storage
2821     i = cast(uint)OmfObj_mangle(s,obj.extdata.ptr);
2822 
2823     ti = (config.fulltypes == CVOLD) ? cv_typidx(s.Stype) : 0;
2824     i += instypidx(obj.extdata.ptr + i,ti);
2825 
2826   if (flag)                             // if in default data segment
2827   {
2828         //printf("NEAR comdef\n");
2829         obj.extdata[i] = 0x62;
2830         length = cast(uint) size * cast(uint) count;
2831         assert(I32 || length <= 0x10000);
2832         i = storelength(length,i + 1);
2833   }
2834   else
2835   {
2836         //printf("FAR comdef\n");
2837         obj.extdata[i] = 0x61;
2838         i = storelength(cast(uint) size,i + 1);
2839         i = storelength(cast(uint) count,i);
2840   }
2841   assert(i <= obj.extdata.length);
2842   objrecord(COMDEF,obj.extdata.ptr,i);
2843   return ++obj.extidx;
2844 }
2845 
2846 /***************************************
2847  * Append an iterated data block of 0s.
2848  * (uninitialized data only)
2849  */
2850 
2851 void OmfObj_write_zeros(seg_data *pseg, targ_size_t count)
2852 {
2853     OmfObj_lidata(pseg.SDseg, pseg.SDoffset, count);
2854     //pseg.SDoffset += count;
2855 }
2856 
2857 /***************************************
2858  * Output an iterated data block of 0s.
2859  * (uninitialized data only)
2860  */
2861 
2862 void OmfObj_lidata(int seg,targ_size_t offset,targ_size_t count)
2863 {   int i;
2864     uint reclen;
2865     static immutable char[20] zero = 0;
2866     char[20] data = void;
2867     char *di;
2868 
2869     //printf("OmfObj_lidata(seg = %d, offset = x%x, count = %d)\n", seg, offset, count);
2870 
2871     SegData[seg].SDoffset += count;
2872 
2873     if (seg == UDATA)
2874         return;
2875     int idx = SegData[seg].segidx;
2876 
2877 Lagain:
2878     if (count <= zero.sizeof)          // if shorter to use ledata
2879     {
2880         OmfObj_bytes(seg,offset,cast(uint)count,cast(char*)zero.ptr);
2881         return;
2882     }
2883 
2884     if (seg_is_comdat(idx))
2885     {
2886         while (count > zero.sizeof)
2887         {
2888             OmfObj_bytes(seg,offset,zero.sizeof,cast(char*)zero.ptr);
2889             offset += zero.sizeof;
2890             count -= zero.sizeof;
2891         }
2892         OmfObj_bytes(seg,offset,cast(uint)count,cast(char*)zero.ptr);
2893         return;
2894     }
2895 
2896     i = insidx(data.ptr,idx);
2897     di = data.ptr + i;
2898     TOOFFSET(di,offset);
2899 
2900     if (config.flags & CFGeasyomf)
2901     {
2902         if (count >= 0x8000)            // repeat count can only go to 32k
2903         {
2904             TOWORD(di + 4,cast(ushort)(count / 0x8000));
2905             TOWORD(di + 4 + 2,1);               // 1 data block follows
2906             TOWORD(di + 4 + 2 + 2,0x8000);      // repeat count
2907             TOWORD(di + 4 + 2 + 2 + 2,0);       // block count
2908             TOWORD(di + 4 + 2 + 2 + 2 + 2,1);   // 1 byte of 0
2909             reclen = i + 4 + 5 * 2;
2910             objrecord(obj.mlidata,data.ptr,reclen);
2911 
2912             offset += (count & ~cast(targ_size_t)0x7FFF);
2913             count &= 0x7FFF;
2914             goto Lagain;
2915         }
2916         else
2917         {
2918             TOWORD(di + 4,cast(ushort)count);       // repeat count
2919             TOWORD(di + 4 + 2,0);                       // block count
2920             TOWORD(di + 4 + 2 + 2,1);                   // 1 byte of 0
2921             reclen = i + 4 + 2 + 2 + 2;
2922             objrecord(obj.mlidata,data.ptr,reclen);
2923         }
2924     }
2925     else
2926     {
2927         TOOFFSET(di + _tysize[TYint],count);
2928         TOWORD(di + _tysize[TYint] * 2,0);     // block count
2929         TOWORD(di + _tysize[TYint] * 2 + 2,1); // repeat 1 byte of 0s
2930         reclen = i + (I32 ? 12 : 8);
2931         objrecord(obj.mlidata,data.ptr,reclen);
2932     }
2933     assert(reclen <= data.sizeof);
2934 }
2935 
2936 /****************************
2937  * Output a MODEND record.
2938  */
2939 
2940 private void obj_modend()
2941 {
2942     if (obj.startaddress)
2943     {   char[10] mdata = void;
2944         int i;
2945         uint framedatum,targetdatum;
2946         ubyte fd;
2947         targ_size_t offset;
2948         int external;           // !=0 if identifier is defined externally
2949         tym_t ty;
2950         Symbol *s = obj.startaddress;
2951 
2952         // Turn startaddress into a fixup.
2953         // Borrow heavilly from OmfObj_reftoident()
2954 
2955         obj.resetSymbols.push(s);
2956         symbol_debug(s);
2957         offset = 0;
2958         ty = s.ty();
2959 
2960         switch (s.Sclass)
2961         {
2962             case SCcomdat:
2963             case_SCcomdat:
2964             case SCextern:
2965             case SCcomdef:
2966                 if (s.Sxtrnnum)                // identifier is defined somewhere else
2967                     external = s.Sxtrnnum;
2968                 else
2969                 {
2970                  Ladd:
2971                     s.Sclass = SCextern;
2972                     external = objmod.external(s);
2973                     outextdata();
2974                 }
2975                 break;
2976             case SCinline:
2977                 if (config.flags2 & CFG2comdat)
2978                     goto case_SCcomdat; // treat as initialized common block
2979                 goto case;
2980 
2981             case SCsinline:
2982             case SCstatic:
2983             case SCglobal:
2984                 if (s.Sseg == UNKNOWN)
2985                     goto Ladd;
2986                 if (seg_is_comdat(SegData[s.Sseg].segidx))   // if in comdat
2987                     goto case_SCcomdat;
2988                 goto case;
2989 
2990             case SClocstat:
2991                 external = 0;           // identifier is static or global
2992                                             // and we know its offset
2993                 offset += s.Soffset;
2994                 break;
2995             default:
2996                 //symbol_print(s);
2997                 assert(0);
2998         }
2999 
3000         if (external)
3001         {   fd = FD_T2;
3002             targetdatum = external;
3003             switch (s.Sfl)
3004             {
3005                 case FLextern:
3006                     if (!(ty & (mTYcs | mTYthread)))
3007                         goto L1;
3008                     goto case;
3009 
3010                 case FLfunc:
3011                 case FLfardata:
3012                 case FLcsdata:
3013                 case FLtlsdata:
3014                     if (config.exe & EX_flat)
3015                     {   fd |= FD_F1;
3016                         framedatum = 1;
3017                     }
3018                     else
3019                     {
3020                 //case FLtlsdata:
3021                         fd |= FD_F2;
3022                         framedatum = targetdatum;
3023                     }
3024                     break;
3025                 default:
3026                     goto L1;
3027             }
3028         }
3029         else
3030         {
3031             fd = FD_T0;                 // target is always a segment
3032             targetdatum = SegData[s.Sseg].segidx;
3033             assert(targetdatum != -1);
3034             switch (s.Sfl)
3035             {
3036                 case FLextern:
3037                     if (!(ty & (mTYcs | mTYthread)))
3038                         goto L1;
3039                     goto case;
3040 
3041                 case FLfunc:
3042                 case FLfardata:
3043                 case FLcsdata:
3044                 case FLtlsdata:
3045                     if (config.exe & EX_flat)
3046                     {   fd |= FD_F1;
3047                         framedatum = 1;
3048                     }
3049                     else
3050                     {
3051                 //case FLtlsdata:
3052                         fd |= FD_F0;
3053                         framedatum = targetdatum;
3054                     }
3055                     break;
3056                 default:
3057                 L1:
3058                     fd |= FD_F1;
3059                     framedatum = DGROUPIDX;
3060                     //if (flags == CFseg)
3061                     {   fd = FD_F1 | FD_T1;     // target is DGROUP
3062                         targetdatum = DGROUPIDX;
3063                     }
3064                     break;
3065             }
3066         }
3067 
3068         // Write the fixup into mdata[]
3069         mdata[0] = 0xC1;
3070         mdata[1] = fd;
3071         i = 2 + insidx(&mdata[2],framedatum);
3072         i += insidx(&mdata[i],targetdatum);
3073         TOOFFSET(mdata.ptr + i,offset);
3074 
3075         objrecord(obj.mmodend,mdata.ptr,i + _tysize[TYint]);       // write mdata[] to .OBJ file
3076     }
3077     else
3078     {   static immutable char[1] modend = [0];
3079 
3080         objrecord(obj.mmodend,modend.ptr,modend.sizeof);
3081     }
3082 }
3083 
3084 /****************************
3085  * Output the fixups in list fl.
3086  */
3087 
3088 private void objfixupp(FIXUP *f)
3089 {
3090   uint i,j,k;
3091   targ_size_t locat;
3092   FIXUP *fn;
3093 
3094 static if (1)   // store in one record
3095 {
3096   char[1024] data = void;
3097 
3098   i = 0;
3099   for (; f; f = fn)
3100   {     ubyte fd;
3101 
3102         if (i >= data.sizeof - (3 + 2 + 2))    // if not enough room
3103         {   objrecord(obj.mfixupp,data.ptr,i);
3104             i = 0;
3105         }
3106 
3107         //printf("f = %p, offset = x%x\n",f,f.FUoffset);
3108         assert(f.FUoffset < 1024);
3109         locat = (f.FUlcfd & 0xFF00) | f.FUoffset;
3110         data[i+0] = cast(char)(locat >> 8);
3111         data[i+1] = cast(char)locat;
3112         data[i+2] = fd = cast(ubyte)f.FUlcfd;
3113         k = i;
3114         i += 3 + insidx(&data[i+3],f.FUframedatum);
3115         //printf("FUframedatum = x%x\n", f.FUframedatum);
3116         if ((fd >> 4) == (fd & 3) && f.FUframedatum == f.FUtargetdatum)
3117         {
3118             data[k + 2] = (fd & 15) | FD_F5;
3119         }
3120         else
3121         {   i += insidx(&data[i],f.FUtargetdatum);
3122             //printf("FUtargetdatum = x%x\n", f.FUtargetdatum);
3123         }
3124         //printf("[%d]: %02x %02x %02x\n", k, data[k + 0] & 0xFF, data[k + 1] & 0xFF, data[k + 2] & 0xFF);
3125         fn = f.FUnext;
3126         free(f);
3127   }
3128   assert(i <= data.sizeof);
3129   if (i)
3130       objrecord(obj.mfixupp,data.ptr,i);
3131 }
3132 else   // store in multiple records
3133 {
3134   for (; fl; fl = list_next(fl))
3135   {
3136         char[7] data = void;
3137 
3138         assert(f.FUoffset < 1024);
3139         locat = (f.FUlcfd & 0xFF00) | f.FUoffset;
3140         data[0] = locat >> 8;
3141         data[1] = locat;
3142         data[2] = f.FUlcfd;
3143         i = 3 + insidx(&data[3],f.FUframedatum);
3144         i += insidx(&data[i],f.FUtargetdatum);
3145         objrecord(obj.mfixupp,data,i);
3146   }
3147 }
3148 }
3149 
3150 
3151 /***************************
3152  * Add a new fixup to the fixup list.
3153  * Write things out if we overflow the list.
3154  */
3155 
3156 private void addfixup(Ledatarec *lr, targ_size_t offset,uint lcfd,
3157         uint framedatum,uint targetdatum)
3158 {   FIXUP *f;
3159 
3160     assert(offset < 0x1024);
3161 debug
3162 {
3163     assert(targetdatum <= 0x7FFF);
3164     assert(framedatum <= 0x7FFF);
3165 }
3166     f = cast(FIXUP *) malloc(FIXUP.sizeof);
3167     //printf("f = %p, offset = x%x\n",f,offset);
3168     f.FUoffset = offset;
3169     f.FUlcfd = cast(ushort)lcfd;
3170     f.FUframedatum = cast(ushort)framedatum;
3171     f.FUtargetdatum = cast(ushort)targetdatum;
3172     f.FUnext = lr.fixuplist;  // link f into list
3173     lr.fixuplist = f;
3174     debug
3175     obj.fixup_count++;                  // gather statistics
3176 }
3177 
3178 
3179 /*********************************
3180  * Open up a new ledata record.
3181  * Input:
3182  *      seg     segment number data is in
3183  *      offset  starting offset of start of data for this record
3184  */
3185 
3186 private Ledatarec *ledata_new(int seg,targ_size_t offset)
3187 {
3188 
3189     //printf("ledata_new(seg = %d, offset = x%lx)\n",seg,offset);
3190     assert(seg > 0 && seg < SegData.length);
3191 
3192     Ledatarec** p = obj.ledatas.push();
3193     Ledatarec* lr = *p;
3194     if (!lr)
3195     {
3196         lr = cast(Ledatarec *) mem_malloc(Ledatarec.sizeof);
3197         *p = lr;
3198     }
3199     memset(lr, 0, Ledatarec.sizeof);
3200 
3201     lr.lseg = seg;
3202     lr.offset = offset;
3203 
3204     if (seg_is_comdat(SegData[seg].segidx) && offset)      // if continuation of an existing COMDAT
3205     {
3206         Ledatarec *d = cast(Ledatarec*)SegData[seg].ledata;
3207         if (d)
3208         {
3209             if (d.lseg == seg)                 // found existing COMDAT
3210             {   lr.flags = d.flags;
3211                 lr.alloctyp = d.alloctyp;
3212                 lr._align = d._align;
3213                 lr.typidx = d.typidx;
3214                 lr.pubbase = d.pubbase;
3215                 lr.pubnamidx = d.pubnamidx;
3216             }
3217         }
3218     }
3219     SegData[seg].ledata = lr;
3220     return lr;
3221 }
3222 
3223 /***********************************
3224  * Append byte to segment.
3225  */
3226 
3227 void OmfObj_write_byte(seg_data *pseg, uint _byte)
3228 {
3229     OmfObj_byte(pseg.SDseg, pseg.SDoffset, _byte);
3230     pseg.SDoffset++;
3231 }
3232 
3233 /************************************
3234  * Output byte to object file.
3235  */
3236 
3237 void OmfObj_byte(int seg,targ_size_t offset,uint _byte)
3238 {
3239     Ledatarec *lr = cast(Ledatarec*)SegData[seg].ledata;
3240     if (!lr)
3241         goto L2;
3242 
3243     if (
3244          lr.i > LEDATAMAX - 1 ||       // if it'll overflow
3245          offset < lr.offset || // underflow
3246          offset > lr.offset + lr.i
3247      )
3248     {
3249         // Try to find an existing ledata
3250         for (size_t i = obj.ledatas.length; i; )
3251         {   Ledatarec *d = obj.ledatas[--i];
3252             if (seg == d.lseg &&       // segments match
3253                 offset >= d.offset &&
3254                 offset + 1 <= d.offset + LEDATAMAX &&
3255                 offset <= d.offset + d.i
3256                )
3257             {
3258                 lr = d;
3259                 SegData[seg].ledata = cast(void*)d;
3260                 goto L1;
3261             }
3262         }
3263 L2:
3264         lr = ledata_new(seg,offset);
3265 L1:     { }
3266     }
3267 
3268     uint i = cast(uint)(offset - lr.offset);
3269     if (lr.i <= i)
3270         lr.i = i + 1;
3271     lr.data[i] = cast(ubyte)_byte;           // 1st byte of data
3272 }
3273 
3274 /***********************************
3275  * Append bytes to segment.
3276  */
3277 
3278 void OmfObj_write_bytes(seg_data *pseg, uint nbytes, void *p)
3279 {
3280     OmfObj_bytes(pseg.SDseg, pseg.SDoffset, nbytes, p);
3281     pseg.SDoffset += nbytes;
3282 }
3283 
3284 /************************************
3285  * Output bytes to object file.
3286  * Returns:
3287  *      nbytes
3288  */
3289 
3290 uint OmfObj_bytes(int seg, targ_size_t offset, uint nbytes, void* p)
3291 {
3292     uint n = nbytes;
3293 
3294     //dbg_printf("OmfObj_bytes(seg=%d, offset=x%lx, nbytes=x%x, p=%p)\n",seg,offset,nbytes,p);
3295     Ledatarec *lr = cast(Ledatarec*)SegData[seg].ledata;
3296     if (!lr)
3297         lr = ledata_new(seg, offset);
3298  L1:
3299     if (
3300          lr.i + nbytes > LEDATAMAX ||  // or it'll overflow
3301          offset < lr.offset ||         // underflow
3302          offset > lr.offset + lr.i
3303      )
3304     {
3305         while (nbytes)
3306         {
3307             OmfObj_byte(seg, offset, *cast(char*)p);
3308             offset++;
3309             p = (cast(char *)p) + 1;
3310             nbytes--;
3311             lr = cast(Ledatarec*)SegData[seg].ledata;
3312             if (lr.i + nbytes <= LEDATAMAX)
3313                 goto L1;
3314             if (lr.i == LEDATAMAX)
3315             {
3316                 while (nbytes > LEDATAMAX)  // start writing full ledatas
3317                 {
3318                     lr = ledata_new(seg, offset);
3319                     memcpy(lr.data.ptr, p, LEDATAMAX);
3320                     p = (cast(char *)p) + LEDATAMAX;
3321                     nbytes -= LEDATAMAX;
3322                     offset += LEDATAMAX;
3323                     lr.i = LEDATAMAX;
3324                 }
3325                 goto L1;
3326             }
3327         }
3328     }
3329     else
3330     {
3331         uint i = cast(uint)(offset - lr.offset);
3332         if (lr.i < i + nbytes)
3333             lr.i = i + nbytes;
3334         memcpy(lr.data.ptr + i,p,nbytes);
3335     }
3336     return n;
3337 }
3338 
3339 /************************************
3340  * Output word of data. (Two words if segment:offset pair.)
3341  * Input:
3342  *      seg     CODE, DATA, CDATA, UDATA
3343  *      offset  offset of start of data
3344  *      data    word of data
3345  *      lcfd    LCxxxx | FDxxxx
3346  *      if (FD_F2 | FD_T6)
3347  *              idx1 = external Symbol #
3348  *      else
3349  *              idx1 = frame datum
3350  *              idx2 = target datum
3351  */
3352 
3353 void OmfObj_ledata(int seg,targ_size_t offset,targ_size_t data,
3354         uint lcfd,uint idx1,uint idx2)
3355 {
3356     uint size;                      // number of bytes to output
3357 
3358     uint ptrsize = tysize(TYfptr);
3359 
3360     if ((lcfd & LOCxx) == obj.LOCpointer)
3361         size = ptrsize;
3362     else if ((lcfd & LOCxx) == LOCbase)
3363         size = 2;
3364     else
3365         size = tysize(TYnptr);
3366 
3367     Ledatarec *lr = cast(Ledatarec*)SegData[seg].ledata;
3368     if (!lr)
3369          lr = ledata_new(seg, offset);
3370     assert(seg == lr.lseg);
3371     if (
3372          lr.i + size > LEDATAMAX ||    // if it'll overflow
3373          offset < lr.offset || // underflow
3374          offset > lr.offset + lr.i
3375      )
3376     {
3377         // Try to find an existing ledata
3378 //dbg_printf("seg = %d, offset = x%lx, size = %d\n",seg,offset,size);
3379         for (size_t i = obj.ledatas.length; i; )
3380         {   Ledatarec *d = obj.ledatas[--i];
3381 
3382 //dbg_printf("d: seg = %d, offset = x%lx, i = x%x\n",d.lseg,d.offset,d.i);
3383             if (seg == d.lseg &&       // segments match
3384                 offset >= d.offset &&
3385                 offset + size <= d.offset + LEDATAMAX &&
3386                 offset <= d.offset + d.i
3387                )
3388             {
3389 //dbg_printf("match\n");
3390                 lr = d;
3391                 SegData[seg].ledata = cast(void*)d;
3392                 goto L1;
3393             }
3394         }
3395         lr = ledata_new(seg,offset);
3396 L1:     { }
3397     }
3398 
3399     uint i = cast(uint)(offset - lr.offset);
3400     if (lr.i < i + size)
3401         lr.i = i + size;
3402     if (size == 2 || !I32)
3403         TOWORD(lr.data.ptr + i,cast(uint)data);
3404     else
3405         TOLONG(lr.data.ptr + i,cast(uint)data);
3406     if (size == ptrsize)         // if doing a seg:offset pair
3407         TOWORD(lr.data.ptr + i + tysize(TYnptr),0);        // segment portion
3408     addfixup(lr, offset - lr.offset,lcfd,idx1,idx2);
3409 }
3410 
3411 /************************************
3412  * Output long word of data.
3413  * Input:
3414  *      seg     CODE, DATA, CDATA, UDATA
3415  *      offset  offset of start of data
3416  *      data    long word of data
3417  *   Present only if size == 2:
3418  *      lcfd    LCxxxx | FDxxxx
3419  *      if (FD_F2 | FD_T6)
3420  *              idx1 = external Symbol #
3421  *      else
3422  *              idx1 = frame datum
3423  *              idx2 = target datum
3424  */
3425 
3426 void OmfObj_write_long(int seg,targ_size_t offset,uint data,
3427         uint lcfd,uint idx1,uint idx2)
3428 {
3429     uint sz = tysize(TYfptr);
3430     Ledatarec *lr = cast(Ledatarec*)SegData[seg].ledata;
3431     if (!lr)
3432          lr = ledata_new(seg, offset);
3433     if (
3434          lr.i + sz > LEDATAMAX || // if it'll overflow
3435          offset < lr.offset || // underflow
3436          offset > lr.offset + lr.i
3437        )
3438         lr = ledata_new(seg,offset);
3439     uint i = cast(uint)(offset - lr.offset);
3440     if (lr.i < i + sz)
3441         lr.i = i + sz;
3442     TOLONG(lr.data.ptr + i,data);
3443     if (I32)                              // if 6 byte far pointers
3444         TOWORD(lr.data.ptr + i + LONGSIZE,0);              // fill out seg
3445     addfixup(lr, offset - lr.offset,lcfd,idx1,idx2);
3446 }
3447 
3448 /*******************************
3449  * Refer to address that is in the data segment.
3450  * Input:
3451  *      seg =           where the address is going
3452  *      offset =        offset within seg
3453  *      val =           displacement from address
3454  *      targetdatum =   DATA, CDATA or UDATA, depending where the address is
3455  *      flags =         CFoff, CFseg
3456  * Example:
3457  *      int *abc = &def[3];
3458  *      to allocate storage:
3459  *              OmfObj_reftodatseg(DATA,offset,3 * (int *).sizeof,UDATA);
3460  */
3461 
3462 void OmfObj_reftodatseg(int seg,targ_size_t offset,targ_size_t val,
3463         uint targetdatum,int flags)
3464 {
3465     assert(flags);
3466 
3467     if (flags == 0 || flags & CFoff)
3468     {
3469         // The frame datum is always 1, which is DGROUP
3470         OmfObj_ledata(seg,offset,val,
3471             LOCATsegrel | obj.LOCoffset | FD_F1 | FD_T4,DGROUPIDX,SegData[targetdatum].segidx);
3472         offset += _tysize[TYint];
3473     }
3474 
3475     if (flags & CFseg)
3476     {
3477 static if (0)
3478 {
3479         if (config.wflags & WFdsnedgroup)
3480             warerr(WM_ds_ne_dgroup);
3481 }
3482         OmfObj_ledata(seg,offset,0,
3483             LOCATsegrel | LOCbase | FD_F1 | FD_T5,DGROUPIDX,DGROUPIDX);
3484     }
3485 }
3486 
3487 /*******************************
3488  * Refer to address that is in a far segment.
3489  * Input:
3490  *      seg =           where the address is going
3491  *      offset =        offset within seg
3492  *      val =           displacement from address
3493  *      farseg =        far segment index
3494  *      flags =         CFoff, CFseg
3495  */
3496 
3497 void OmfObj_reftofarseg(int seg,targ_size_t offset,targ_size_t val,
3498         int farseg,int flags)
3499 {
3500     assert(flags);
3501 
3502     int idx = SegData[farseg].segidx;
3503     if (flags == 0 || flags & CFoff)
3504     {
3505         OmfObj_ledata(seg,offset,val,
3506             LOCATsegrel | obj.LOCoffset | FD_F0 | FD_T4,idx,idx);
3507         offset += _tysize[TYint];
3508     }
3509 
3510     if (flags & CFseg)
3511     {
3512         OmfObj_ledata(seg,offset,0,
3513             LOCATsegrel | LOCbase | FD_F0 | FD_T4,idx,idx);
3514     }
3515 }
3516 
3517 /*******************************
3518  * Refer to address that is in the code segment.
3519  * Only offsets are output, regardless of the memory model.
3520  * Used to put values in switch address tables.
3521  * Input:
3522  *      seg =           where the address is going (CODE or DATA)
3523  *      offset =        offset within seg
3524  *      val =           displacement from start of this module
3525  */
3526 
3527 void OmfObj_reftocodeseg(int seg,targ_size_t offset,targ_size_t val)
3528 {
3529     uint framedatum;
3530     uint lcfd;
3531 
3532     int idx = SegData[cseg].segidx;
3533     if (seg_is_comdat(idx))             // if comdat
3534     {   idx = -idx;
3535         framedatum = idx;
3536         lcfd = (LOCATsegrel | obj.LOCoffset) | (FD_F2 | FD_T6);
3537     }
3538     else if (config.exe & EX_flat)
3539     {   framedatum = 1;
3540         lcfd = (LOCATsegrel | obj.LOCoffset) | (FD_F1 | FD_T4);
3541     }
3542     else
3543     {   framedatum = idx;
3544         lcfd = (LOCATsegrel | obj.LOCoffset) | (FD_F0 | FD_T4);
3545     }
3546 
3547     OmfObj_ledata(seg,offset,val,lcfd,framedatum,idx);
3548 }
3549 
3550 /*******************************
3551  * Refer to an identifier.
3552  * Input:
3553  *      seg =           where the address is going (CODE or DATA)
3554  *      offset =        offset within seg
3555  *      s .            Symbol table entry for identifier
3556  *      val =           displacement from identifier
3557  *      flags =         CFselfrel: self-relative
3558  *                      CFseg: get segment
3559  *                      CFoff: get offset
3560  * Returns:
3561  *      number of bytes in reference (2 or 4)
3562  * Example:
3563  *      extern int def[];
3564  *      int *abc = &def[3];
3565  *      to allocate storage:
3566  *              OmfObj_reftodatseg(DATA,offset,3 * (int *).sizeof,UDATA);
3567  */
3568 
3569 int OmfObj_reftoident(int seg,targ_size_t offset,Symbol *s,targ_size_t val,
3570         int flags)
3571 {
3572     uint targetdatum;       // which datum the symbol is in
3573     uint framedatum;
3574     int     lc;
3575     int     external;           // !=0 if identifier is defined externally
3576     int numbytes;
3577     tym_t ty;
3578 
3579 static if (0)
3580 {
3581     printf("OmfObj_reftoident('%s' seg %d, offset x%lx, val x%lx, flags x%x)\n",
3582         s.Sident.ptr,seg,offset,val,flags);
3583     printf("Sseg = %d, Sxtrnnum = %d\n",s.Sseg,s.Sxtrnnum);
3584     symbol_print(s);
3585 }
3586     assert(seg > 0);
3587 
3588     ty = s.ty();
3589     while (1)
3590     {
3591         switch (flags & (CFseg | CFoff))
3592         {
3593             case 0:
3594                 // Select default
3595                 flags |= CFoff;
3596                 if (tyfunc(ty))
3597                 {
3598                     if (tyfarfunc(ty))
3599                         flags |= CFseg;
3600                 }
3601                 else // DATA
3602                 {
3603                     if (LARGEDATA)
3604                         flags |= CFseg;
3605                 }
3606                 continue;
3607             case CFoff:
3608                 if (I32)
3609                 {
3610                     if (ty & mTYthread)
3611                     {   lc = LOC32tlsoffset;
3612                     }
3613                     else
3614                         lc = obj.LOCoffset;
3615                 }
3616                 else
3617                 {
3618                     // The 'loader_resolved' offset is required for VCM
3619                     // and Windows support. A fixup of this type is
3620                     // relocated by the linker to point to a 'thunk'.
3621                     lc = (tyfarfunc(ty)
3622                           && !(flags & CFselfrel))
3623                             ? LOCloader_resolved : obj.LOCoffset;
3624                 }
3625                 numbytes = tysize(TYnptr);
3626                 break;
3627             case CFseg:
3628                 lc = LOCbase;
3629                 numbytes = 2;
3630                 break;
3631             case CFoff | CFseg:
3632                 lc = obj.LOCpointer;
3633                 numbytes = tysize(TYfptr);
3634                 break;
3635 
3636             default:
3637                 assert(0);
3638         }
3639         break;
3640     }
3641 
3642     switch (s.Sclass)
3643     {
3644         case SCcomdat:
3645         case_SCcomdat:
3646         case SCextern:
3647         case SCcomdef:
3648             if (s.Sxtrnnum)            // identifier is defined somewhere else
3649             {
3650                 external = s.Sxtrnnum;
3651 
3652                 debug
3653                 if (external > obj.extidx)
3654                 {
3655                     printf("obj.extidx = %d\n", obj.extidx);
3656                     symbol_print(s);
3657                 }
3658 
3659                 assert(external <= obj.extidx);
3660             }
3661             else
3662             {
3663                 // Don't know yet, worry about it later
3664              Ladd:
3665                 size_t byteswritten = addtofixlist(s,offset,seg,val,flags);
3666                 assert(byteswritten == numbytes);
3667                 return numbytes;
3668             }
3669             break;
3670         case SCinline:
3671             if (config.flags2 & CFG2comdat)
3672                 goto case_SCcomdat;     // treat as initialized common block
3673             goto case;
3674 
3675         case SCsinline:
3676         case SCstatic:
3677         case SCglobal:
3678             if (s.Sseg == UNKNOWN)
3679                 goto Ladd;
3680             if (seg_is_comdat(SegData[s.Sseg].segidx))
3681                 goto case_SCcomdat;
3682             goto case;
3683 
3684         case SClocstat:
3685             external = 0;               // identifier is static or global
3686                                         // and we know its offset
3687             if (flags & CFoff)
3688                 val += s.Soffset;
3689             break;
3690         default:
3691             symbol_print(s);
3692             assert(0);
3693     }
3694 
3695     lc |= (flags & CFselfrel) ? LOCATselfrel : LOCATsegrel;
3696     if (external)
3697     {   lc |= FD_T6;
3698         targetdatum = external;
3699         switch (s.Sfl)
3700         {
3701             case FLextern:
3702                 if (!(ty & (mTYcs | mTYthread)))
3703                     goto L1;
3704                 goto case;
3705 
3706             case FLfunc:
3707             case FLfardata:
3708             case FLcsdata:
3709             case FLtlsdata:
3710                 if (config.exe & EX_flat)
3711                 {   lc |= FD_F1;
3712                     framedatum = 1;
3713                 }
3714                 else
3715                 {
3716             //case FLtlsdata:
3717                     lc |= FD_F2;
3718                     framedatum = targetdatum;
3719                 }
3720                 break;
3721             default:
3722                 goto L1;
3723         }
3724     }
3725     else
3726     {
3727         lc |= FD_T4;                    // target is always a segment
3728         targetdatum = SegData[s.Sseg].segidx;
3729         assert(s.Sseg != UNKNOWN);
3730         switch (s.Sfl)
3731         {
3732             case FLextern:
3733                 if (!(ty & (mTYcs | mTYthread)))
3734                     goto L1;
3735                 goto case;
3736 
3737             case FLfunc:
3738             case FLfardata:
3739             case FLcsdata:
3740             case FLtlsdata:
3741                 if (config.exe & EX_flat)
3742                 {   lc |= FD_F1;
3743                     framedatum = 1;
3744                 }
3745                 else
3746                 {
3747             //case FLtlsdata:
3748                     lc |= FD_F0;
3749                     framedatum = targetdatum;
3750                 }
3751                 break;
3752             default:
3753             L1:
3754                 lc |= FD_F1;
3755                 framedatum = DGROUPIDX;
3756                 if (flags == CFseg)
3757                 {   lc = LOCATsegrel | LOCbase | FD_F1 | FD_T5;
3758                     targetdatum = DGROUPIDX;
3759                 }
3760 static if (0)
3761 {
3762                 if (flags & CFseg && config.wflags & WFdsnedgroup)
3763                     warerr(WM_ds_ne_dgroup);
3764 }
3765                 break;
3766         }
3767     }
3768 
3769     OmfObj_ledata(seg,offset,val,lc,framedatum,targetdatum);
3770     return numbytes;
3771 }
3772 
3773 /*****************************************
3774  * Generate far16 thunk.
3775  * Input:
3776  *      s       Symbol to generate a thunk for
3777  */
3778 
3779 void OmfObj_far16thunk(Symbol *s)
3780 {
3781     static ubyte[25] cod32_1 =
3782     [
3783         0x55,                           //      PUSH    EBP
3784         0x8B,0xEC,                      //      MOV     EBP,ESP
3785         0x83,0xEC,0x04,                 //      SUB     ESP,4
3786         0x53,                           //      PUSH    EBX
3787         0x57,                           //      PUSH    EDI
3788         0x56,                           //      PUSH    ESI
3789         0x06,                           //      PUSH    ES
3790         0x8C,0xD2,                      //      MOV     DX,SS
3791         0x80,0xE2,0x03,                 //      AND     DL,3
3792         0x80,0xCA,0x07,                 //      OR      DL,7
3793         0x89,0x65,0xFC,                 //      MOV     -4[EBP],ESP
3794         0x8C,0xD0,                      //      MOV     AX,SS
3795         0x66,0x3D, // 0x00,0x00 */      /*      CMP     AX,seg FLAT:_DATA
3796     ];
3797     assert(cod32_1[cod32_1.length - 1] == 0x3D);
3798 
3799     static ubyte[22 + 46] cod32_2 =
3800     [
3801         0x0F,0x85,0x10,0x00,0x00,0x00,  //      JNE     L1
3802         0x8B,0xC4,                      //      MOV     EAX,ESP
3803         0x66,0x3D,0x00,0x08,            //      CMP     AX,2048
3804         0x0F,0x83,0x04,0x00,0x00,0x00,  //      JAE     L1
3805         0x66,0x33,0xC0,                 //      XOR     AX,AX
3806         0x94,                           //      XCHG    ESP,EAX
3807                                         // L1:
3808         0x55,                           //      PUSH    EBP
3809         0x8B,0xC4,                      //      MOV     EAX,ESP
3810         0x16,                           //      PUSH    SS
3811         0x50,                           //      PUSH    EAX
3812         LEA,0x75,0x08,                  //      LEA     ESI,8[EBP]
3813         0x81,0xEC,0x00,0x00,0x00,0x00,  //      SUB     ESP,numparam
3814         0x8B,0xFC,                      //      MOV     EDI,ESP
3815         0xB9,0x00,0x00,0x00,0x00,       //      MOV     ECX,numparam
3816         0x66,0xF3,0xA4,                 //      REP     MOVSB
3817         0x8B,0xC4,                      //      MOV     EAX,ESP
3818         0xC1,0xC8,0x10,                 //      ROR     EAX,16
3819         0x66,0xC1,0xE0,0x03,            //      SHL     AX,3
3820         0x0A,0xC2,                      //      OR      AL,DL
3821         0xC1,0xC0,0x10,                 //      ROL     EAX,16
3822         0x50,                           //      PUSH    EAX
3823         0x66,0x0F,0xB2,0x24,0x24,       //      LSS     SP,[ESP]
3824         0x66,0xEA, // 0,0,0,0, */       /*      JMPF    L3
3825     ];
3826     assert(cod32_2[cod32_2.length - 1] == 0xEA);
3827 
3828     static ubyte[26] cod32_3 =
3829     [                                   // L2:
3830         0xC1,0xE0,0x10,                 //      SHL     EAX,16
3831         0x0F,0xAC,0xD0,0x10,            //      SHRD    EAX,EDX,16
3832         0x0F,0xB7,0xE4,                 //      MOVZX   ESP,SP
3833         0x0F,0xB2,0x24,0x24,            //      LSS     ESP,[ESP]
3834         0x5D,                           //      POP     EBP
3835         0x8B,0x65,0xFC,                 //      MOV     ESP,-4[EBP]
3836         0x07,                           //      POP     ES
3837         0x5E,                           //      POP     ESI
3838         0x5F,                           //      POP     EDI
3839         0x5B,                           //      POP     EBX
3840         0xC9,                           //      LEAVE
3841         0xC2,0x00,0x00                  //      RET     numparam
3842     ];
3843     assert(cod32_3[cod32_3.length - 3] == 0xC2);
3844 
3845     uint numparam = 24;
3846     targ_size_t L2offset;
3847     int idx;
3848 
3849     s.Sclass = SCstatic;
3850     s.Sseg = cseg;             // identifier is defined in code segment
3851     s.Soffset = Offset(cseg);
3852 
3853     // Store numparam into right places
3854     assert((numparam & 0xFFFF) == numparam);    // 2 byte value
3855     TOWORD(&cod32_2[32],numparam);
3856     TOWORD(&cod32_2[32 + 7],numparam);
3857     TOWORD(&cod32_3[cod32_3.sizeof - 2],numparam);
3858 
3859     //------------------------------------------
3860     // Generate CODE16 segment if it isn't there already
3861     if (obj.code16segi == 0)
3862     {
3863         // Define CODE16 segment for far16 thunks
3864 
3865         static immutable char[8] lname = "\06CODE16";
3866 
3867         // Put out LNAMES record
3868         objrecord(LNAMES,lname.ptr,lname.sizeof - 1);
3869 
3870         obj.code16segi = obj_newfarseg(0,4);
3871         obj.CODE16offset = 0;
3872 
3873         // class CODE
3874         uint attr = SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);
3875         SegData[obj.code16segi].attr = attr;
3876         objsegdef(attr,0,obj.lnameidx++,4);
3877         obj.segidx++;
3878     }
3879 
3880     //------------------------------------------
3881     // Output the 32 bit thunk
3882 
3883     OmfObj_bytes(cseg,Offset(cseg),cod32_1.sizeof,cod32_1.ptr);
3884     Offset(cseg) += cod32_1.sizeof;
3885 
3886     // Put out fixup for SEG FLAT:_DATA
3887     OmfObj_ledata(cseg,Offset(cseg),0,LOCATsegrel|LOCbase|FD_F1|FD_T4,
3888         DGROUPIDX,DATA);
3889     Offset(cseg) += 2;
3890 
3891     OmfObj_bytes(cseg,Offset(cseg),cod32_2.sizeof,cod32_2.ptr);
3892     Offset(cseg) += cod32_2.sizeof;
3893 
3894     // Put out fixup to CODE16 part of thunk
3895     OmfObj_ledata(cseg,Offset(cseg),obj.CODE16offset,LOCATsegrel|LOC16pointer|FD_F0|FD_T4,
3896         SegData[obj.code16segi].segidx,
3897         SegData[obj.code16segi].segidx);
3898     Offset(cseg) += 4;
3899 
3900     L2offset = Offset(cseg);
3901     OmfObj_bytes(cseg,Offset(cseg),cod32_3.sizeof,cod32_3.ptr);
3902     Offset(cseg) += cod32_3.sizeof;
3903 
3904     s.Ssize = Offset(cseg) - s.Soffset;            // size of thunk
3905 
3906     //------------------------------------------
3907     // Output the 16 bit thunk
3908 
3909     OmfObj_byte(obj.code16segi,obj.CODE16offset++,0x9A);       //      CALLF   function
3910 
3911     // Make function external
3912     idx = OmfObj_external(s);                         // use Pascal name mangling
3913 
3914     // Output fixup for function
3915     OmfObj_ledata(obj.code16segi,obj.CODE16offset,0,LOCATsegrel|LOC16pointer|FD_F2|FD_T6,
3916         idx,idx);
3917     obj.CODE16offset += 4;
3918 
3919     OmfObj_bytes(obj.code16segi,obj.CODE16offset,3,cast(void*)"\x66\x67\xEA".ptr);    // JMPF L2
3920     obj.CODE16offset += 3;
3921 
3922     OmfObj_ledata(obj.code16segi,obj.CODE16offset,L2offset,
3923         LOCATsegrel | LOC32pointer | FD_F1 | FD_T4,
3924         DGROUPIDX,
3925         SegData[cseg].segidx);
3926     obj.CODE16offset += 6;
3927 
3928     SegData[obj.code16segi].SDoffset = obj.CODE16offset;
3929 }
3930 
3931 /**************************************
3932  * Mark object file as using floating point.
3933  */
3934 
3935 void OmfObj_fltused()
3936 {
3937     if (!obj.fltused)
3938     {
3939         obj.fltused = 1;
3940         if (!(config.flags3 & CFG3wkfloat))
3941             OmfObj_external_def("__fltused");
3942     }
3943 }
3944 
3945 Symbol *OmfObj_tlv_bootstrap()
3946 {
3947     // specific for Mach-O
3948     assert(0);
3949 }
3950 
3951 void OmfObj_gotref(Symbol *s)
3952 {
3953 }
3954 
3955 /*****************************************
3956  * write a reference to a mutable pointer into the object file
3957  * Params:
3958  *      s    = symbol that contains the pointer
3959  *      soff = offset of the pointer inside the Symbol's memory
3960  */
3961 
3962 void OmfObj_write_pointerRef(Symbol* s, uint soff)
3963 {
3964 version (MARS)
3965 {
3966     // defer writing pointer references until the symbols are written out
3967     obj.ptrrefs.push(PtrRef(s, soff));
3968 }
3969 }
3970 
3971 /*****************************************
3972  * flush a single pointer reference saved by write_pointerRef
3973  * to the object file
3974  * Params:
3975  *      s    = symbol that contains the pointer
3976  *      soff = offset of the pointer inside the Symbol's memory
3977  */
3978 private void objflush_pointerRef(Symbol* s, uint soff)
3979 {
3980 version (MARS)
3981 {
3982     bool isTls = (s.Sfl == FLtlsdata);
3983     int* segi = isTls ? &obj.tlsrefsegi : &obj.datrefsegi;
3984     symbol_debug(s);
3985 
3986     if (*segi == 0)
3987     {
3988         // We need to always put out the segments in triples, so that the
3989         // linker will put them in the correct order.
3990         static immutable char[12] lnames_dat = "\03DPB\02DP\03DPE";
3991         static immutable char[12] lnames_tls = "\03TPB\02TP\03TPE";
3992         const lnames = isTls ? lnames_tls.ptr : lnames_dat.ptr;
3993         // Put out LNAMES record
3994         objrecord(LNAMES,lnames,lnames_dat.sizeof - 1);
3995 
3996         int dsegattr = obj.csegattr;
3997 
3998         // Put out beginning segment
3999         objsegdef(dsegattr,0,obj.lnameidx,CODECLASS);
4000         obj.lnameidx++;
4001         obj.segidx++;
4002 
4003         // Put out segment definition record
4004         *segi = obj_newfarseg(0,CODECLASS);
4005         objsegdef(dsegattr,0,obj.lnameidx,CODECLASS);
4006         SegData[*segi].attr = dsegattr;
4007         assert(SegData[*segi].segidx == obj.segidx);
4008 
4009         // Put out ending segment
4010         objsegdef(dsegattr,0,obj.lnameidx + 1,CODECLASS);
4011 
4012         obj.lnameidx += 2;              // for next time
4013         obj.segidx += 2;
4014     }
4015 
4016     targ_size_t offset = SegData[*segi].SDoffset;
4017     offset += objmod.reftoident(*segi, offset, s, soff, CFoff);
4018     SegData[*segi].SDoffset = offset;
4019 }
4020 }
4021 
4022 /*****************************************
4023  * flush all pointer references saved by write_pointerRef
4024  * to the object file
4025  */
4026 private void objflush_pointerRefs()
4027 {
4028 version (MARS)
4029 {
4030     foreach (ref pr; obj.ptrrefs)
4031         objflush_pointerRef(pr.sym, pr.offset);
4032     obj.ptrrefs.reset();
4033 }
4034 }
4035 
4036 }
4037 
4038 }