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