/****************************  cof2elf.cpp   ********************************
* Author:        Agner Fog
* Date created:  2006-07-20
* Last modified: 2022-05-21
* Project:       objconv
* Module:        cof2elf.cpp
* Description:
* Module for converting PE/COFF file to ELF file
*
* Copyright 2006-2022 GNU General Public License http://www.gnu.org/licenses
*****************************************************************************/
#include "stdafx.h"


template <class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
CCOF2ELF<ELFSTRUCTURES>::CCOF2ELF () {
   // Constructor
   memset(this, 0, sizeof(*this));
}


template <class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
void CCOF2ELF<ELFSTRUCTURES>::Convert() {
   // Do the conversion
   NumSectionsNew = 5;                                    // Number of sections generated so far

   // Allocate variable size buffers
   MaxSectionsNew    = NumSectionsNew + 2 * NSections;    // Max number of sections needed
   NewSections.SetNum(MaxSectionsNew);                    // Allocate buffers for each section
   NewSections.SetZero();                                 // Initialize
   NewSectionHeaders.SetNum(MaxSectionsNew);              // Allocate array for temporary section headers
   NewSectionHeaders.SetZero();                           // Initialize
   NewSectIndex.SetNum(NSections);                        // Array for translating old section index (0-based) to new section index
   NewSectIndex.SetZero();                                // Initialize
   NewSymbolIndex.SetNum(NumberOfSymbols);                // Array of new symbol indices
   NewSymbolIndex.SetZero();                              // Initialize

   // Call the subfunctions
   ToFile.SetFileType(FILETYPE_ELF);   // Set type of to file
   MakeSegments();                     // Make segment headers and code/data segments
   MakeSymbolTable();                  // Symbol table and string tables
   MakeRelocationTables();             // Relocation tables
   MakeBinaryFile();                   // Putting sections together
   *this << ToFile;                    // Take over new file buffer
}


