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