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