File: cof2omf.cpp

package info (click to toggle)
objconv 2.56%2Bds-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,300 kB
  • sloc: cpp: 27,039; makefile: 4; sh: 2
file content (803 lines) | stat: -rw-r--r-- 37,276 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
/****************************  cof2omf.cpp   ********************************
* Author:        Agner Fog
* Date created:  2007-02-03
* Last modified: 2007-02-03
* Project:       objconv
* Module:        cof2omf.cpp
* Description:
* Module for converting 32 bit PE/COFF file to OMF file
*
* Copyright 2007-2008 GNU General Public License http://www.gnu.org/licenses
*****************************************************************************/
#include "stdafx.h"

CCOF2OMF::CCOF2OMF () {
   // Constructor
   memset(this, 0, sizeof(*this));
}


void CCOF2OMF::Convert() {
   // Do the conversion
   if (WordSize != 32) {
      err.submit(2317, WordSize);                // Wrong word size
      return;
   }

   // Allocate variable size buffers
   SectionBuffer.SetNum(NSections + 2);          // Allocate buffer for section translation list
   SectionBuffer.SetZero();                      // Initialize
   SymbolBuffer.SetNum(NumberOfSymbols + 1);     // Allocate buffer for symbol translation list
   SymbolBuffer.SetZero();                       // Initialize
   NameBuffer.Push(0, 1);                        // Make first entry in NameBuffer empty

   // Call the subfunctions
   ToFile.SetFileType(FILETYPE_OMF);             // Set type of output file
   MakeSegmentList();                            // Make temporary segment conversion list
   MakeSymbolList();                             // Make temporary symbol conversion list
   MakeRelocationsList();                        // Make temporary list of relocations (fixups) and sort it
   MakeLNAMES();                                 // Make THEADR and LNAMES records
   MakeSEGDEF();                                 // Make SEGDEF and GRPDEF records
   MakeEXTDEF();                                 // Make EXTDEF records
   MakePUBDEF();                                 // Make PUBDEF records
   MakeLEDATA();                                 // Make LEDATA, LIDATA and FIXUPP records
   MakeMODEND();                                 // Finish output file
   *this << ToFile;                              // Take over new file buffer containing the converted file
}