template <class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
void CCOF2ELF<ELFSTRUCTURES>::MakeSegments() {
   // Convert subfunction: Make segment headers and code/data segments
   TELF_SectionHeader NewSecHeader;    // New section header
   int oldsec;                         // Section index in old file
   int newsec;                         // Section index in new file
   uint32_t SecNameIndex;                // Section name index into shstrtab
   char const * SecName;               // Name of new section
   const int WordSize = sizeof(NewFileHeader.e_entry) * 8; // word size 32 or 64 bits

   // Special segment names
   static const char * SpecialSegmentNames[] = {
      "Null", ".symtab", ".shstrtab", ".strtab", ".stabstr"
   };
   // Indexes to these are:
   symtab      = 1;               // Symbol table section number
   shstrtab    = 2;               // Section name string table section number
   strtab      = 3;               // Object name string table section number
   stabstr     = 4;               // Debug string table section number

   // Number of special segments = number of names in SpecialSegmentNames:
   const int NumSpecialSegments = sizeof(SpecialSegmentNames)/sizeof(SpecialSegmentNames[0]);

   // Make first section header string table entry empty
   NewSections[shstrtab].PushString("");

   // Loop through special sections, except the first Null section:
   for (newsec = 0; newsec < NumSpecialSegments; newsec++) {
      // Put data into new section header:
      // Initialize to zero
      memset(&NewSecHeader, 0, sizeof(NewSecHeader));

      if (newsec > 0) {
         // Put name into section header string table
         SecName = SpecialSegmentNames[newsec];
         SecNameIndex = NewSections[shstrtab].PushString(SecName);

         // Put name into new section header
         NewSecHeader.sh_name = SecNameIndex;
      }

      // Put section header into temporary buffer
      NewSectionHeaders[newsec] = NewSecHeader;
   }

   // Put type, flags, etc. into special segments:
   NewSectionHeaders[symtab]  .sh_type  = SHT_SYMTAB;
   NewSectionHeaders[symtab]  .sh_entsize = sizeof(TELF_Symbol);
   NewSectionHeaders[symtab]  .sh_link  = strtab;
   NewSectionHeaders[shstrtab].sh_type  = SHT_STRTAB;
   NewSectionHeaders[shstrtab].sh_flags = SHF_STRINGS;
   NewSectionHeaders[shstrtab].sh_addralign = 1;
   NewSectionHeaders[strtab]  .sh_type  = SHT_STRTAB;
   NewSectionHeaders[strtab]  .sh_flags = SHF_STRINGS;
   NewSectionHeaders[strtab]  .sh_addralign = 1;
   NewSectionHeaders[stabstr] .sh_type  = SHT_STRTAB;
   NewSectionHeaders[stabstr] .sh_flags = SHF_STRINGS;
   NewSectionHeaders[stabstr] .sh_addralign = 1;

   if (newsec != NumSectionsNew) {
      // Check my program for internal consistency
      // If you get this error then change the value of NumSectionsNew in 
      // the constructor CCOF2ELF::CCOF2ELF to equal the number of entries in 
      // SpecialSegmentNames, including the Null segment
      err.submit(9000);
   }

   // Loop through source file sections
   for (oldsec = 0; oldsec < this->NSections; oldsec++) {

      // Pointer to old section header
      SCOFF_SectionHeader * SectionHeader = &this->SectionHeaders[oldsec];

      // Get section name
      SecName = this->GetSectionName(SectionHeader->Name);
      if (strnicmp(SecName,"debug",5) == 0 || strnicmp(SecName+1,"debug",5) == 0) {
         // This is a debug section
         if (cmd.DebugInfo == CMDL_DEBUG_STRIP) {
            // Remove debug info
            NewSectIndex[oldsec] = COFF_SECTION_REMOVE_ME;  // Remember that this section is removed
            cmd.CountDebugRemoved();
            continue;
         }
         else if (cmd.InputType != cmd.OutputType) {
            err.submit(1029); // Warn that debug information is incompatible
         }
      }
      if (strnicmp(SecName,".drectve",8) == 0 || (SectionHeader->Flags & (PE_SCN_LNK_INFO | PE_SCN_LNK_REMOVE))) {
         // This is a directive section
         if (cmd.ExeptionInfo) {
            // Remove directive section
            NewSectIndex[oldsec] = COFF_SECTION_REMOVE_ME;  // Remember that this section is removed
            cmd.CountExceptionRemoved();
            continue;
         }
      }
      if (strnicmp(SecName,".pdata", 6) == 0) {
         // This section has exception information
         if (cmd.ExeptionInfo == CMDL_EXCEPTION_STRIP) {
            // Remove exception info
            NewSectIndex[oldsec] = COFF_SECTION_REMOVE_ME;  // Remember that this section is removed
            cmd.CountExceptionRemoved();
            continue;
         }
         else if (cmd.InputType != cmd.OutputType) {
            err.submit(1030); // Warn that exception information is incompatible
         }
      }
      
      if (strnicmp(SecName,".cormeta", 8) == 0) {
         // This is a .NET Common Language Runtime section
         err.submit(2014);
      }
      if (strnicmp(SecName,".rsrc", 5) == 0) {
         // This section has Windows resource information
         err.submit(1031);
      }

      // Store section index in index translation table (zero-based index)
      NewSectIndex[oldsec] = newsec;

      // Store section data
      if (SectionHeader->SizeOfRawData > 0) {
         NewSections[newsec].Push(Buf()+SectionHeader->PRawData, SectionHeader->SizeOfRawData);
      }

      // Put data into new section header:
      // Initialize to zero
      memset(&NewSecHeader, 0, sizeof(NewSecHeader));

      // Section type
      if (!(SectionHeader->Flags & PE_SCN_LNK_REMOVE)) {
         NewSecHeader.sh_type = SHT_PROGBITS;  // Program code or data
         NewSecHeader.sh_flags |= SHF_ALLOC;   // Occupies memory during execution
      }
      if (SectionHeader->Flags & PE_SCN_CNT_UNINIT_DATA) {
         NewSecHeader.sh_type = SHT_NOBITS;    // BSS
      }

      // Section flags
      if (SectionHeader->Flags & PE_SCN_MEM_WRITE) {
         NewSecHeader.sh_flags |= SHF_WRITE;
      }
      if (SectionHeader->Flags & PE_SCN_MEM_EXECUTE) {
         NewSecHeader.sh_flags |= SHF_EXECINSTR;
      }

      // Check for special sections
      if (strcmp(SecName, COFF_CONSTRUCTOR_NAME)==0) {
         // Constructors segment
         SecName = ELF_CONSTRUCTOR_NAME;
         NewSecHeader.sh_flags = SHF_WRITE | SHF_ALLOC;
      }

      // Put name into section header string table
      SecNameIndex = NewSections[shstrtab].PushString(SecName);

      // Put name into new section header
      NewSecHeader.sh_name = SecNameIndex;

      // Section virtual memory address
      NewSecHeader.sh_addr = SectionHeader->VirtualAddress;

      // Section size in memory
      NewSecHeader.sh_size = SectionHeader->VirtualSize;

      // Section alignment
      if (SectionHeader->Flags & PE_SCN_ALIGN_MASK) {
         NewSecHeader.sh_addralign = uint32_t(1 << (((SectionHeader->Flags & PE_SCN_ALIGN_MASK) / PE_SCN_ALIGN_1) - 1));
      }

      // Put section header into temporary buffer
      NewSectionHeaders[newsec] = NewSecHeader;

      // Increment section number
      newsec++;

      if (SectionHeader->NRelocations > 0) {
         // Source section has relocations. 
         // Make a relocation section in destination file

         // Put data into relocation section header:
         // Initialize to zero
         memset(&NewSecHeader, 0, sizeof(NewSecHeader));

         // Name for relocation section = ".rel" or ".rela" + name of section
         const int MAXSECTIONNAMELENGTH = 256;
         char RelocationSectionName[MAXSECTIONNAMELENGTH] = ".rel";
         if (WordSize == 64) strcat(RelocationSectionName, "a"); // 32-bit: .rel, 64-bit: .rela

         strncat(RelocationSectionName, SecName, MAXSECTIONNAMELENGTH-5);
         RelocationSectionName[MAXSECTIONNAMELENGTH-1] = 0;

         // Put name into section header string table
         uint32_t SecNameIndex = NewSections[shstrtab].PushString(RelocationSectionName);

         // Put name into new section header
         NewSecHeader.sh_name = SecNameIndex;

         // Section type
         NewSecHeader.sh_type = (WordSize == 32) ? SHT_REL : SHT_RELA;  // Relocation section

         // Put section header into temporary buffer
         NewSectionHeaders[newsec] = NewSecHeader;

         // Increment section number
         newsec++;
      }
   }
   // Number of sections generated
   NumSectionsNew = newsec;
}


