/****************************  omf2cof.cpp   *********************************
* Author:        Agner Fog
* Date created:  2007-02-08
* Last modified: 2009-07-16
* Project:       objconv
* Module:        omf2cof.cpp
* Description:
* Module for converting OMF file to PE/COFF file
*
* Copyright 2007-2009 GNU General Public License http://www.gnu.org/licenses
*****************************************************************************/
#include "stdafx.h"

// Alignment value translation table
static const uint32 OMFAlignTranslate[8] = {0,1,2,16,256,4,0,0};

COMF2COF::COMF2COF() {
   // Constructor
   memset(this, 0, sizeof(*this));               // Reset everything
}

void COMF2COF::Convert() {
   // Do the conversion
   // Allocate variable size buffers
   //NewSectIndex.SetNum(this->NSections);// Allocate section translation table
   //NewSectIndex.SetZero();              // Initialize

   // Call the subfunctions
   ToFile.SetFileType(FILETYPE_COFF);  // Set type of to file
   MakeFileHeader();                   // Make file header
   MakeSymbolTable1();                 // Make symbol table and string table entries for file and segments
   MakeSymbolTable2();                 // Make symbol table and string table entries for external symbols
   MakeSymbolTable3();                 // Make symbol table and string table entries for public symbols
   MakeSymbolTable4();                 // Make symbol table and string table entries for communal symbols
   MakeSymbolTable5();                 // Make symbol table and string table entries for local symbols
   MakeSections();                     // Make sections and relocation tables
   CheckUnsupportedRecords();          // Make warnings if file containes unsupported record types
   MakeBinaryFile();                   // Put sections together
   *this << ToFile;                    // Take over new file buffer
}


void COMF2COF::MakeFileHeader() {
   // Convert subfunction: File header
   // Make PE file header
   NewFileHeader.Machine = PE_MACHINE_I386;
   NewFileHeader.TimeDateStamp = (uint32)time(0);
   NewFileHeader.SizeOfOptionalHeader = 0;
   NewFileHeader.Flags = 0;

   // Values inserted later:
   NewFileHeader.NumberOfSections = 0;
   NewFileHeader.PSymbolTable = 0;
   NewFileHeader.NumberOfSymbols = 0;
}