void CCOF2OMF::MakeSegmentList() {
   // Make temporary segment conversion list
   const char * oldname;                         // Old name of section
   uint32_t namei;                                 // Name index into NameBuffer
   uint32_t align;                                 // Segment alignment = 2^align
   int32_t align2;                                 // align2 = 2^align
   uint32_t flags;                                 // Old flags
   int i, j;                                     // Loop counters
   int oldsec;                                   // Old section number

   // Loop through old sections
   for (j = 0; j < NSections; j++) {
      // Old section number
      oldsec = j + 1;

      // Get old section header
      SCOFF_SectionHeader * pSectionHeader = &SectionHeaders[j];

      // Get name
      oldname = GetSectionName(pSectionHeader->Name);

      // Check for debug sections
      if (strnicmp(oldname,"debug",5) == 0 || strnicmp(oldname+1,"debug",5) == 0) {
         // This is a debug section
         if (cmd.DebugInfo == CMDL_DEBUG_STRIP) {
            // Remove debug info
            SectionBuffer[oldsec].NewNumber = 0;
            cmd.CountDebugRemoved();
            continue;
         }
         else if (cmd.InputType != cmd.OutputType) {
            err.submit(1029); // Warn that debug information is incompatible
         }
      }

      // Check for directive sections
      if (strnicmp(oldname,".drectve",8) == 0 || (pSectionHeader->Flags & (PE_SCN_LNK_INFO | PE_SCN_LNK_REMOVE))) {
         // This is a directive section
         if (cmd.ExeptionInfo) {
            // Remove directive section
            SectionBuffer[oldsec].NewNumber = 0;
            cmd.CountExceptionRemoved();
            continue;
         }
      }

      // Get alignment
      align = (pSectionHeader->Flags & PE_SCN_ALIGN_MASK) / PE_SCN_ALIGN_1;
      if (align > 0) align--;                 // Alignment = 2^align
      align2 = 1 << align;                    // 2^align

      // Check for previous sections with same name
      for (i = 0; i < SectionBufferNum; i++) {
         if (strcmp(oldname, (char*)NameBuffer.Buf() + SectionBuffer[i].OldName) == 0) break; // Found same name
      }
      if (i < SectionBufferNum) {
         // Previous section with same name found.
         // i = first section with this name, oldsec = current section with this name
         SectionBuffer[oldsec] = SectionBuffer[i];    // Copy record
         SectionBuffer[oldsec].NewNameI = 0;          // Indicate this is not the first record

         // Check if alignment is the same
         if (align != SectionBuffer[i].Align) {
            err.submit(1060, oldname);           // Warning different alignments
            if (align > SectionBuffer[i].Align) SectionBuffer[i].Align = align; // Use highest alignment
         }

         // Get section header
         SectionBuffer[oldsec].psechdr = pSectionHeader;

         // Get size of this section
         SectionBuffer[oldsec].Size = pSectionHeader->SizeOfRawData;

         // Get offset relative to first section with same name
         SectionBuffer[oldsec].Offset = SectionBuffer[i].Offset + SectionBuffer[i].SegmentSize;

         // Align this section (We are aligning each section in the segment)
         SectionBuffer[oldsec].Offset = (SectionBuffer[oldsec].Offset + align2 - 1) & (- align2);

         // Update total size of all sections with same name
         SectionBuffer[i].SegmentSize = SectionBuffer[oldsec].Offset + SectionBuffer[oldsec].Size;
      }
      else {
         // No previous section found with same name. Make SOMFSegmentList record
         SectionBufferNum = oldsec + 1;                  // End of entries in SectionBuffer

         // Assign a number to this segment
         SectionBuffer[oldsec].NewNumber = ++NumSegments;
         SectionBuffer[oldsec].NewNameI  = NumSegments + OMF_LNAME_LAST;

         // Point to old section header
         SectionBuffer[oldsec].psechdr = pSectionHeader;

         // Give it a name
         namei = NameBuffer.PushString(oldname);      // Save name in buffer, because it is volatile
         SectionBuffer[oldsec].OldName = namei;            // Index to name
         // Segment names like .text and _TEXT are both common. No need to convert the name
         // Only restriction is length < 256.
         // Do we need a unique segment name if the alignment is different from segments
         // with same name in another module?
         if (strlen(oldname) > 255) {
            // Segment name too long. This is very unlikely
            namei = NameBuffer.Push(oldname, 255);    // Make truncated name
            NameBuffer.Push(0, 1);                    // Terminate by zero
         }
         SectionBuffer[oldsec].NewName = namei;            // Index to name

         // Size
         SectionBuffer[oldsec].Size = pSectionHeader->SizeOfRawData;
         SectionBuffer[oldsec].SegmentSize = pSectionHeader->SizeOfRawData;
         SectionBuffer[oldsec].Offset = 0;

         // Alignment
         SectionBuffer[oldsec].Align = align;

         // Segment type
         flags = pSectionHeader->Flags;
      
         // Get segment class
         if (flags & (PE_SCN_CNT_CODE | PE_SCN_MEM_EXECUTE)) {
            // Code segment
            SectionBuffer[oldsec].Class = OMF_LNAME_CODE;
         }
         else if (flags & PE_SCN_CNT_UNINIT_DATA) {
            // Uninitialized data
            SectionBuffer[oldsec].Class = OMF_LNAME_BSS;
         }
         else if (!(flags & PE_SCN_MEM_WRITE)) {
            // Read only
            SectionBuffer[oldsec].Class = OMF_LNAME_CONST;
         }
         else {
            // Normal data
            SectionBuffer[oldsec].Class = OMF_LNAME_DATA;
         }
      }
   }

   // Add 1 to section count because new section numbers are 1-based
   SectionBufferNum = NSections + 1;
}