template <class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
void CCOF2ELF<ELFSTRUCTURES>::MakeSymbolTable() {
   // Convert subfunction: Make symbol table and string tables
   int isym;                           // current symbol table entry
   int numaux;                         // Number of auxiliary entries in source record
   int OldSectionIndex;                // Index into old section table. 1-based
   int NewSectionIndex;                // Index into new section table. 0-based
   //const int WordSize = sizeof(NewFileHeader.e_entry) * 8; // word size 32 or 64 bits

   TELF_Symbol sym;                    // Temporary symbol table record
   const char * name1;                 // Name of section or main record

   // Pointer to old symbol table
   union {
      SCOFF_SymTableEntry * p;         // Symtab entry pointer
      int8_t * b;                        // Used for increment
   } OldSymtab;

   // Make the first record empty
   NewSections[symtab].Push(0, sizeof(TELF_Symbol));

   // Make first string table entries empty
   NewSections[strtab] .PushString("");
   NewSections[stabstr].PushString("");

   // Loop twice through source symbol table to get local symbols first, global symbols last
   // Loop 1: Look for local symbols only
   OldSymtab.p = SymbolTable; // Pointer to source symbol table
   for (isym = 0; isym < this->NumberOfSymbols; isym += numaux+1, OldSymtab.b += SIZE_SCOFF_SymTableEntry*(numaux+1)) {

      if (OldSymtab.b >= Buf() + DataSize) {
         err.submit(2040);
         break;
      }

      // Number of auxiliary records belonging to same symbol
      numaux = OldSymtab.p->s.NumAuxSymbols;  if (numaux < 0) numaux = 0;

      if (OldSymtab.p->s.StorageClass != COFF_CLASS_EXTERNAL && OldSymtab.p->s.StorageClass != COFF_CLASS_WEAK_EXTERNAL) {
         // Symbol is local

         // Reset destination entry
         memset(&sym, 0, sizeof(sym));

         // Binding
         sym.st_bind = STB_LOCAL;

         // Get first aux record if numaux > 0
         //SCOFF_SymTableEntryAux * sa = (SCOFF_SymTableEntryAux *)(OldSymtab.b + SIZE_SCOFF_SymTableEntry);

         // Symbol name
         name1 = this->GetSymbolName(OldSymtab.p->s.Name);

         // Symbol value
         sym.st_value = OldSymtab.p->s.Value;

         // Get section
         OldSectionIndex = OldSymtab.p->s.SectionNumber;  // 1-based index into old section table
         NewSectionIndex = 0;                 // 0-based index into old section table
         if (OldSectionIndex > 0 && OldSectionIndex <= this->NSections) {
            // Subtract 1 from OldSectionIndex because NewSectIndex[] is zero-based while OldSectionIndex is 1-based
            // Get new section index from translation table
            NewSectionIndex = NewSectIndex[OldSectionIndex-1]; 
         }
         if (NewSectionIndex == COFF_SECTION_REMOVE_ME) {
            continue; // Section has been removed. Remove symbol too
         }

         sym.st_shndx = (uint16_t)NewSectionIndex;

         // Check symbol type
         if (OldSymtab.p->s.StorageClass == COFF_CLASS_FILE) {
            // This is a filename record
            if (numaux > 0 && numaux < 20) {
               // Get filename from subsequent Aux records.
               // Remove path from filename because the path makes no sense on a different platform.
               const char * filename = GetShortFileName(OldSymtab.p);
               // Put file name into string table and debug string table
               sym.st_name = NewSections[strtab].PushString(filename);
               NewSections[stabstr].PushString(filename);
            }
            // Attributes for filename record
            sym.st_shndx  = (uint16_t)SHN_ABS;
            sym.st_type   = STT_FILE;
            sym.st_bind   = STB_LOCAL;
            sym.st_value  = 0;
         }
         else if (numaux && OldSymtab.p->s.StorageClass == COFF_CLASS_STATIC
         && OldSymtab.p->s.Value == 0 && OldSymtab.p->s.Type != 0x20) {
            // This is a section definition record
            sym.st_name  = 0;  name1 = 0;
            sym.st_type  = STT_SECTION;
            sym.st_bind  = STB_LOCAL;
            sym.st_value = 0;
            // aux record contains length and number of relocations. Ignore aux record
         }
         else if (OldSymtab.p->s.SectionNumber < 0) {
            // This is an absolute or debug symbol
            sym.st_type  = STT_NOTYPE;
            sym.st_shndx = (uint16_t)SHN_ABS;
         }
         else if (OldSymtab.p->s.Type == 0 && OldSymtab.p->s.StorageClass == COFF_CLASS_FUNCTION) {
            // This is a .bf, .lf, or .ef record following a function record
            // Contains line number information etc. Ignore this record
            continue;
         }
         else if (OldSymtab.p->s.SectionNumber <= 0) {
            // Unknown
            sym.st_type = STT_NOTYPE;
         }
         else {
            // This is a local data definition record
            sym.st_type = STT_OBJECT;
            // The size is not specified in COFF record,
            // so we may give it an arbitrary size:
            // sym.size = 4;
         }

         // Put symbol name into string table if we have not already done so
         if (sym.st_name == 0 && name1) {
            sym.st_name = NewSections[strtab].PushString(name1);
         }

         // Put record into new symbol table
         NewSections[symtab].Push(&sym, sizeof(sym));

         // Insert into symbol translation table
         NewSymbolIndex[isym] = NewSections[symtab].GetLastIndex();

      } // End if not external
   }  // End loop 1

   // Finished with local symbols
   // Make index to first global symbol
   NewSectionHeaders[symtab].sh_info = NewSections[symtab].GetLastIndex() + 1;

   // Loop 2: Look for global symbols only
   OldSymtab.p = SymbolTable; // Pointer to source symbol table
   for (isym = 0; isym < NumberOfSymbols; isym += numaux+1, OldSymtab.b += SIZE_SCOFF_SymTableEntry*(numaux+1)) {

      // Number of auxiliary records belonging to same symbol
      numaux = OldSymtab.p->s.NumAuxSymbols;  if (numaux < 0) numaux = 0;

      if (OldSymtab.p->s.StorageClass == COFF_CLASS_EXTERNAL || OldSymtab.p->s.StorageClass == COFF_CLASS_WEAK_EXTERNAL) {
         // Symbol is global (public or external)

         // Reset destination entry
         memset(&sym, 0, sizeof(sym));

         // Binding
         sym.st_bind = STB_GLOBAL;
         if (OldSymtab.p->s.StorageClass == COFF_CLASS_WEAK_EXTERNAL) sym.st_bind = STB_WEAK;

         // Get first aux record if numaux > 0
         SCOFF_SymTableEntry * sa = (SCOFF_SymTableEntry*)(OldSymtab.b + SIZE_SCOFF_SymTableEntry);

         // Symbol name
         name1 = GetSymbolName(OldSymtab.p->s.Name);

         // Symbol value
         sym.st_value = OldSymtab.p->s.Value;

         // Get section
         OldSectionIndex = OldSymtab.p->s.SectionNumber; // 1-based index into old section table
         NewSectionIndex = 0;                          // 0-based index into old section table
         if (OldSectionIndex > 0 && OldSectionIndex <= NSections) {
            // Subtract 1 from OldSectionIndex because NewSectIndex[] is zero-based while OldSectionIndex is 1-based
            // Get new section index from translation table
            NewSectionIndex = NewSectIndex[OldSectionIndex-1]; 
         }
         if (NewSectionIndex == COFF_SECTION_REMOVE_ME) {
            continue; // Section has been removed. Remove symbol too
         }
         if ((int16_t)OldSectionIndex == COFF_SECTION_ABSOLUTE) {
            NewSectionIndex = SHN_ABS;
         }

         sym.st_shndx = (uint16_t)NewSectionIndex;

         // Check symbol type
         if (OldSymtab.p->s.SectionNumber < 0) {
            // This is an absolute or debug symbol
            sym.st_type = STT_NOTYPE;
         }
         else if (OldSymtab.p->s.Type == COFF_TYPE_FUNCTION && OldSymtab.p->s.SectionNumber > 0) {
            // This is a function definition record
            sym.st_type = STT_FUNC;
            if (numaux) {
               // Get size from aux record
               sym.st_size = sa->func.TotalSize;
            }
            if (sym.st_size == 0) {
               // The size is not specified in the COFF file. 
               // We may give it an arbitrary size:
               // sym.size = 1;
            }
         }
         else if (OldSymtab.p->s.SectionNumber <= 0) {
            // This is an external symbol
            sym.st_type = STT_NOTYPE;
         }
         else {
            // This is a data definition record
            sym.st_type = STT_OBJECT;
            // Symbol must have a size. The size is not specified in COFF record,
            // so we just give it an arbitrary size
            sym.st_size = 4;
         }

         // Put symbol name into string table if we have not already done so
         if (sym.st_name == 0 && name1) {
            sym.st_name = NewSections[strtab].PushString(name1);
         }

         // Put record into new symbol table
         NewSections[symtab].Push(&sym, sizeof(sym));

         // Insert into symbol translation table
         NewSymbolIndex[isym] = NewSections[symtab].GetLastIndex();

      } // End if external
   }  // End loop 2
}