void COMF2COF::MakeSymbolTable1() {
   // Make symbol table string table and section table entries for file and segments
   SCOFF_SymTableEntry sym;                      // Symbol table entry
   SCOFF_SectionHeader sec;                      // Section header entry
   char * ClassName;                             // Old segment class name

   // Initialize new string table. make space for 4-bytes size
   NewStringTable.Push(0, 4);

   // Allocate SegmentTranslation buffer
   SegmentTranslation.SetNum(SegmentNameOffset.GetNumEntries());

   // Make symbol table entry for file name
   memset(&sym, 0, SIZE_SCOFF_SymTableEntry);
   strcpy(sym.s.Name, ".file");
   sym.s.SectionNumber = COFF_SECTION_DEBUG;
   sym.s.StorageClass = COFF_CLASS_FILE;
   char * ShortFileName = CLibrary::TruncateMemberName(OutputFileName);
   sym.s.NumAuxSymbols = 1;  // File name is truncated so it will fit into the 18 bytes of SIZE_SCOFF_SymTableEntry
   NewSymbolTable.Push(sym); // Store symbol table entry
   // Needs auxiliary entry:
   memset(&sym, 0, SIZE_SCOFF_SymTableEntry);
   if (strlen(ShortFileName) < SIZE_SCOFF_SymTableEntry) {
      strcpy(sym.s.Name, ShortFileName);
   }
   NewSymbolTable.Push(sym); // Store auxiliary symbol table entry

   // Define structure of attributes
   OMF_SAttrib Attributes;
   // Other segment properties
   uint32 Offset, SegLength, NameIndex, ClassIndex;
   const char * sname;                           // Segment/section name
   uint32 SegNum = 0;                            // Segment/section number
   uint32 StringI;                               // New sting table index
   uint32 i;                                     // Record number
   int32  j, n;                                  // Temporary
   
   // Loop through segments of old file
   for (i = 0; i < NumRecords; i++) {
      if (Records[i].Type2 == OMF_SEGDEF) {
         // SEGDEF record
         Records[i].Index = 3;
         // Loop through entries in record. There should be only 1
         while (Records[i].Index < Records[i].End) {
            Attributes.b = Records[i].GetByte(); // Read attributes
            if (Attributes.u.A == 0) {
               // Frame and Offset only included if A = 0
               Records[i].GetWord();             // Frame ignored
               Offset = Records[i].GetByte();
            }
            else Offset = 0;
            SegLength  = Records[i].GetNumeric();
            NameIndex  = Records[i].GetIndex();
            ClassIndex = Records[i].GetIndex();  // Class index
            Records[i].GetIndex();               // Overlay index ignored
            sname = GetLocalName(NameIndex);     // Segment name = new section name

            if (Attributes.u.B) {
               // Segment is big
               if (Attributes.u.P) {
                  // 32 bit segment. Big means 2^32 bytes!
                  err.submit(2306);
               }
               else {
                  // 16 bit segment. Big means 2^16 bytes
                  SegLength = 0x10000;
               }
            }

            // make symbol table entry
            memset(&sym, 0, SIZE_SCOFF_SymTableEntry);

            // Put name into string table
            StringI = NewStringTable.PushString(sname);

            // Put name into symbol table
            //COFF_PutNameInSymbolTable(sym, sname, NewStringTable);
            ((uint32*)(sym.s.Name))[1] = StringI;

            sym.s.SectionNumber = ++SegNum;        // Count section number
            sym.s.StorageClass = COFF_CLASS_STATIC;
            sym.s.NumAuxSymbols = 1;               // Needs 1 aux record

            // Remember NewSymbolTable index
            SegmentTranslation[SegNum] = NewSymbolTable.GetNumEntries();
            NewSymbolTable.Push(sym);            // Store symbol table entry

            // Make auxiliary entry
            memset(&sym, 0, SIZE_SCOFF_SymTableEntry);

            // Insert section size
            sym.section.Length = SegLength;

            // Remember to insert NumberOfRelocations here later

            // Store auxiliary symbol table entry
            NewSymbolTable.Push(sym);

            // Make section header            
            memset(&sec, 0, sizeof(sec));        // Reset section header

            // Put name into section header
            sprintf(sec.Name, "/%i", StringI);

            // Put size into section header
            sec.SizeOfRawData = SegLength;

            // Alignment
            switch (Attributes.u.A) {
            case 0:  // Absolute segment
               err.submit(2307);  break;
            case 1:  // Byte
               sec.Flags |= PE_SCN_ALIGN_1;  break;
            case 2:  // Word
               sec.Flags |= PE_SCN_ALIGN_2;  break;
            case 3:  // Paragraph
               sec.Flags |= PE_SCN_ALIGN_16;  break;
            case 4:  // Page. May be 256 or 4096, depending on system
               // If we use 4096 where the source intended 256, we may get
               // size and symbol offsets wrong!
               sec.Flags |= PE_SCN_ALIGN_256;  break;
            case 5:  // Dword
               sec.Flags |= PE_SCN_ALIGN_4;  break;
            default: // Unknown alignment
               err.submit(2308, Attributes.u.A);
               sec.Flags |= PE_SCN_ALIGN_16;  break;
            }

            // Get further attributes from class name
            ClassName = GetLocalName(ClassIndex);

            // Convert class name to upper case
            n = (int32)strlen(ClassName);
            for (j = 0; j < n; j++) ClassName[j] &= ~0x20;

            // Search for known class names.
            // Standard names are CODE, DATA, BSS, CONST, STACK
            if (strstr(ClassName, "CODE") || strstr(ClassName, "TEXT")) {
               // Code segment
               sec.Flags |= PE_SCN_CNT_CODE | PE_SCN_MEM_EXECUTE | PE_SCN_MEM_READ;
            }
            else if (strstr(ClassName, "DATA")) {
               // Data segment
               sec.Flags |= PE_SCN_CNT_INIT_DATA | PE_SCN_MEM_READ | PE_SCN_MEM_WRITE;
            }
            else if (strstr(ClassName, "BSS")) {
               // Unitialized data segment
               sec.Flags |= PE_SCN_CNT_UNINIT_DATA | PE_SCN_MEM_READ | PE_SCN_MEM_WRITE;
            }
            else if (strstr(ClassName, "CONST")) {
               // Constant data segment
               sec.Flags |= PE_SCN_CNT_INIT_DATA | PE_SCN_MEM_READ;
            }
            else if (strstr(ClassName, "STACK")) {
               // Stack segment. Ignore
               sec.Flags |= PE_SCN_LNK_REMOVE;
               err.submit(1206); // Warning: ignored
            }
            else {
               // Unknown/user defined class. Assume data segment
               sec.Flags |= PE_SCN_CNT_INIT_DATA | PE_SCN_MEM_READ | PE_SCN_MEM_WRITE;
            }

            // Insert pointers to relocations and raw data later
            // Store section header
            NewSectionHeaders.Push(sec);
         }
         if (Records[i].Index != Records[i].End) err.submit(1203);   // Check for consistency
      }
      if (Records[i].Type2 == OMF_COMDAT || Records[i].Type2 == OMF_COMDEF) {
         // Communal sections
         err.submit(1055);
      }
   }
}