void CCOF2OMF::MakeSymbolList() {
   // Make temporary symbol conversion list
   int isym = 0;  // current symbol table entry
   //int jsym = 0;  // auxiliary entry number
   union {        // Pointer to symbol table
      SCOFF_SymTableEntry * p;  // Normal pointer
      int8_t * b;                 // Used for address calculation
   } Symtab;

   Symtab.p = SymbolTable;      // Set pointer to begin of SymbolTable

   // Loop through symbol table
   while (isym < NumberOfSymbols) {
      // Check scope
      if (Symtab.p->s.StorageClass == COFF_CLASS_EXTERNAL) {
         // Scope is public or external

         if (Symtab.p->s.SectionNumber > 0) {

            // Symbol is public
            SymbolBuffer[isym].Scope = S_PUBLIC;               // Scope = public
            SymbolBuffer[isym].NewIndex = ++NumPublicSymbols;  // Public symbol number
            // Get name
            SymbolBuffer[isym].Name = NameBuffer.PushString(GetSymbolName(Symtab.p->s.Name));

            // Find section in SectionBuffer
            uint32_t OldSection = Symtab.p->s.SectionNumber;
            SymbolBuffer[isym].Segment = SectionBuffer[OldSection].NewNumber; // New segment number

            // Calculate offset = offset into old section + offset of old section to first section with same name
            SymbolBuffer[isym].Offset = Symtab.p->s.Value + SectionBuffer[OldSection].Offset;
         }
         else if (Symtab.p->s.SectionNumber == 0) {

            // Symbol is external
            SymbolBuffer[isym].Scope = S_EXTERNAL;        // Scope = external
            SymbolBuffer[isym].NewIndex = ++NumExternalSymbols;  // External symbol number
            SymbolBuffer[isym].Name = NameBuffer.PushString(GetSymbolName(Symtab.p->s.Name));
         }
         else if (Symtab.p->s.SectionNumber == COFF_SECTION_ABSOLUTE) {

            // Symbol is public, absolute
            SymbolBuffer[isym].Scope = S_PUBLIC;        // Scope = public
            SymbolBuffer[isym].NewIndex = ++NumPublicSymbols;  // Public symbol number
            // Get name
            SymbolBuffer[isym].Name = NameBuffer.PushString(GetSymbolName(Symtab.p->s.Name));

            SymbolBuffer[isym].Segment = 0;          // 0 indicates absolute

            SymbolBuffer[isym].Offset = Symtab.p->s.Value;   // Store value in Offset
         }
         else {
            // COFF_SECTION_DEBUG, COFF_SECTION_N_TV, COFF_SECTION_P_TV
            // Ignore
         }
      }

      // Skip auxiliary symbol table entries and increment pointer
      isym += Symtab.p->s.NumAuxSymbols + 1;
      Symtab.b += (Symtab.p->s.NumAuxSymbols + 1) * SIZE_SCOFF_SymTableEntry;
   }
}