template <class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
void CCOF2ELF<ELFSTRUCTURES>::MakeRelocationTables() {
   // Convert subfunction: Relocation tables
   int32_t oldsec;                                 // Relocated section number in source file
   int32_t newsec;                                 // Relocated section number in destination file
   int32_t newsecr;                                // Relocation table section number in destination file
   TELF_SectionHeader * NewRelTableSecHeader;    // Section header for new relocation table
   char TempText[32];                            // Temporary text buffer
   const int WordSize = sizeof(NewFileHeader.e_entry) * 8; // word size 32 or 64 bits

   // Loop through source file sections
   for (oldsec = 0; oldsec < NSections; oldsec++) {

      // New section index
      newsec = NewSectIndex[oldsec];
      if (newsec == COFF_SECTION_REMOVE_ME) {
         continue;   // This is a debug or exception handler section which has been removed
      }

      // Pointer to old section header
      SCOFF_SectionHeader * SectionHeader = &this->SectionHeaders[oldsec];

      if (SectionHeader->NRelocations > 0) {
         // This section has relocations

         // Finc new relocation table section
         newsecr = newsec + 1;

         // Check that we have allocated a relocation section
         if (oldsec+1 < this->NSections && NewSectIndex[oldsec+1] == newsecr) err.submit(9000);
         if (newsecr >= NumSectionsNew) err.submit(9000);

         // New relocation table section header
         NewRelTableSecHeader = &NewSectionHeaders[newsecr];

         // Insert header info
         NewRelTableSecHeader->sh_type  = (WordSize == 32) ? SHT_REL : SHT_RELA;
         NewRelTableSecHeader->sh_flags = 0;
         NewRelTableSecHeader->sh_addralign = WordSize / 8; // Alignment
         NewRelTableSecHeader->sh_link = symtab; // Point to symbol table
         NewRelTableSecHeader->sh_info = newsec; // Point to relocated section
         // Entry size:
         NewRelTableSecHeader->sh_entsize = (WordSize == 32) ? sizeof(Elf32_Rel) : sizeof(Elf64_Rela);

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

         // Loop through relocations

         OldReloc.b = Buf() + SectionHeader->PRelocations;
         for (int i = 0; i < SectionHeader->NRelocations; i++, OldReloc.b += SIZE_SCOFF_Relocation) {

            // Make new relocation entry and set to zero
            TELF_Relocation NewRelocEntry;
            memset(&NewRelocEntry, 0, sizeof(NewRelocEntry));

            // Section offset of relocated address
            NewRelocEntry.r_offset = OldReloc.p->VirtualAddress;

            // Target symbol
            uint32_t TargetSymbol = OldReloc.p->SymbolTableIndex;
            if (TargetSymbol >= (uint32_t)NumberOfSymbols) {
               err.submit(2031);  // Symbol not in table
            }
            else {  // Translate symbol number
               NewRelocEntry.r_sym = NewSymbolIndex[TargetSymbol];
            }

            if (WordSize == 32) {
               // Interpret 32-bit COFF relocation types
               switch (OldReloc.p->Type) {
               case COFF32_RELOC_ABS:     // Ignored
                  NewRelocEntry.r_type = R_386_NONE;  break;

               case COFF32_RELOC_TOKEN:   // .NET common language runtime token
                  err.submit(2014);       // Error message
                  // Continue in next case and insert absolute address as token:
               case COFF32_RELOC_DIR32:   // 32-bit absolute virtual address
                  NewRelocEntry.r_type = R_386_32;  break;

               case COFF32_RELOC_IMGREL:  // 32-bit image relative address
                  // Image-relative relocation not supported in ELF
                  if (cmd.OutputType == FILETYPE_MACHO_LE) {
                     // Intermediate during conversion to MachO
                     NewRelocEntry.r_type = R_UNSUPPORTED_IMAGEREL;
                     break;
                  }
                  // Work-around unsupported image-relative relocation
                  // Convert to absolute
                  NewRelocEntry.r_type = R_386_32; // Absolute relocation
                  if (cmd.ImageBase == 0) {
                     // Default image base for 32-bit Linux
                     cmd.ImageBase = 0x8048000; // 0x400000 ?
                  }
                  NewRelocEntry.r_addend -= cmd.ImageBase;
                  // Warn that image base must be set to the specified value
                  sprintf(TempText, "%X", cmd.ImageBase); // write value as hexadecimal
                  err.submit(1301, TempText);  err.ClearError(1301);
                  break;

               case COFF32_RELOC_REL32:   // 32-bit self-relative
                  NewRelocEntry.r_type = R_386_PC32;
                  // Difference between EIP-relative and self-relative relocation = size of address field
                  NewRelocEntry.r_addend = -4;  break; 
                  /* !! error  if self-relative relocation with offset
                   !! test data that fails = testpic32.obj */

               case COFF32_RELOC_SECTION:   // 16-bit section index in file
               case COFF32_RELOC_SECREL:    // 32-bit section-relative
               case COFF32_RELOC_SECREL7:   //  8-bit section-relative
                  // These fixup types are not supported in ELF files
                  if (cmd.DebugInfo != CMDL_DEBUG_STRIP) {
                     // Issue warning. Ignore if stripping debug info
                     err.submit(1010);
                  }
                  break;
                  
               default:
                  err.submit(2030, OldReloc.p->Type);  break; // Error: Unknown relocation type (%i) ignored
               }
            }
            else {
               // Interpret 64-bit COFF relocation types
               switch (OldReloc.p->Type) {
               case COFF64_RELOC_ABS:        // Ignored
                  NewRelocEntry.r_type = R_X86_64_NONE;  break;

               case COFF64_RELOC_TOKEN:      // .NET common language runtime token
                  err.submit(2014);          // Error message
                  // Continue in next case and insert absolute address as token:

               case COFF64_RELOC_ABS64:      // 64 bit absolute virtual address
                  NewRelocEntry.r_type = R_X86_64_64;  break;

               case COFF64_RELOC_PPC_TOKEN:
                  err.submit(2014);          // Error message
                  // Continue in next case and insert absolute address as token:

               case COFF64_RELOC_ABS32:      // 32 bit absolute address
                  NewRelocEntry.r_type = R_X86_64_32S;  break;

               case COFF64_RELOC_IMGREL:     // 32 bit image-relative
                  // Image-relative relocation not supported in ELF
                  if (cmd.OutputType == FILETYPE_MACHO_LE) {
                     // Intermediate during conversion to MachO
                     NewRelocEntry.r_type = R_UNSUPPORTED_IMAGEREL;
                     break;
                  }
                  // Work-around unsupported image-relative relocation
                  // Convert to absolute
                  NewRelocEntry.r_type = R_X86_64_32S; // Absolute 32-bit relocation
                  if (cmd.ImageBase == 0) {
                     // Default image base for 64-bit Linux
                     cmd.ImageBase = 0x400000;
                  }
                  NewRelocEntry.r_addend -= cmd.ImageBase;
                  // Warn that image base must be set to the specified value
                  sprintf(TempText, "%X", cmd.ImageBase); // write value as hexadecimal
                  err.submit(1301, TempText);  err.ClearError(1301);
                  break;

               case COFF64_RELOC_REL32:      // 32 bit, RIP-relative
               case COFF64_RELOC_REL32_1:    // 32 bit, relative to RIP - 1. For instruction with immediate byte operand
               case COFF64_RELOC_REL32_2:    // 32 bit, relative to RIP - 2. For instruction with immediate word operand
               case COFF64_RELOC_REL32_3:    // 32 bit, relative to RIP - 3. (useless)
               case COFF64_RELOC_REL32_4:    // 32 bit, relative to RIP - 4. For instruction with immediate dword operand
               case COFF64_RELOC_REL32_5:    // 32 bit, relative to RIP - 5. (useless)
                  NewRelocEntry.r_type = R_X86_64_PC32;
                  // Note:
                  // The microprocessor calculates RIP-relative addresses 
                  // relative to the value of the instruction pointer AFTER 
                  // the instruction. This is equal to the address of the 
                  // relocated field plus the size of the relocated field 
                  // itself plus the size of any immediate operand coming 
                  // after the relocated field.
                  // The COFF format makes the correction for this offset in 
                  // the linker by using a differet relocation type for 
                  // immediate operand size = 0, 1, 2 or 4.
                  // The ELF format makes the same correction by an explicit 
                  // addend, which is -4, -5, -6 or -8, respectively.
                  // The difference between RIP-relative and self-relative 
                  // relocation is equal to the size of the address field plus
                  // the size of any immediate operand:
                  NewRelocEntry.r_addend = -(4 + OldReloc.p->Type - COFF64_RELOC_REL32);                  
                  break;

               case COFF64_RELOC_SECTION:   // 16-bit section index in file
               case COFF64_RELOC_SECREL:    // 32-bit section-relative
               case COFF64_RELOC_SECREL7:   //  8-bit section-relative

                  // These fixup types are not supported in ELF files
                  if (cmd.DebugInfo != CMDL_DEBUG_STRIP) {
                     // Issue warning. Ignore if stripping debug info
                     err.submit(1010);
                  }
                  break; 

               default:
                  err.submit(2030, OldReloc.p->Type);  break; // Error: Unknown relocation type (%i) ignored
               }
            }

            // Find inline addend
            int32_t * paddend = 0;
            if (OldReloc.p->VirtualAddress + 4 > NewSections[newsec].GetDataSize()
               || NewSectionHeaders[newsec].sh_type == SHT_NOBITS) {
                  // Address of relocation is invalid
                  err.submit(2032);
               }
            else {
               // Make pointer to inline addend
               paddend = (int32_t*)(NewSections[newsec].Buf() 
                  + NewSectionHeaders[newsec].sh_offset + OldReloc.p->VirtualAddress);
            } 

            // Put relocation record into table
            if (WordSize == 32) {
               if (NewRelocEntry.r_addend != 0) {
                  // Use inline addends in 32 bit ELF (SHT_REL)
                  // Put addend inline
                  if (paddend) *paddend += uint32_t(NewRelocEntry.r_addend);
                  NewRelocEntry.r_addend = 0;
               }

               // Save 32-bit relocation record Elf32_Rel, not Elf32_Rela
               if (NewRelocEntry.r_addend) err.submit(9000);
               NewSections[newsecr].Push(&NewRelocEntry, sizeof(Elf32_Rel));
            }
            else {
               // 64 bit
               /*
               if (*paddend != 0) {
                  // Use explicit addend in 64 bit ELF (SHT_RELA)
                  // Explicit addend may cause link error if it appears to point outside section
                  NewRelocEntry.r_addend += *paddend;
                  *paddend = 0;
               }*/

               // Save 64-bit relocation record. Must be Elf64_Rela
               NewSections[newsecr].Push(&NewRelocEntry, sizeof(Elf64_Rela));
            }
         }
      }
   }
}