void COMF2COF::MakeSymbolTable2() {
   // Make symbol table and string table entries for external symbols
   uint32 i;
   SCOFF_SymTableEntry sym;                      // new symbol table entry
   uint32 NumExtSym = SymbolNameOffset.GetNumEntries(); // Number of external symbols
   ExtdefTranslation.SetNum(NumExtSym+1);          // Allocate space in translation table

   // Loop through external symbol names
   for (i = 1; i < NumExtSym; i++) {
      // Reset symbol table entry
      memset(&sym, 0, SIZE_SCOFF_SymTableEntry);

      // Insert name
      COFF_PutNameInSymbolTable(sym, GetSymbolName(i), NewStringTable);

      // Insert storage class
      sym.s.StorageClass = COFF_CLASS_EXTERNAL;

      // Store symbol table entry
      NewSymbolTable.Push(sym);

      // Update table for translating old EXTDEF number (1-based) to new symbol table index (0-based)
      ExtdefTranslation[i] = NewSymbolTable.GetNumEntries() - 1;
   }
}


void COMF2COF::MakeSymbolTable3() {
   // Make symbol table and string table entries for public symbols
   SCOFF_SymTableEntry sym;                      // new symbol table entry
   uint32 i;                                     // Record index
   char * string;                                // Symbol name
   uint32 Segment;                               // Segment
   uint32 Offset;                                // Offset
   uint32 Namei;                                 // Index into symbol table
   SOMFLocalSymbol localsym;                     // Entry into LocalSymbols

   // Search for PUBDEF records
   for (i = 0; i < NumRecords; i++) {
      if (Records[i].Type2 == OMF_PUBDEF) {
         // PUBDEF record

         Records[i].Index = 3;
         Records[i].GetIndex();                  // Group. Ignore
         Segment = Records[i].GetIndex();        // Segment
         if (Segment == 0) Records[i].GetWord(); // Base frame. Ignore

         // Loop through strings in record
         while (Records[i].Index < Records[i].End) {
            string = Records[i].GetString();     // Symbol name
            Offset = Records[i].GetNumeric();    // Offset to segment
            Records[i].GetIndex();               // Type index. Ignore

            // Reset symbol table entry
            memset(&sym, 0, SIZE_SCOFF_SymTableEntry);

            // Insert name
            Namei = COFF_PutNameInSymbolTable(sym, string, NewStringTable);

            // Insert storage class
            sym.s.StorageClass = COFF_CLASS_EXTERNAL;

            // Store offset
            sym.s.Value = Offset;

            // Section number = segment number
            if (Segment == 0) sym.s.SectionNumber = COFF_SECTION_ABSOLUTE;
            else sym.s.SectionNumber = (int16)Segment;

            // Store symbol table entry
            NewSymbolTable.Push(sym);

            // Make entry into LocalSymbols to indicate that this symbol has a name
            if (Segment > 0) {
               // Make index into NewStringTable if we don't allready have one
               if (Namei == 0) Namei = NewStringTable.PushString(string);
               localsym.Offset = Offset;
               localsym.Segment = Segment;
               localsym.Name = Namei;
               localsym.NewSymtabIndex = NewSymbolTable.GetNumEntries() - 1; // 0-based index into new symbol table
               LocalSymbols.PushUnique(localsym);
            }
         }
         if (Records[i].Index != Records[i].End) err.submit(1203);   // Check for consistency
      }
   }
}

void COMF2COF::MakeSymbolTable4() {
   // Make symbol table and string table entries for communal symbols
   // Warning: Currently not supported!
}