void CCOF2OMF::MakeRelocationsList() {
   // Make temporary list of relocations (fixups) and sort it
   uint32_t i;                                     // Relocation number in old file
   int j;                                        // Section number of relocation source in old file
   int isym;                                     // Symbol table index in old file
   //int32_t * paddend = 0;                          // Pointer to inline addend
   uint32_t TargetOldSection;                      // Section number of relocation target in old file

   SOMFRelocation NewRel;                        // Entry in RelocationBuffer

   union {                                       // Pointer to symbol table
      SCOFF_SymTableEntry * p;                   // Normal pointer
      int8_t * b;                                  // Used for address calculation
   } Symtab;

   union {                                       // Pointer to relocation entry
      SCOFF_Relocation * p;                      // pointer to record
      int8_t * b;                                  // used for address calculation and incrementing
   } Reloc;

   // Loop through section headers of old file
   for (j = 0; j < NSections; j++) {
      SCOFF_SectionHeader * SectionHeaderp = &SectionHeaders[j];

      // Pointer to first relocation entry in section
      Reloc.b = Buf() + SectionHeaderp->PRelocations;

      // Loop through relocations in section
      for (i = 0; i < SectionHeaderp->NRelocations; i++) {

         // Find symbol table entry
         isym = Reloc.p->SymbolTableIndex;
         if ((uint32_t)isym >= (uint32_t)NumberOfSymbols) {
            err.submit(2040);                    // SymbolTableIndex points outside Symbol Table
            isym = 0;
         }
         Symtab.p = SymbolTable;                 // Set pointer to begin of SymbolTable
         Symtab.b += SIZE_SCOFF_SymTableEntry * isym; // Calculate address of entry isym

         // Find inline addend
         if (Reloc.p->Type < COFF32_RELOC_SEG12) {
            //paddend = (int32_t*)(Buf()+SectionHeaderp->PRawData+Reloc.p->VirtualAddress);
         }
         //else paddend = 0;

         // Make entry in RelocationBuffer
         // Relocation type
         switch (Reloc.p->Type) {
         case COFF32_RELOC_DIR32:                // Direct 32 bit
            NewRel.Mode = 1;  break;             // 0 = EIP-relative, 1 = direct

         case COFF32_RELOC_REL32:                // 32 bit EIP relative
            NewRel.Mode = 0;  break;             // 0 = EIP-relative, 1 = direct

         case COFF32_RELOC_ABS:                  // Ignore
            continue;

         default:                                // Other. Not supported
            NewRel.Mode = -1;                    // -1 = unsupported. 
            // Postpone error message in case it refers to a debug section that is being removed
            NewRel.TargetOffset = Reloc.p->Type; // Remember relocation type
            //err.submit(2030, Reloc.p->Type); continue;  // Unsupported relocation type
            break;
         }
         // Get source
         NewRel.Section = j + 1;                 // Section number in old file
         NewRel.SourceOffset = Reloc.p->VirtualAddress;// Offset of source relative to section

         // Get target
         if (Symtab.p->s.SectionNumber > 0) {
            // Local
            NewRel.Scope  = S_LOCAL;                   // 0 = local, 2 = external
            TargetOldSection = Symtab.p->s.SectionNumber;    // Target section
            if (TargetOldSection > uint32_t(NSections)) {
               // SectionNumber out of range
               err.submit(2035);  continue;
            }
            // Segment index of target in new file:
            NewRel.TargetSegment = SectionBuffer[TargetOldSection].NewNumber; 
            // Offset relative to old section
            NewRel.TargetOffset = Symtab.p->s.Value;          
            // Add offset relative to first section with same name to get offset relative to new segment
            NewRel.TargetOffset += SectionBuffer[TargetOldSection].Offset; 
         }
         else {
            // External
            NewRel.Scope  = S_EXTERNAL;               // 0 = local, 2 = external
            NewRel.TargetOffset = 0;                  // Any addend is inline    
            // Find EXTDEF index in SymbolBuffer
            NewRel.TargetSegment = SymbolBuffer[isym].NewIndex;
         }

         // Put NewRel into RelocationBuffer
         RelocationBuffer.Push(NewRel);

         // Increment pointer to relocation record
         Reloc.b += SIZE_SCOFF_Relocation;       // Next relocation record
      }
   }
   // Sort RelocationBuffer
   RelocationBuffer.Sort();

   // Store number of relocations
   NumRelocations = RelocationBuffer.GetNumEntries();

   // Check for overlapping relocation sources
   for (uint32_t i = 1; i < RelocationBuffer.GetNumEntries(); i++) {
      if (RelocationBuffer[i].Section == RelocationBuffer[i-1].Section
      && RelocationBuffer[i].SourceOffset >= RelocationBuffer[i-1].SourceOffset
      && RelocationBuffer[i].SourceOffset <  RelocationBuffer[i-1].SourceOffset + 4
      && (RelocationBuffer[i].Mode == 0 || RelocationBuffer[i].Mode == 1)) {
         err.submit(2210);                       // Error: overlapping relocation sources
      }
   }
}


void CCOF2OMF::MakeLNAMES() {
   // Make THEADR and LNAMES records
   int Sec;                                      // Loop counter
   uint32_t NameI;                                 // Name index

   // Make first record in output file = Translator header
   ToFile.StartRecord(OMF_THEADR);
   // Remove path from file name and limit length
   char * ShortName = CLibrary::ShortenMemberName(OutputFileName);   
   ToFile.PutString(ShortName);
   ToFile.EndRecord();

   // Make LNAMES record containing names of segments, groups and classes
   ToFile.StartRecord(OMF_LNAMES);

   // Store default group and class names
   ToFile.PutString("FLAT");                     // 1: FLAT  = group name
   ToFile.PutString("CODE");                     // 2: CODE  = class name for code
   ToFile.PutString("DATA");                     // 3: DATA  = class name for data segment
   ToFile.PutString("BSS");                      // 4: BSS   = class name for uninitialized data
   ToFile.PutString("CONST");                    // 5: CONST = class name for readonly data

   NameI = OMF_LNAME_LAST + 1;                   // Index of next name

   // Get segment names
   for (Sec = 0; Sec < SectionBufferNum; Sec++) {
      if (SectionBuffer[Sec].NewNameI == NameI) {
         // Found next segment name to add
         // Check if current record too big
         if (ToFile.GetSize() >= 1024 - 256) {   // Max size = 1024
            ToFile.EndRecord();                  // End current record
            ToFile.StartRecord(OMF_LNAMES);      // Start new LNAMES record
         }
         // Store name of this segment
         ToFile.PutString((char*)NameBuffer.Buf() + SectionBuffer[Sec].NewName);
         NameI++;                                // Ready for next name
      }
   }
   // End LNAMES record
   ToFile.EndRecord();
}