template <class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
void CCOF2ELF<ELFSTRUCTURES>::MakeBinaryFile() {
   // Convert subfunction: Make section headers and file header,
   // and combine everything into a single memory buffer.
   int32_t  newsec;              // Section index
   uint32_t SecOffset;           // Section offset in file
   uint32_t SecSize;             // Section size in file
   uint32_t SectionHeaderOffset; // File offset to section headers

   // Set file type in ToFile
   ToFile.SetFileType(FILETYPE_ELF);
   
   // Make space for file header in ToFile, but don't fill data into it yet
   ToFile.Push(0, sizeof(TELF_Header));

   // Loop through new section buffers
   for (newsec = 0; newsec < NumSectionsNew; newsec++) {

      // Size of section
      SecSize = NewSections[newsec].GetDataSize();

      // Put section into ToFile
      SecOffset = ToFile.Push(NewSections[newsec].Buf(), SecSize);

      // Put size and offset into section header
      NewSectionHeaders[newsec].sh_offset = SecOffset;
      NewSectionHeaders[newsec].sh_size   = SecSize;

      // Align before next entry
      ToFile.Align(16);
   }

   // Start offset of section headers
   SectionHeaderOffset = ToFile.GetDataSize();

   // Loop through new section headers
   for (newsec = 0; newsec < NumSectionsNew; newsec++) {

      // Put section header into ToFile
      ToFile.Push(&NewSectionHeaders[newsec], sizeof(TELF_SectionHeader));
   }

   // Make file header
   TELF_Header FileHeader;
   memset(&FileHeader, 0, sizeof(FileHeader)); // Initialize to 0

   // Put file type magic number in
   strcpy((char*)(FileHeader.e_ident), ELFMAG);
   // File class
   FileHeader.e_ident[EI_CLASS] = (WordSize == 32) ? ELFCLASS32 : ELFCLASS64;
   // Data Endian-ness
   FileHeader.e_ident[EI_DATA] = ELFDATA2LSB;
   // ELF version
   FileHeader.e_ident[EI_VERSION] = EV_CURRENT;
   // ABI
   FileHeader.e_ident[EI_OSABI] = ELFOSABI_SYSV;
   // ABI version
   FileHeader.e_ident[EI_ABIVERSION] = 0;
   // File type
   FileHeader.e_type = ET_REL;
   // Machine architecture
   FileHeader.e_machine = (WordSize == 32) ? EM_386 : EM_X86_64;
   // Version
   FileHeader.e_version = EV_CURRENT;
   // Flags
   FileHeader.e_flags = 0;

   // Section header table offset
   FileHeader.e_shoff = SectionHeaderOffset;

   // File header size
   FileHeader.e_ehsize = sizeof(TELF_Header);

   // Section header size
   FileHeader.e_shentsize = sizeof(TELF_SectionHeader);

   // Number of section headers
   FileHeader.e_shnum = (uint16_t)NumSectionsNew;

   // Section header string table index
   FileHeader.e_shstrndx = (uint16_t)shstrtab;

   // Put file header into beginning of ToFile where we made space for it
   memcpy(ToFile.Buf(), &FileHeader, sizeof(FileHeader));
}


// Make template instances for 32 and 64 bits
template class CCOF2ELF<ELF32STRUCTURES>;
template class CCOF2ELF<ELF64STRUCTURES>;