void COMF2COF::MakeSymbolTable5() {
   // Make symbol table and string table entries for local symbols.
   // There is no table for local symbols in OMF files. We have to search
   // through all FIXUPP records for relocation targets and assign arbitrary
   // names to them.

   uint32 i;                                     // Loop counter
   uint32 Target, TargetDisplacement;            // Contents of FIXUPP record
   uint8 byte1, byte2;                           // First two bytes of FIXUPP subrecord
   // Bitfields in subrecords
   OMF_SLocat Locat;                             // Structure of first two bytes of FIXUP subrecord swapped = Locat field
   OMF_SFixData FixData;                         // Structure of FixData field in FIXUP subrecord of FIXUPP record
   OMF_STrdDat TrdDat;                           // Structure of Thread Data field in THREAD subrecord of FIXUPP record
   int8 * LastDataRecordPointer = 0;             // Pointer to data in the data record that relocations refer to
   uint32 LastDataRecordSize = 0;                // Size of the data record that relocations refer to
   SOMFLocalSymbol localsym;                     // Entry into LocalSymbols
   uint32 LocalSymNum = 0;                       // Number of unnamed local symbols
   char NewName[32];                             // Buffer for making new name
   SCOFF_SymTableEntry sym;                      // New symbol table entry

   // Search for FIXUPP records and data records
   for (i = 0; i < NumRecords; i++) {

      if (Records[i].Type2 == OMF_LEDATA) {
         // LEDATA record. Remember pointer to binary data in order to read inline offset
         Records[i].Index = 3;                   // Initialize record reading
         Records[i].GetIndex();                  // Read segment and offset
         Records[i].GetNumeric();
         LastDataRecordSize = Records[i].End - Records[i].Index; // Calculate size of data
         LastDataRecordPointer = Records[i].buffer + Records[i].FileOffset + Records[i].Index;
      }

      if (Records[i].Type2 == OMF_FIXUPP) {
         // FIXUPP record
         Records[i].Index = 3;                   // Initialize record reading

         // Loop through entries in record
         while (Records[i].Index < Records[i].End) {

            // Read first byte
            byte1 = Records[i].GetByte();

            if (byte1 & 0x80) {
               // This is a FIXUP subrecord

               Target = 0; TargetDisplacement = 0;
               // read second byte
               byte2 = Records[i].GetByte();
               // swap bytes and put into byte12 bitfield
               Locat.bytes[1] = byte1;
               Locat.bytes[0] = byte2;
               // Read FixData
               FixData.b = Records[i].GetByte();
               // Read conditional fields
               if (FixData.s.F == 0) {
                  if (FixData.s.Frame < 4) {
                     Records[i].GetIndex();  // Frame. Ignore
                  }
               }
               if (FixData.s.T == 0) {
                  // Target specified
                  Target = Records[i].GetIndex();
                  uint32 TargetMethod = FixData.s.Target + FixData.s.P * 4;
               }
               if (FixData.s.P == 0) {
                  TargetDisplacement = Records[i].GetNumeric();
               }
               // Get inline addend
               if (LastDataRecordPointer && Locat.s.Offset < LastDataRecordSize) {
                  int8 * inlinep = LastDataRecordPointer + Locat.s.Offset;
                  if (Locat.s.Location == 9 || Locat.s.Location == 13) {
                     TargetDisplacement += *(int32*)inlinep;
                  }
               }
               if (FixData.s.T == 0 && (FixData.s.Target == 0 || FixData.s.Target == 1)) {
                  // Target is local symbol

                  // Make entry in LocalSymbols
                  localsym.Offset = TargetDisplacement;    // Offset to segment
                  localsym.Segment = Target;               // Target segment
                  localsym.Name = 0;                       // Has no name yet
                  localsym.NewSymtabIndex = 0;             // Not in new symbol table yet
                  // Make entry in LocalSymbols.
                  // PushUnique will not make an entry if this target address already
                  // has an entry in LocalSymbols and possibly a public name
                  LocalSymbols.PushUnique(localsym);       
               }
            }
            else {
               // This is a THREAD subrecord. Read and ignore
               TrdDat.b = byte1;                 // Put byte into bitfield
               if (TrdDat.s.Method < 4) {
                  Records[i].GetIndex();         // has index field if method < 4 ?
               }
            }
         } // Finished loop through subrecords
         if (Records[i].Index != Records[i].End) err.submit(1203);   // Check for consistency
      }
   } // Finished loop through records

   // Now check LocalSymbols for unnamed symbols
   for (i = 0; i < LocalSymbols.GetNumEntries(); i++) {
      if (LocalSymbols[i].Name == 0) {

         // Unnamed symbol. Give it a name
         sprintf(NewName, "?NoName%02i", ++LocalSymNum);

         // Make index in new symbol table
         // Reset symbol table entry
         memset(&sym, 0, SIZE_SCOFF_SymTableEntry);

         // Insert name
         LocalSymbols[i].Name = COFF_PutNameInSymbolTable(sym, NewName, NewStringTable);
         // if (LocalSymbols[i].Name == 0) LocalSymbols[i].Name = NewStringTable.PushString(NewName);

         // Store offset
         sym.s.Value = LocalSymbols[i].Offset;

         // Section number = segment number
         sym.s.SectionNumber = LocalSymbols[i].Segment;

         // Storage class
         sym.s.StorageClass = COFF_CLASS_STATIC;

         // Store symbol table entry
         NewSymbolTable.Push(sym);

         // Store index into new symbol table (0 - based)
         LocalSymbols[i].NewSymtabIndex = NewSymbolTable.GetNumEntries() - 1;
      }
   }
}