void CCOF2OMF::MakeSEGDEF() {
   // Make SEGDEF and GRPDEF records
   int Sec;                                      // Index into SectionBuffer
   uint32_t SegNum = 0;                            // Segment number in new file
   OMF_SAttrib Attr;                             // Segment attributes bitfield
   uint32_t align;                                 // Alignment in new file

   // Loop through SectionBuffer
   for (Sec = 0; Sec < SectionBufferNum; Sec++) {

      if (SectionBuffer[Sec].NewNumber == SegNum+1 && SectionBuffer[Sec].NewNameI) {

         // New segment found
         SegNum++;                               // Segment number

         // Make a SEGDEF record for this segment 
         ToFile.StartRecord(OMF_SEGDEF + 1);     // Odd record number = 32 bit

         // Attributes bitfield
         Attr.u.P = 1;                           // Indicate 32 bit segment
         Attr.u.B = 0;                           // 1 indicates 4 Gbytes
         Attr.u.C = 2;                           // Indicates public combination

         // Translate alignment
         switch(SectionBuffer[Sec].Align) {
         case 0:  // Align by 1
            align = 1;  break;

         case 1:  // Align by 2
            align = 2;  break;

         case 2:  // Align by 4
            align = 5;  break;

         case 3:  // Align by 8 not supported, use 16
         case 4:  // Align by 16
            align = 3;  break;

         default:  // 32 or higher. Use 'page' alignment
            // Note: this gives 256 on some systems, 4096 on other systems
            align = 4;
            if (SectionBuffer[Sec].Align > 8) {
               err.submit(1205, 1 << SectionBuffer[Sec].Align); // Warning: alignment not supported
            }
         }
         Attr.u.A = align;                      // Put alignment into bitfield

         ToFile.PutByte(Attr.b);                // Save attributes bitfield

         // Segment length
         ToFile.PutNumeric(SectionBuffer[Sec].SegmentSize);

         // Segment name given by index into LNAMES record
         ToFile.PutIndex(SectionBuffer[Sec].NewNameI);

         // Class name index
         ToFile.PutIndex(SectionBuffer[Sec].Class);

         // Overlay index (ignored)
         ToFile.PutIndex(0);

         // End SEGDEF record
         ToFile.EndRecord();
      }
   }

   // Make GRPDEF record for the FLAT group
   ToFile.StartRecord(OMF_GRPDEF);               // Strart GRPDEF record
   ToFile.PutIndex(OMF_LNAME_FLAT);              // Index of name "FLAT"
   // No need to put segment indices into the GRPDEF record when group is FLAT
   // End GRPDEF record
   ToFile.EndRecord();
}


void CCOF2OMF::MakeEXTDEF() {
   // Make EXTDEF records
   uint32_t j;                                     // SymbolBuffer entry index
   uint32_t ExtSymNum = 0;                         // External symbol number

   if (NumExternalSymbols > 0) {                 // Are there any external symbols?

      // Make EXTDEF record for one or more symbols
      ToFile.StartRecord(OMF_EXTDEF);            // Start record

      // Loop through SymbolBuffer
      for (j = 0; j < SymbolBuffer.GetNumEntries(); j++) {
         if (SymbolBuffer[j].Scope == S_EXTERNAL && SymbolBuffer[j].NewIndex == ExtSymNum+1) {
            // Found external symbol
            ExtSymNum++;                         // Symbol number   

            // Check if current record too big
            if (ToFile.GetSize() >= 1024 - 257) {// Max size = 1024
               ToFile.EndRecord();               // End current record
               ToFile.StartRecord(OMF_EXTDEF);   // Start new EXTDEF record
            }
            // Put symbol name in record
            ToFile.PutString((char*)NameBuffer.Buf() + SymbolBuffer[j].Name);

            // Type index
            ToFile.PutIndex(0);                  // Not used any more
         }
      }
      ToFile.EndRecord();                        // End EXTDEF record
   }
}


