/****************************  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 namei;                                 // Name index into NameBuffer
   uint32 align;                                 // Segment alignment = 2^align
   int32 align2;                                 // align2 = 2^align
   uint32 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, 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 * 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 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 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 * paddend = 0;                          // Pointer to inline addend
   uint32 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 * b;                                  // Used for address calculation
   } Symtab;

   union {                                       // Pointer to relocation entry
      SCOFF_Relocation * p;                      // pointer to record
      int8 * 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 (isym >= 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*)(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(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 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 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::TruncateMemberName(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(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 SegNum = 0;                            // Segment number in new file
   OMF_SAttrib Attr;                             // Segment attributes bitfield
   uint32 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 j;                                     // SymbolBuffer entry index
   uint32 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(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 j;                                     // SymbolBuffer entry index
   uint32 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 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(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 SegOffset;                             // Offset of section relative to segment
   uint32 SectOffset;                            // Offset of LEDATA record relative to section
   uint32 CutOff;                                // Size limit for splitting section into multiple LEDATA records
   uint32 RelFirst;                              // Index into RelocationBuffer of first relocation for section/record
   uint32 RelLast;                               // Index into RelocationBuffer of last relocation for record + 1
   uint32 Rel;                                   // Current index into RelocationBuffer
   uint32 FrameDatum;                            // Frame datum field in FIXUPP record
   uint32 TargetDatum;                           // Target datum field in FIXUPP record
   uint32 TargetDisplacement;                    // Target displacement field in FIXUPP record
   uint32 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 == 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 != 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 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
}