void COMF2COF::MakeSections() {
   // Make sections and relocation tables
   uint32 SegNum;                                // Index into NewSectionHeaders = segment - 1
   uint32 DesiredSegment;                        // Old segment number = new section number
   uint32 RecNum;                                // Old record number
   CMemoryBuffer TempBuf;                        // Temporary buffer for building raw data
   CMemoryBuffer RelocationTable;                // Temporary buffer for building new relocation table
   SCOFF_Relocation rel;                         // New relocation table record
   uint32 LastDataRecord = 0;                    // Index to the data record that relocations refer to
   uint32 LastDataRecordSize = 0;                // Size of the data record that relocations refer to
   int8 * LastDataRecordPointer = 0;             // Pointer to data in the data record that relocations refer to
   uint32 Segment;                               // Segment of last LEDATA, LIDATA or COMDEF record
   uint32 Offset;                                // Offset of LEDATA or LIDATA record to segment
   uint32 Size;                                  // Size of data in LEDATA or LIDATA record
   uint32 SegmentSize;                           // Total size of segment
   uint32 LastOffset;                            // Offset after last LEDATA into segment
   uint32 FileOffsetData;                        // File offset of first raw data and relocations in new file
   uint32 FileOffset;                            // File offset of current raw data or relocations

   // File offset of first data = size of file header and section headers
   FileOffsetData = sizeof(SCOFF_FileHeader) + NewSectionHeaders.GetNumEntries() * sizeof(SCOFF_SectionHeader);

   // Loop through segments
   for (SegNum = 0; SegNum < NewSectionHeaders.GetNumEntries(); SegNum++) {

      DesiredSegment = SegNum + 1;               // Search for records referring to this segment

      SegmentSize = NewSectionHeaders[SegNum].SizeOfRawData;
      if (SegmentSize == 0) continue;            // Empty segment

      // Allocate temporary data buffer and reset it
      TempBuf.SetSize(SegmentSize + 16);
      int FillByte = 0;                          // Byte to fill memory with
      if (NewSectionHeaders[SegNum].Flags & PE_SCN_CNT_CODE) {
         // Code segment. Fill any unused bytes with NOP opcode = 0x90
         FillByte = 0x90;
      }
      memset(TempBuf.Buf(), FillByte, SegmentSize + 16);// Reset to all 0 or NOP

      // Reset relocation table buffer
      RelocationTable.SetSize(0);

      LastOffset = 0;  LastDataRecordSize = 0;

      // Search for LEDATA, LIDATA and FIXUPP records for this segment
      for (RecNum = 0; RecNum < NumRecords; RecNum++) {
         if (Records[RecNum].Type2 == OMF_LEDATA) {

            // LEDATA record
            Records[RecNum].Index = 3;           // Initialize record reading
            Segment = Records[RecNum].GetIndex();// Read segment number

            if (Segment != DesiredSegment) continue; // Does not refer to this segment

            Offset  = Records[RecNum].GetNumeric();// Read offset
            Size    = Records[RecNum].End - Records[RecNum].Index; // Calculate size of data
            LastDataRecord = RecNum;             // Save for later FIXUPP that refers to this record

            if (Offset < LastOffset + LastDataRecordSize && LastOffset < Offset + Size) {
               // Overlapping data records
               if (Offset + 8 < LastOffset + LastDataRecordSize || !(NewSectionHeaders[SegNum].Flags & PE_SCN_CNT_CODE)) {
                  // Overlapping data by more than 7 bytes or not executable code
                  err.submit(1207);
               }
               else {
                  // Possibly backpatched code
                  err.submit(1208);              // Warning
                  err.ClearError(1208);          // Report only once
               }
            }

            LastDataRecordSize = Size;
            LastDataRecordPointer = Records[RecNum].buffer + Records[RecNum].FileOffset + Records[RecNum].Index;
            LastOffset = Offset;                 // Save offset for subsequent FIXUPP records

            // Check if data within segment
            if (Offset + Size > SegmentSize) {
               err.submit(2309, GetSegmentName(Segment));
               continue;
            }

            // Put raw data into temporary buffer
            memcpy(TempBuf.Buf() + Offset, LastDataRecordPointer, Size);

         } // Finished with LEDATA record

         if (Records[RecNum].Type2 == OMF_LIDATA) {
            // LIDATA record
            Records[RecNum].Index = 3;           // Initialize record reading
            Segment = Records[RecNum].GetIndex();

            if (Segment != DesiredSegment) continue; // Does not refer to this segment

            LastDataRecord = RecNum;             // Save for later FIXUPP that refers to this record

            Offset  = Records[RecNum].GetNumeric();// Read offset

            if (Offset > SegmentSize) {
               err.submit(2310); continue;       // Error: outside bounds
            }

            // Unpack LIDATA blocks recursively
            Size = Records[RecNum].UnpackLIDATABlock(TempBuf.Buf() + Offset, SegmentSize - Offset);

            if (Offset < LastOffset + LastDataRecordSize && LastOffset < Offset + Size) {
               // Overlapping data records
               err.submit(1207);                 // Warning
            }
            LastDataRecordSize = Size;           // Save data size
            LastOffset = Offset;                 // Save offset for subsequent FIXUPP records

         } // Finished with LIDATA record

         if (Records[RecNum].Type2 == OMF_COMDAT) {
            // COMDAT record. Currently not supported by objconv
            LastDataRecord = RecNum;             // Save for later FIXUPP that refers to this record
            Segment = 0;                         // Ignore any relocation referring to this
         }

         if (Records[RecNum].Type2 == OMF_FIXUPP) {
            // FIXUPP record

            if (Segment != DesiredSegment) continue; // Does not refer to this segment

            uint32 Frame, Target, TargetDisplacement; // Contents of FIXUPP record
            uint8 byte1, byte2;                 // First two bytes of subrecord

            // Bitfields in subrecords
            OMF_SLocat Locat;         // Structure of first two bytes of FIXUP subrecord swapped = Locat field
            OMF_SFixData FixData;     // Structure of FixData field in FIXUP subrecord of FIXUPP record
            OMF_STrdDat TrdDat;       // Structure of Thread Data field in THREAD subrecord of FIXUPP record

            Records[RecNum].Index = 3;

            if (Records[LastDataRecord].Type2 != OMF_LEDATA && Records[RecNum].Index < Records[RecNum].End) {
               // Non-empty FIXUPP record does not refer to LEDATA record
               if (Records[LastDataRecord].Type2 == OMF_COMDAT) {
                  // COMDAT currently not supported. Ignore!
               }
               else if (Records[LastDataRecord].Type2 == OMF_LIDATA) {
                  err.submit(2311);              // Error: Relocation of iterated data not supported
               }
               else {
                  err.submit(2312);              // Does not refer to data record
               }
               continue;                         // Ignore this FIXUPP record
            }

            // Loop through entries in record
            while (Records[RecNum].Index < Records[RecNum].End) {

               // Read first byte
               byte1 = Records[RecNum].GetByte();
               if (byte1 & 0x80) {

                  // This is a FIXUP subrecord
                  Frame = 0; Target = 0; TargetDisplacement = 0;

                  // read second byte
                  byte2 = Records[RecNum].GetByte();
                  // swap bytes and put into byte12 bitfield
                  Locat.bytes[1] = byte1;
                  Locat.bytes[0] = byte2;
                  // Read FixData
                  FixData.b = Records[RecNum].GetByte();

                  // Read conditional fields
                  if (FixData.s.F == 0) {
                     if (FixData.s.Frame < 4) {
                        Frame = Records[RecNum].GetIndex();
                     }
                  }

                  if (FixData.s.T == 0) {
                     // Target specified
                     Target = Records[RecNum].GetIndex();
                     uint32 TargetMethod = FixData.s.Target + FixData.s.P * 4;
                  }
                  else {
                     // Target specified in previous thread
                     // Does anybody still use compression of repeated fixup targets?
                     // I don't care to support this if it is never used
                     err.submit(2313);           // Error message: not supported
                     continue;
                  }

                  if (FixData.s.P == 0) {
                     TargetDisplacement = Records[RecNum].GetNumeric();
                  }

                  // Get inline addend and check relocation method
                  if (LastDataRecordPointer && Locat.s.Offset < LastDataRecordSize) {
                     // Pointer to relocation source inline in raw data:
                     int8 * inlinep = LastDataRecordPointer + Locat.s.Offset;

                     switch (Locat.s.Location) { // Relocation method

                     case 9: case 13: // 32 bit
                        // The OMF format may indicate a relocation target by an
                        // offset stored inline in the relocation source.
                        // We prefer to store the target address explicitly in a
                        // symbol table entry. 

                        // Add the inline offset to the explicit offset
                        TargetDisplacement += *(uint32*)inlinep;

                        // Remove the inline addend to avoid adding it twice:
                        // We have to do this in the new buffer TempBuf because 
                        // the data have already been copied to TempBuf
                        if (*(uint32*)(TempBuf.Buf() + LastOffset + Locat.s.Offset) != *(uint32*)inlinep) {
                           // Check that the data in Buf() and TempBuf.Buf() are the same
                           err.submit(9000);
                        }
                        // Remove the inline addend to avoid adding it twice
                        *(uint32*)(TempBuf.Buf() + LastOffset + Locat.s.Offset) = 0;
                        break;

                     case 0: case 4:  // 8 bit. Not supported
                        err.submit(2316, "8 bit");  break;

                     case 1: case 2: case 5: // 16 bit. Not supported
                        err.submit(2316, "16 bit");  break;

                     case 3: // 16+16 bit. Not supported
                        err.submit(2316, "16+16 bit far");  break;

                     case 6: case 11: // 16+32 bit. Not supported
                        err.submit(2316, "16+32 bit far");  break;
                     }
                  }

                  // Make relocation record
                  // Offset of relocation source
                  rel.VirtualAddress = Locat.s.Offset + LastOffset; 

                  SOMFLocalSymbol locsym; // Symbol record for search in LocalSymbols table
                  int32 LocalSymbolsIndex; // Index into LocalSymbols table

                  // Relocation type: direct or EIP-relative
                  // (The displacement between relocation source and EIP for 
                  // self-relative relocations is implicit in both OMF and COFF 
                  // files. No need for correction)
                  rel.Type = Locat.s.M ? COFF32_RELOC_DIR32 : COFF32_RELOC_REL32;

                  switch (FixData.s.Target) { // = Target method modulo 4
                  case 0: // T0 and T4: Target = segment

                     // Local or public symbol. Search in LocalSymbols table
                     locsym.Segment = Target;     // Target segment
                     locsym.Offset = TargetDisplacement;  // Target offset including inline displacement
                     // Find in LocalSymbols table
                     LocalSymbolsIndex = LocalSymbols.Exists(locsym);
                     if (LocalSymbolsIndex < 0) {err.submit(9000); continue;} // Not found

                     // Get index into new symbol table
                     rel.SymbolTableIndex = LocalSymbols[LocalSymbolsIndex].NewSymtabIndex;
                     break;

                  case 1: // T1 and T5: Target = segment group
                     // Don't know how to handle group-relative relocation. Make error message
                     err.submit(2315, GetLocalName(Target));
                     continue;

                  case 2: // T2 and T6: Target = external symbol

                     // Translate old EXTDEF index to new symbol table index
                     rel.SymbolTableIndex = ExtdefTranslation[Target];

                     // Put addend inline in new file
                     if (LastOffset + Locat.s.Offset < SegmentSize) {
                        *(uint32*)(TempBuf.Buf() + LastOffset + Locat.s.Offset) = TargetDisplacement;
                     }
                     break;

                  default: // Unknown method
                     err.submit(2314, FixData.s.Target + FixData.s.P * 4);
                  }

                  // Store in temporary relocation table
                  RelocationTable.Push(&rel, SIZE_SCOFF_Relocation);

               }
               else {
                  // This is a THREAD subrecord.
                  // I don't think this feature for compressing fixup data is
                  // used any more, if it ever was. I am not supporting it here.
                  // Frame threads can be safely ignored. A target thread cannot
                  // be ignored if there is any reference to it. The error is 
                  // reported above at the reference to a target thread, not here.
                  TrdDat.b = byte1;              // Put byte into bitfield
                  if (TrdDat.s.Method < 4) {     // Make sure we read this correctly, even if ignored
                     Records[RecNum].GetIndex(); // has index field if method < 4 ?
                  }
               }
            } // Finished loop through subrecords

            if (Records[RecNum].Index != Records[RecNum].End) err.submit(1203);   // Check for consistency
         }
      } // End of loop to search for LEDATA, LIDATA and FIXUPP records for this segment

      // Transfer raw data from TempBuf to NewData buffer
      FileOffset = NewData.Push(TempBuf.Buf(), SegmentSize);

      // Put file offset of raw data into section header
      NewSectionHeaders[SegNum].PRawData = FileOffsetData + FileOffset;

      // Align relocation table by 4
      NewData.Align(4);

      // Transfer relocation table from RelocationTable to NewData buffer
      FileOffset = NewData.Push(RelocationTable.Buf(), RelocationTable.GetDataSize());

      // Put file offset of relocations into section header
      NewSectionHeaders[SegNum].PRelocations = FileOffsetData + FileOffset;

      // Put number of relocations into section header
      NewSectionHeaders[SegNum].NRelocations = (uint16)(RelocationTable.GetNumEntries());

      // Put number of relocations into symbol table auxiliary entry.
      // Search for the symbol table entry for this section:
      for (uint32 sym = 0; sym < NewSymbolTable.GetNumEntries(); sym++) {
         if (NewSymbolTable[sym].s.SectionNumber == DesiredSegment
         && NewSymbolTable[sym].s.StorageClass == COFF_CLASS_STATIC 
         && NewSymbolTable[sym].s.NumAuxSymbols == 1) {
            // Found right symbol table entry. Insert NumberOfRelocations
            NewSymbolTable[sym+1].section.NumberOfRelocations = NewSectionHeaders[SegNum].NRelocations;
            break;  // No need to search further
         }
      }
   } // End of loop through segments
}


