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