void CCOF2OMF::MakePUBDEF() {
   // Make PUBDEF records
   uint32_t j;                                     // SymbolBuffer entry index
   uint32_t PubSymNum = 0;                         // Public symbol number

   // Loop through SymbolBuffer
   for (j = 0; j < SymbolBuffer.GetNumEntries(); j++) {
      if (SymbolBuffer[j].Scope == S_PUBLIC && SymbolBuffer[j].NewIndex == PubSymNum+1) {
         // Found public symbol
         PubSymNum++;                            // Symbol number   

         // Make PUBDEF record for this symbol
         ToFile.StartRecord(OMF_PUBDEF + 1);     // Start new PUBDEF record, 32 bit

         // Group index
         uint32_t Group = SymbolBuffer[j].Segment ? OMF_LNAME_FLAT : 0; // Group = FLAT, except for absolute symbols
         ToFile.PutIndex(Group);                 // Group name index

         // Segment index
         ToFile.PutIndex(SymbolBuffer[j].Segment);

         // Base frame field if segment = 0
         if (SymbolBuffer[j].Segment == 0) ToFile.PutWord(0);

         // Put symbol name in record
         ToFile.PutString((char*)NameBuffer.Buf() + SymbolBuffer[j].Name);

         // Offset relative to segment
         ToFile.PutNumeric(SymbolBuffer[j].Offset);

         // Type index
         ToFile.PutIndex(0);                     // Not used any more

         // End record
         ToFile.EndRecord();                     // End PUBDEF record
      }
   }
}