void COMF2COF::CheckUnsupportedRecords() {
   // Make warnings if file containes unsupported record types
   uint32 RecNum;                                // Record number
   uint32 NumComdat = 0;                         // Number of COMDAT records
   uint32 NumComent = 0;                         // Number of COMENT records

   // Loop through all records
   for (RecNum = 0; RecNum < NumRecords; RecNum++) {
      // Check record type
      switch (Records[RecNum].Type2) {
      case OMF_THEADR: case OMF_MODEND: case OMF_EXTDEF: case OMF_PUBDEF:
      case OMF_LNAMES: case OMF_SEGDEF: case OMF_GRPDEF: case OMF_FIXUPP:
      case OMF_LEDATA: case OMF_LIDATA: case OMF_COMDEF: case OMF_VERNUM:
         // These record types are supported or can safely be ignored
         break;

      case OMF_LINNUM: case OMF_LINSYM:
         // Debug records
         cmd.CountDebugRemoved();  break;

      case OMF_COMDAT: case OMF_LCOMDEF: case OMF_CEXTDEF:
         NumComdat++;  break;                    // Count COMDAT records

      case OMF_COMENT: 
         NumComent++;  break;                    // Count COMENT records

      default:                                   // Warning for unknown record type
         err.submit(1212, COMF::GetRecordTypeName(Records[RecNum].Type2));
      }
   }
   // Report number of unsupported sections found
   if (NumComdat) err.submit(2305, NumComdat);
   if (NumComent) err.submit(1211, NumComent);
}


void COMF2COF::MakeBinaryFile() {
   // Putting sections together
   uint32 i;

   // Get number of symbols and sections into file header
   NewFileHeader.NumberOfSymbols = NewSymbolTable.GetNumEntries();
   NewFileHeader.NumberOfSections = NewSectionHeaders.GetNumEntries();

   // Put file header into new file
   ToFile.Push(&NewFileHeader, sizeof(NewFileHeader));

   // Put section headers into new file
   if (NewSectionHeaders.GetNumEntries()) {
      ToFile.Push(&NewSectionHeaders[0], NewSectionHeaders.GetNumEntries() * sizeof(SCOFF_SectionHeader));
   }

   // Put raw data and relocation tables into new file
   ToFile.Push(NewData.Buf(), NewData.GetDataSize());

   // Get address of symbol table into file header
   ToFile.Get<SCOFF_FileHeader>(0).PSymbolTable = ToFile.GetDataSize();

   // Put symbol table into new file
   for (i = 0; i < NewSymbolTable.GetNumEntries(); i++) {
      ToFile.Push(&NewSymbolTable[i], SIZE_SCOFF_SymTableEntry);
   }

   // Insert string table size
   NewStringTable.Get<uint32>(0) = NewStringTable.GetDataSize();

   // Put string table into new file
   ToFile.Push(NewStringTable.Buf(), NewStringTable.GetDataSize());
}