void CCOF2OMF::MakeLEDATA() {
/* 
This function makes both LEDATA records, containing binary data, and FIXUPP
records, containing relocations.

The function is quite complicated because the LEDATA and FIXUPP records are 
mutually interdependent. Some explanation is in place here.

I am using the word segment for the collection of all sections in the old file
having the same name. A section is a record of binary data in the old file.
Each section is stored as one or more LEDATA records in the new file.
A segment may thus be split into multiple sections, which again may be split
into multiple LEDATA records. The sections must be aligned according to the
specified alignment for the segment. The LEDATA records need not be aligned,
and they may be misaligned for reasons explained below.

Each LEDATA record is followed by a FIXUPP record containing all relocations 
referring to a source in the LEDATA record, if any.

The size of a LEDATA record is limited to 1024 bytes because each entry in
the FIXUPP record has only 10 bits for addressing it. Some linkers impose
the 1024 bytes size limit to all OMF records, although this limitation is
strictly necessary only for LEDATA records. If the code has many relocations
then it may be necessary to make a LEDATA record smaller than 1024 bytes
in order to avoid that the corresponding FIXUPP record becomes bigger than
1024 bytes. Furthermore, the size of a LEDATA record may need adjustment to
avoid that a relocation source crosses a LEDATA record boundary.

I have stored all relocations in a temporary list RelocationBuffer which is
sorted by relocation source address in order to make it easier to find all
relocations with a source address in the current LEDATA record.
*/
   int    Segment;                               // Segment index in new file
   int    OldSection;                            // Section index in old file
   uint32_t SegOffset;                             // Offset of section relative to segment
   uint32_t SectOffset;                            // Offset of LEDATA record relative to section
   uint32_t CutOff;                                // Size limit for splitting section into multiple LEDATA records
   uint32_t RelFirst;                              // Index into RelocationBuffer of first relocation for section/record
   uint32_t RelLast;                               // Index into RelocationBuffer of last relocation for record + 1
   uint32_t Rel;                                   // Current index into RelocationBuffer
   uint32_t FrameDatum;                            // Frame datum field in FIXUPP record
   uint32_t TargetDatum;                           // Target datum field in FIXUPP record
   uint32_t TargetDisplacement;                    // Target displacement field in FIXUPP record
   uint32_t AlignmentFiller;                       // Number of unused bytes from end of one section to begin of next section due to alignment

   SOMFRelocation Reloc0;                        // Reference relocation record for compare and search
   OMF_SLocat Locat;                             // Locat bitfield for FIXUPP record
   OMF_SFixData FixData;                         // FixData bitfield for FIXUPP record

   // Loop through segment numbers
   for (Segment = 1; Segment <= NumSegments; Segment++) {
      SegOffset = 0;                             // SegOffset = 0 for first section in segment

      // Search through SectionBuffer for old sections contributing to this segment
      for (OldSection = 0; OldSection < SectionBufferNum; OldSection++) {

         if (SectionBuffer[OldSection].NewNumber == (uint32_t)Segment && SectionBuffer[OldSection].Size) {
            // This section contributes to Segment. Make LEDATA record(s)

            if (SectionBuffer[OldSection].Offset > SegOffset) {
               // Fillers needed for alignment after previous section
               AlignmentFiller = SectionBuffer[OldSection].Offset - SegOffset;
            }
            else {
               AlignmentFiller = 0;
            }
            SectOffset = 0;                      // Offset of LEDATA record relative to section start

            if (AlignmentFiller > 0 
            && AlignmentFiller < 4096 && AlignmentFiller < (1u << SectionBuffer[OldSection].Align)
            && SectionBuffer[OldSection].Class == OMF_LNAME_CODE) {
               // This is a code segment and there is a space from previous section
               // Fill the alignment space with NOP's
               // Make LIDATA record with NOP's
               ToFile.StartRecord(OMF_LIDATA);             // Start new LEDATA record
               ToFile.PutIndex(Segment);                   // Segment index 
               ToFile.PutNumeric(SegOffset);               // Offset of LIDATA relative to segment
               ToFile.PutNumeric(AlignmentFiller);         // Repeat count
               ToFile.PutWord(0);                          // Block count
               ToFile.PutByte(1);                          // Byte count
               ToFile.PutByte(0x90);                       // NOP opcode
               ToFile.EndRecord();                         // End LIDATA record
            }

            SegOffset = SectionBuffer[OldSection].Offset; // Offset of section to segment

            // Search for relocations for this section
            Reloc0.Section = OldSection;
            Reloc0.SourceOffset = 0;
            RelFirst = RelocationBuffer.FindFirst(Reloc0); // Points to first relocation for this section            
            RelLast = RelFirst;

            // Loop for possibly more than one LEDATA records for this section
            while (SectOffset < SectionBuffer[OldSection].Size) {

               CutOff = SectionBuffer[OldSection].Size - SectOffset; // Size of rest of section
               if (CutOff > 1024 && SectionBuffer[OldSection].Class != OMF_LNAME_BSS) {
                  CutOff = 1024; // Maximum LEDATA size
               }

               // Search for last relocation entry
               while (RelLast < NumRelocations) {
                  if (RelocationBuffer[RelLast].Section != (uint32_t)OldSection) {
                     break; // Reached end of relocations for this section
                  }
                  if (RelocationBuffer[RelLast].SourceOffset >= CutOff + SectOffset) {
                     break; // Reached size limit of LEDATA record
                  }
                  if (RelocationBuffer[RelLast].SourceOffset + 4 > CutOff + SectOffset) {
                     // Relocation crosses LEDATA boundary. 
                     // Reduce limit of LEDATA to before this relocation source
                     CutOff = RelocationBuffer[RelLast].SourceOffset - SectOffset;
                     if (CutOff == 0) {
                        err.submit(2302); // Relocation source extends beyond end of section. 
                        CutOff = 4;       // Prevent infinite loop
                     }
                     break;
                  }
                  if (RelLast - RelFirst > 100) {
                     // FIXUPP record will become too big. End LEDATA record here
                     CutOff = RelocationBuffer[RelLast].SourceOffset - SectOffset;
                     break;
                  }
                  RelLast++;
               } // End of search for last relocation entry for this LEDATA

               if (SectionBuffer[OldSection].Class == OMF_LNAME_BSS) {
                  // BSS: Unitialized data section needs no LEDATA record and no FIXUPP
                  if (RelLast > RelFirst) {
                     // Error: Relocation of uninitialized data
                     err.submit(2041);
                  }
               }
               else {
                  // Section contains initialized data. Needs LEDATA and FIXUPP records

                  // Make LEDATA record for section data
                  ToFile.StartRecord(OMF_LEDATA + 1);// Start new LEDATA record, 32 bit

                  // Segment index
                  ToFile.PutIndex(Segment);         

                  // Offset of LEDATA relative to segment
                  ToFile.PutNumeric(SegOffset + SectOffset);

                  // Binary data
                  ToFile.PutBinary(Buf() + SectionBuffer[OldSection].psechdr->PRawData + SectOffset, CutOff);

                  // End LEDATA record
                  ToFile.EndRecord();

                  if (RelLast > RelFirst) {      // If there are any relocations

                     // Make FIXUPP record with (RelLast-RelFirst) relocation entries
                     ToFile.StartRecord(OMF_FIXUPP + 1); // Start FIXUPP record, 32 bit

                     // Loop through relocations
                     for (Rel = RelFirst; Rel < RelLast; Rel++) {

                        if (RelocationBuffer[Rel].Mode < 0) {
                           // Unsupported mode. Make error message
                           err.submit(2030, RelocationBuffer[Rel].TargetOffset);   // TargetOffset contains COFF relocation mode
                           continue;
                        }

                        // Make Locat word bitfield 
                        Locat.s.one = 1;              // Indicates FIXUP subrecord
                        Locat.s.M = RelocationBuffer[Rel].Mode; // Direct / EIP-relative
                        Locat.s.Location = 9;              // Indicates 32-bit offset

                        // Offset of source relative to section (10 bits)
                        uint32_t RelocOffset = RelocationBuffer[Rel].SourceOffset - SectOffset; // Offset of relocation source to begin of LEDATA record
                        if (RelocOffset >= 1024) err.submit(9000); // Check that it fits into 10 bits
                        Locat.s.Offset = RelocOffset;

                        // Make FixData byte bitfield
                        FixData.b = 0;              // Start with all bits 0

                        if (RelocationBuffer[Rel].Scope == S_LOCAL) {
                           // Local target
                           FixData.s.Target = 0;      // Target method T0 or T4: Target = segment
                           FixData.s.Frame = 1;       // Frame method F1: specified by a group index (FLAT)
                           FrameDatum = OMF_LNAME_FLAT; // Target frame = FLAT group
                           TargetDatum = RelocationBuffer[Rel].TargetSegment; // Fixup target = segment
                           TargetDisplacement = RelocationBuffer[Rel].TargetOffset; // Displacement or addend (?)
                        }
                        else {
                           // External symbol target
                           FixData.s.Frame = 5;       // Frame method F5: Frame specified by target, not here
                           FixData.s.Target = 2;      // Target method T2 or T6: Target specified by EXTDEF index
                           TargetDatum = RelocationBuffer[Rel].TargetSegment; // Index into EXTDEF
                           TargetDisplacement = RelocationBuffer[Rel].TargetOffset; // This is zero. Any addend is inline
                        }
                        if (TargetDisplacement) {
                           FixData.s.P = 0;           // Displacement field present
                        }
                        else {
                           FixData.s.P = 1;           // Displacement field absent
                        }

                        // Put these data into record
                        // Locat bytes in reverse order:
                        ToFile.PutByte(Locat.bytes[1]);  
                        ToFile.PutByte(Locat.bytes[0]);
                        // FixData byte
                        ToFile.PutByte(FixData.b);      
                        // Frame datum field only if FixData.F = 0 and Frame < 4
                        if (FixData.s.Frame < 4) ToFile.PutIndex(FrameDatum); 
                        // Target datum field if FixData.T = 0, which it is here
                        ToFile.PutIndex(TargetDatum);
                        // Target displacement field if FixData.P = 0
                        if (FixData.s.P == 0) ToFile.PutNumeric(TargetDisplacement);

                     } // End of loop through relocation for last LEDATA

                     // End FIXUPP record
                     ToFile.EndRecord();
                  }
               }
               // Update pointers to after this LEDATA
               SectOffset += CutOff;
               RelFirst = RelLast;

            } // End of loop for multiple LEDATA records for one section

            // Find end of section
            SegOffset += SectionBuffer[OldSection].Size;   // End of section

         } // End of if section in segment
      } // End of loop through multiple sections for same segment
   } // End of loop through segments
}


void CCOF2OMF::MakeMODEND() {
   // Make MODEND record and finish file
   ToFile.StartRecord(OMF_MODEND);               // Start MODEND record
   ToFile.PutByte(0);                            // Module type field. 0 if not a startup module with start address
   ToFile.EndRecord();                           // End MODEND record
}