/****************************  mac2elf.cpp   *********************************
* Author:        Agner Fog
* Date created:  2008-05-15
* Last modified: 2009-05-19
* Project:       objconv
* Module:        mac2elf.cpp
* Description:
* Module for converting Mach-O file to ELF file
*
* Copyright 2008 GNU General Public License http://www.gnu.org/licenses
*****************************************************************************/

#include "stdafx.h"


template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt,
          class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
CMAC2ELF<MACSTRUCTURES,ELFSTRUCTURES>::CMAC2ELF () {
   // Constructor
   memset(this, 0, sizeof(*this));                    // Reset everything
}


template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt,
          class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
void CMAC2ELF<MACSTRUCTURES,ELFSTRUCTURES>::Convert() {
   // Do the conversion
   // Some compilers require this-> for accessing members of template base class,
   // according to the so-called two-phase lookup rule.

   NumSectionsNew = 5;                                    // Number of sections generated so far

   // Allocate variable size buffers
   MaxSectionsNew = NumSectionsNew + 2 * this->NumSections + 2;// Max number of sections needed
   NewSections.SetNum(MaxSectionsNew+1);                  // Allocate buffers for each section
   NewSections.SetZero();                                 // Initialize
   NewSectionHeaders.SetNum(MaxSectionsNew+1);            // Allocate array for temporary section headers
   NewSectionHeaders.SetZero();                           // Initialize
   NewSectIndex.SetNum(this->NumSections+1);              // Array for translating old section index to new section index
   NewSectIndex.SetZero();                                // Initialize
   SectionSymbols.SetNum(this->MaxSectionsNew+1);         // Array of new symbol indices for sections
   SectionSymbols.SetZero();                              // Initialize
   NewSymbolIndex.SetNum(this->SymTabNumber);             // 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(this->FileHeader); // Make relocation tables
   MakeImportTables();                     // Fill import tables
   MakeGOT();                              // Make fake Global Offset Table
   MakeBinaryFile();                       // Putting sections together
   *this << ToFile;                        // Take over new file buffer
}


template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt,
          class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
void CMAC2ELF<MACSTRUCTURES,ELFSTRUCTURES>::MakeSegments() {
   // Convert subfunction: Make segment headers and code/data segments
   TELF_SectionHeader NewSecHeader; // New section header
   uint32_t oldsec;                   // Section index in old file
   uint32_t newsec;                   // Section index in new file
   uint32_t SecNameIndex;             // Section name index into shstrtab
   char const * SecName;            // Name of new section
   const int MAXSECTIONNAMELENGTH = 256;
   char RelocationSectionName[MAXSECTIONNAMELENGTH];
   const int WordSize = sizeof(MInt) * 8;

   // 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 uint32_t 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 to equal the number of entries in 
      // SpecialSegmentNames, including the Null segment
      err.submit(9000);
   }

   // Find sections in old file
   uint32_t icmd;                        // Current load command
   uint32_t command;                     // Load command
   uint32_t cmdsize = 0;                 // Command size

   // Pointer to current position in old file
   uint8_t * currentp = (uint8_t*)(this->Buf() + sizeof(TMAC_header));

   // Loop through file commands
   for (icmd = 1; icmd <= this->FileHeader.ncmds; icmd++, currentp += cmdsize) {
      command = ((MAC_load_command*)currentp) -> cmd;
      cmdsize = ((MAC_load_command*)currentp) -> cmdsize;

      if (command == MAC_LC_SEGMENT || command == MAC_LC_SEGMENT_64) {
         // This is the segment command (there should be only one)
         if ((command == MAC_LC_SEGMENT) ^ (WordSize == 32)) {
            // 32-bit segment in 64-bit file or vice versa
            err.submit(2320);  return;
         }
         if (cmdsize < sizeof(TMAC_segment_command)) {
            // Zero cmdsize or too small
            err.submit(2321); return;
         }
         // Point to segment command
         TMAC_segment_command * sh = (TMAC_segment_command*)currentp;

         if (stricmp(sh->segname, MAC_SEG_OBJC) == 0) {
            // objective-C runtime segment
            err.submit(2021);  continue;
         }

         // Find first section header
         TMAC_section * sectp = (TMAC_section*)(currentp + sizeof(TMAC_segment_command));

         // Loop through section headers
         for (oldsec = 1; oldsec <= this->NumSections; oldsec++, sectp++) {

            // Get section name
            SecName = sectp->sectname;

            // Check for special section names
            if (stricmp(SecName,"__eh_frame") == 0) {
               // This is an exception handler section
               if (cmd.ExeptionInfo == CMDL_EXCEPTION_STRIP) {
                  // Remove exception handler section
                  cmd.CountExceptionRemoved();
                  continue;
               }
               else if (cmd.InputType != cmd.OutputType) {
                  err.submit(1030); // Warn that exception information is incompatible
               }
            }
            if (sectp->flags & MAC_S_ATTR_DEBUG) {
               // This section has debug information
               if (cmd.DebugInfo == CMDL_DEBUG_STRIP) {
                  // Remove debug info
                  cmd.CountDebugRemoved();
                  continue;
               }
               else if (cmd.InputType != cmd.OutputType) {
                  err.submit(1029); // Warn that debug information is incompatible
               }
            }

            // Store section index in index translation table
            NewSectIndex[oldsec] = newsec;

            // Store section data
            if (sectp->size > 0 && !((sectp->flags & MAC_SECTION_TYPE) == MAC_S_ZEROFILL || (sectp->flags & MAC_SECTION_TYPE)==MAC_S_GB_ZEROFILL)) {
               NewSections[newsec].Push(this->Buf()+sectp->offset, uint32_t(sectp->size));
            }

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

            uint32_t type = sectp->flags & MAC_SECTION_TYPE;
            uint32_t attributes = sectp->flags & MAC_SECTION_ATTRIBUTES;

            // Section type
            if (type == MAC_S_ZEROFILL || type == MAC_S_GB_ZEROFILL) {
               // BSS section
               NewSecHeader.sh_type = SHT_NOBITS;    // BSS
            }
            else {
               // Normal code or data section
               NewSecHeader.sh_type = SHT_PROGBITS;  // Program code or data
            }

            // Section flags
            NewSecHeader.sh_flags |= SHF_ALLOC;      // Occupies memory during execution
            if (attributes & (MAC_S_ATTR_SOME_INSTRUCTIONS | MAC_S_ATTR_PURE_INSTRUCTIONS)) {
               // Executable
               NewSecHeader.sh_flags |= SHF_EXECINSTR;
            }
            else {
               switch (type) {
               case MAC_S_CSTRING_LITERALS:
               case MAC_S_4BYTE_LITERALS:
               case MAC_S_8BYTE_LITERALS:
               case MAC_S_16BYTE_LITERALS:
               case MAC_S_LITERAL_POINTERS:
                  // not writeable
                  break;
               default:
                  // writeable
                  NewSecHeader.sh_flags |= SHF_WRITE;
                  break;
               }
            }

            // Check for special sections
            if (strcmp(SecName, MAC_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 = sectp->addr;

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

            // Section alignment
            NewSecHeader.sh_addralign = uint32_t(1 << sectp->align);

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

            // Increment section number
            newsec++;

            // Check if section is import table
            int SectionType   = sectp->flags & MAC_SECTION_TYPE;
            int IsImportTable = SectionType >= MAC_S_NON_LAZY_SYMBOL_POINTERS && SectionType <= MAC_S_SYMBOL_STUBS;

            if (sectp->nreloc > 0 || IsImportTable) {
               // 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
               if (WordSize == 32) {
                  strcpy(RelocationSectionName, ".rel");
               }
               else {
                  strcpy(RelocationSectionName, ".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

               // Entry size
               NewSecHeader.sh_entsize = (WordSize == 32) ? sizeof(Elf32_Rel) : sizeof(Elf64_Rela);  // Relocation section

               // Section alignment
               NewSecHeader.sh_addralign = WordSize / 8;

               // Link to the section it relocates for
               NewSecHeader.sh_info = newsec - 1;

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

               // Increment section number
               newsec++;

               // Check if there are any GOT relocations
               // Pointer to old relocation entry
               if (sectp->reloff >= this->GetDataSize()) {err.submit(2035); break;}
               MAC_relocation_info * relp = (MAC_relocation_info*)(this->Buf() + sectp->reloff);
               // Loop through old relocations
               for (uint32_t oldr = 1; oldr <= sectp->nreloc; oldr++, relp++) {
                  uint32_t RType = relp->r_type;         // relocation type
                  // No scattered relocation in 64-bit mode. GOT only in 64-bit mode
                  if (WordSize == 64 && (RType == MAC64_RELOC_GOT_LOAD || RType == MAC64_RELOC_GOT)) {
                     HasGOT++;
                  }
               }
            }
         }

         // Check if GOT needed
         if (HasGOT && WordSize == 64) {
            // Make a fake Global Offset Table
            FakeGOTSection = newsec;

            // Put name and data into section header
            memset(&NewSecHeader, 0, sizeof(NewSecHeader));
            SecNameIndex = NewSections[shstrtab].PushString("_fakeGOT");
            NewSecHeader.sh_name = SecNameIndex;
            NewSecHeader.sh_type = SHT_PROGBITS; // Type
            NewSecHeader.sh_flags = SHF_ALLOC;   // Flags
            NewSecHeader.sh_addralign = 8;       // Alignment

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

            // Make relocation section for fake GOT
            memset(&NewSecHeader, 0, sizeof(NewSecHeader));
            // Put name and data into section header
            SecNameIndex = NewSections[shstrtab].PushString("_rela.fakeGOT");
            NewSecHeader.sh_name = SecNameIndex;
            NewSecHeader.sh_type = SHT_RELA;     // Type
            NewSecHeader.sh_flags = 0;           // Flags
            NewSecHeader.sh_addralign = 8;       // Alignment
            NewSecHeader.sh_entsize = sizeof(Elf64_Rela);  // Entry size
            NewSecHeader.sh_info = newsec - 1;   // Link to the section it relocates for
            NewSecHeader.sh_link = symtab;       // Link to symbol table
            // Put section header into temporary buffer
            NewSectionHeaders[newsec++] = NewSecHeader;
         }
         // Number of sections generated
         NumSectionsNew = newsec;
      }
   }
}


template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt,
          class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
void CMAC2ELF<MACSTRUCTURES,ELFSTRUCTURES>::MakeSymbolTable() {
   // Convert subfunction: Make symbol table and string tables
   uint32_t isym;            // current old symbol table entry
   uint32_t OldSectionIndex; // Index into old section table. 1-based
   uint32_t NewSectionIndex; // Index into new section table. 0-based
   const char * name1;     // Name of symbol
   TELF_Symbol sym;        // Temporary symbol table record
   uint32_t DebugRemoved = 0;// Debug symbols removed

   // pointer to old string table
   char * oldstringtab = (char*)(this->Buf() + this->StringTabOffset); 

   // pointer to old symbol table
   TMAC_nlist * symp0, *symp;
   symp0 = (TMAC_nlist*)(this->Buf() + this->SymTabOffset);

   // Check within range
   if (this->SymTabOffset + this->SymTabNumber * sizeof(TMAC_nlist) > this->DataSize) {
      err.submit(2040);  return;
   }

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

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

   // Make symbol records for the start of each section in case they are needed
   // by section-relative relocations (r_extern = 0 in MAC_relocation_info)
   for (uint32_t sec = 1; sec < NumSectionsNew; sec++) {
      uint32_t type = NewSectionHeaders[sec].sh_type;
      if (type == SHT_PROGBITS || type == SHT_NOBITS) {
         // Make unnamed symbol table entry for this section
         memset(&sym, 0, sizeof(sym));
         sym.st_shndx = sec;
         sym.st_type = STT_SECTION;
         // Put record into new symbol table
         NewSections[symtab].Push(&sym, sizeof(sym));
         // Insert into section symbol translation table
         SectionSymbols[sec] = NewSections[symtab].GetLastIndex();
      }
   }

   // Loop through old symbol table. Local symbols first, global symbols last
   for (isym = 0, symp = symp0; isym < this->SymTabNumber; isym++, symp++) {

      if ((symp->n_type & MAC_N_STAB) && (cmd.DebugInfo & CMDL_DEBUG_STRIP)) {
         // Debug symbol should be removed
         DebugRemoved++;  continue;
      }

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

      // Get binding
      if (isym < this->iextdefsym) {
         // Local
         sym.st_bind = STB_LOCAL;
      }
      else if (symp->n_desc & (MAC_N_WEAK_REF | MAC_N_WEAK_DEF)) {
         // Weak public or weak external
         sym.st_bind = STB_WEAK;
      }
      else {
         // Global (public or external)
         sym.st_bind = STB_GLOBAL;
      }

      // Symbol name
      if (symp->n_strx < this->StringTabSize) {
         name1 = oldstringtab + symp->n_strx;
      }
      else {
         err.submit(2112);  break;
      }
         
      // Symbol value
      sym.st_value = symp->n_value;
         
      // Get section
      OldSectionIndex = symp->n_sect;
      if (OldSectionIndex > this->NumSections) {
         err.submit(2016); break;
      }
      // Get new section index
      NewSectionIndex = 0;
      if (OldSectionIndex > 0) {
         // Get new section index from translation table
         NewSectionIndex = NewSectIndex[OldSectionIndex];
         // Change symbol address to section-relative
         // (Also in 64-bit mode)
         sym.st_value -= NewSectionHeaders[NewSectionIndex].sh_addr;
      }
      sym.st_shndx = (uint16_t)NewSectionIndex;

      if (OldSectionIndex && !NewSectionIndex) {
         // Section has been removed. Remove symbol also
         continue;
      }

      // Check symbol type
      int32_t RefType = symp->n_desc & MAC_REF_TYPE;
      if (RefType == MAC_REF_FLAG_UNDEFINED_LAZY || RefType == MAC_REF_FLAG_PRIVATE_UNDEFINED_LAZY) {
         // Lazy binding
         err.submit(1061, name1);
      }
      else if ((symp->n_type & MAC_N_TYPE) == MAC_N_ABS) {
         // Absolute symbol
         sym.st_type  = STT_NOTYPE;
         sym.st_shndx = (uint16_t)SHN_ABS;
         if (sym.st_bind == STB_LOCAL) {
            continue; // Remove absolute local symbol (not allowed in COFF)
         }
      }
      else if (sym.st_shndx == 0) {  // added by Vladimir 'phcoder' Serbinenko:
         // This is an external
         sym.st_type = STT_NOTYPE;
      }		  
      else {
         // This is a data definition record
         if (NewSectionHeaders[NewSectionIndex].sh_flags & SHF_EXECINSTR) {
            // Code section, assume this is a function
            sym.st_type = STT_FUNC;
         }
         else {
            // This is a data object
            sym.st_type = STT_OBJECT;
         }
         if (sym.st_bind == STB_GLOBAL && NewSectionIndex) {
            // Symbol is public
            // The size is not specified in Mac record,
            // so we may give it an arbitrary size:
            sym.st_size = 4;
         }
      }

      // Put symbol name into string table
      if (name1 && *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();

      // Make index to first global symbol
      if (isym >= this->iextdefsym && !NewSectionHeaders[symtab].sh_info) {
         // This is the first global symbol
         NewSectionHeaders[symtab].sh_info = NewSymbolIndex[isym];
      }
   }
}


template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt,
          class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
void CMAC2ELF<MACSTRUCTURES,ELFSTRUCTURES>::MakeRelocationTables(MAC_header_32&) {
   // Convert subfunction: Relocation tables, 32-bit version

   uint32_t oldsec;        // Relocated section number in source file
   uint32_t newsec;        // Relocated section number in destination file
   uint32_t newsecr;       // Relocation table section number in destination file
   MInt   SectAddr;      // Section address of relocation source
   MInt   SourceAddress; // Address of relocation source including section address
   MInt   TargetAddress; // Target address including section address
   uint32_t TargetSection; // New section index of relocation target
   uint32_t TargetOffset;  // Section-relative offset of relocation target
   uint32_t RefAddress;    // Reference point address including section address
   uint32_t RefSection;    // New section index of reference point
   uint32_t RefOffset;     // Section-relative offset of reference point
   int32_t * inlinep = 0;  // Pointer to inline addend
   //const int WordSize = sizeof(MInt) * 8;

   TELF_SectionHeader * NewRelTableSecHeader;  // Section header for new relocation table

   // Number of symbols
   uint32_t NumSymbols = NewSections[symtab].GetNumEntries();

   // New symbol table
   //Elf32_Sym * NewSymbolTable = (Elf32_Sym *)(NewSections[symtab].Buf());

   // Find first section header
   MAC_section_32 * sectp = (MAC_section_32*)(this->Buf() + this->SectionHeaderOffset);

   // Loop through section headers
   for (oldsec = 1; oldsec <= this->NumSections; oldsec++, sectp++) {

      if (sectp->nreloc > 0) {
         // Source section has relocations

         // New section index
         newsec = NewSectIndex[oldsec];

         // Check that section has not been deleted
         if (newsec > 0) {

            // Section address
            SectAddr = NewSectionHeaders[newsec].sh_addr;

            // Finc new relocation table section
            newsecr = newsec + 1;
            if (newsecr >= NewSectionHeaders.GetNumEntries()) {
               err.submit(9000); return;}

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

            // Check that we have allocated this as a relocation section
            if (NewRelTableSecHeader->sh_info != newsec) {
               err.submit(9000); return;
            }

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

            // Pointer to old relocation entry
            if (sectp->reloff >= this->GetDataSize()) {err.submit(2035); break;}
            MAC_relocation_info * relp = (MAC_relocation_info*)(this->Buf() + sectp->reloff);

            // Loop through old relocations
            for (uint32_t oldr = 1; oldr <= sectp->nreloc; oldr++, relp++) {

               // Make new relocation entry and set to zero
               Elf32_Rel NewRelocEntry;

               memset(&NewRelocEntry, 0, sizeof(NewRelocEntry));

               if (relp->r_address & R_SCATTERED) {
                  // scattered relocation into
                  MAC_scattered_relocation_info * scatp = (MAC_scattered_relocation_info*)relp;

                  // Address of source
                  NewRelocEntry.r_offset = scatp->r_address;
                  if (NewRelocEntry.r_offset >= NewSections[newsec].GetDataSize()) {
                     err.submit(2035); continue; // Out of range
                  }
                  // Pointer to inline addend
                  inlinep = (int32_t*)(NewSections[newsec].Buf() + NewRelocEntry.r_offset);
                  if (scatp->r_pcrel) {
                     // Self-relative scattered
                     if (scatp->r_type != MAC32_RELOC_VANILLA) {
                        err.submit(2030, scatp->r_type); continue; // Unexpected type
                     }
                     // Scattered, self-relative, vanilla
                     // Note: I have never seen this relocation method, so I have not
                     // been able to test it. I don't know for sure how it works and 
                     // the documentation is poor.
                     SourceAddress = SectAddr + scatp->r_address;

                     // Target address
                     TargetAddress = SourceAddress + *inlinep;
                     TranslateAddress(TargetAddress, TargetSection, TargetOffset);
                     if (TargetSection == 0) {err.submit(2031); continue;} // not found
                     NewRelocEntry.r_sym = SectionSymbols[TargetSection];
                     if (NewRelocEntry.r_sym == 0) {
                        err.submit(2031); continue; // refers to non-program section
                     }

                     // inline contains full relative address
                     // compensate by subtracting relative address to target section
                     *inlinep -= int32_t(NewSectionHeaders[TargetSection].sh_addr - SourceAddress);
                     // Relocation type
                     NewRelocEntry.r_type = R_386_PC32;
                  }
                  else if (scatp->r_type == MAC32_RELOC_VANILLA) {
                     // Scattered, absolute
                     TargetAddress = *inlinep;
                     TranslateAddress(TargetAddress, TargetSection, TargetOffset);
                     if (TargetSection == 0) {
                        err.submit(2031); continue;} // Target not found
                     NewRelocEntry.r_sym = SectionSymbols[TargetSection];
                     *inlinep = TargetOffset;
                     NewRelocEntry.r_type = R_386_32;
                     if (scatp->r_length != 2) {
                        err.submit(2030, scatp->r_type); continue; // Only 32-bit supported
                     }
                  }
                  else if (scatp->r_type == MAC32_RELOC_SECTDIFF || scatp->r_type == MAC32_RELOC_LOCAL_SECTDIFF) {
                     // relative to arbitrary reference point
                     // check that next record is MAC32_RELOC_PAIR
                     if (oldr == sectp->nreloc || (scatp+1)->r_type != MAC32_RELOC_PAIR || scatp->r_length != 2) {                              
                        err.submit(2050); continue;
                     }
                     // Find target address and reference point
                     RefAddress = (scatp+1)->r_value;
                     TranslateAddress(RefAddress, RefSection, RefOffset);
                     TargetAddress = RefAddress + *inlinep;
                     TranslateAddress(TargetAddress, TargetSection, TargetOffset);
                     // Check that both points are found
                     if (RefSection == 0 || TargetSection == 0) {
                        err.submit(2031); oldr++; relp++; continue;
                     }
                     // Address relative to arbitrary reference point can be translated
                     // to self-relative address if reference point is in same section as source
                     if (RefSection != newsec) {
                        err.submit(2044); oldr++; relp++; continue;
                     }
                     // Translation is possible
                     // Get symbol for target section
                     NewRelocEntry.r_sym = SectionSymbols[TargetSection];
                     // Make self-relative relocation
                     NewRelocEntry.r_type = R_386_PC32;
                     // Calculate compensating addend
                     *inlinep = TargetOffset + scatp->r_address - RefOffset;
                     // Linker will add (target section) - (source full address) to *inlinep, which gives
                     // (target full address) - (reference point full address)
                     // Advance pointers because we have used two records
                     oldr++; relp++; 
                  }
                  else if (scatp->r_type == MAC32_RELOC_PB_LA_PTR) {
                     // procedure linkage table. Not supported
                     NewRelocEntry.r_type = R_386_PLT32;
                     err.submit(2043);
                  }
                  else {
                     // unknown scattered relocation type
                     err.submit(2030, scatp->r_type); continue;
                  }
               }
               else {
                  // Non scattered relocation info
                  // Section offset of relocated address
                  NewRelocEntry.r_offset = relp->r_address;
                  if (NewRelocEntry.r_offset >= NewSections[newsec].GetDataSize()) {
                     err.submit(2035); continue; // Out of range
                  }
                  // Pointer to inline addend
                  inlinep = (int32_t*)(NewSections[newsec].Buf() + NewRelocEntry.r_offset);

                  if (relp->r_extern) {
                     // r_extern = 1: target indicated by symbol index
                     uint32_t symold = relp->r_symbolnum;
                     if (symold >= this->SymTabNumber) {
                        err.submit(2031); continue; // index out of range
                     }
                     NewRelocEntry.r_sym = NewSymbolIndex[symold];
                     if (relp->r_pcrel) {
                        // Self-relative. 
                        // Inline contains -(source address)
                        // Add (source address) to compensate
                        *inlinep += int32_t(SectAddr + relp->r_address);
                     }
                  }
                  else {
                     // r_extern = 0. Target indicated by section + offset
                     // Old section number
                     uint32_t secold = relp->r_symbolnum;
                     if (secold > this->NumSections) {
                        err.submit(2031); continue; // index out of range
                     }
                     TargetSection = NewSectIndex[secold];
                     NewRelocEntry.r_sym = SectionSymbols[TargetSection];
                     if (NewRelocEntry.r_sym == 0 || NewRelocEntry.r_sym > NumSymbols) {
                        err.submit(2031); continue; // refers to non-program section
                     }
                     if (relp->r_pcrel) {
                        // Self-relative. 
                        // Inline contains (target address)-(source address)
                        // Subtract this to compensate

                        // Target section address
                        TargetOffset = uint32_t(NewSectionHeaders[TargetSection].sh_addr);
                        SourceAddress = SectAddr + relp->r_address;
                        *inlinep -= int32_t(TargetOffset - SourceAddress);
                     }
                     else {
                        // Absolute reference
                        // Inline contains target address, convert to section:offset address
                        TranslateAddress(*inlinep, TargetSection, TargetOffset);
                        if (TargetSection == 0) { // Target not found
                           err.submit(2035); continue;
                        }
                        // Translate to section-relative address by subtracting target section address
                        *inlinep -= int32_t(NewSectionHeaders[TargetSection].sh_addr);
                     }
                  }
                  // relocation type (32-bit non-scattered)
                  switch (relp->r_type) {
                  case MAC32_RELOC_VANILLA:
                     // Normal relocation
                     if (relp->r_pcrel) { // self relative
                        NewRelocEntry.r_type = R_386_PC32;
                     }
                     else { // direct
                        NewRelocEntry.r_type = R_386_32;
                     }
                     break;
                  default:
                     err.submit(2030, relp->r_type); // unknown type
                     continue;
                  }
                  // size
                  if (relp->r_length != 2) { // wrong size
                     err.submit(2030,relp->r_type);
                  }
               }
               // Put relocation record into table
               NewSections[newsecr].Push(&NewRelocEntry, sizeof(NewRelocEntry));
            }
         }
      }
   }
}


template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt,
          class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
void CMAC2ELF<MACSTRUCTURES,ELFSTRUCTURES>::MakeRelocationTables(MAC_header_64&) {
   // Convert subfunction: Relocation tables, 64-bit version

   uint32_t oldsec;        // Relocated section number in source file
   uint32_t newsec;        // Relocated section number in destination file
   uint32_t newsecr;       // Relocation table section number in destination file
   uint32_t symold;        // Old index of symbol
   uint32_t TargetSym;     // Target symbol
   uint32_t TargetSection; // New section index of relocation target
   uint32_t RefSym;        // Reference symbol
   uint32_t RefSection;    // New section index of reference point
   int64_t  RefOffset;     // Section-relative offset of reference point
   int64_t  SectAddr;      // Address of current section
   //const int WordSize = sizeof(MInt) * 8;  // Word size, 32 or 64 bits

   TELF_SectionHeader * NewRelTableSecHeader;  // Section header for new relocation table

   // Number of symbols
   //uint32_t NumSymbols = NewSections[symtab].GetNumEntries();

   // New symbol table
   Elf64_Sym * NewSymbolTable = (Elf64_Sym *)(NewSections[symtab].Buf());

   // Find first section header
   MAC_section_64 * sectp = (MAC_section_64*)(this->Buf() + this->SectionHeaderOffset);

   // Loop through section headers
   for (oldsec = 1; oldsec <= this->NumSections; oldsec++, sectp++) {

      if (sectp->nreloc > 0) {
         // Source section has relocations

         // New section index
         newsec = NewSectIndex[oldsec];

         // Check that section has not been deleted
         if (newsec > 0) {

            // Section address
            SectAddr = NewSectionHeaders[newsec].sh_addr;

            // Finc new relocation table section
            newsecr = newsec + 1;
            if (newsecr > NewSectionHeaders.GetNumEntries()) {
               err.submit(9000); return;
            }

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

            // Check that we have allocated this as a relocation section
            if (NewRelTableSecHeader->sh_info != newsec) {
               err.submit(9000); return;
            }

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

            // Pointer to old relocation entry
            if (sectp->reloff >= this->GetDataSize()) {err.submit(2035); break;}
            MAC_relocation_info * relp = (MAC_relocation_info*)(this->Buf() + sectp->reloff);

            // Loop through old relocations
            for (uint32_t oldr = 1; oldr <= sectp->nreloc; oldr++, relp++) {

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

               // Pointer to inline addend
               int32_t * inlinep = 0;

               if (relp->r_address & R_SCATTERED) {
                  // scattered not allowed in 64-bit
                  err.submit(2030, ((MAC_scattered_relocation_info*)relp)->r_type); continue;
               }
               else {
                  // Non scattered relocation info
                  // Section offset of relocated address
                  NewRelocEntry.r_offset = relp->r_address;
                  if (NewRelocEntry.r_offset >= NewSections[newsec].GetDataSize()) {
                     err.submit(2035); continue; // Out of range
                  }
                  // Pointer to inline addend
                  inlinep = (int32_t*)(NewSections[newsec].Buf() + NewRelocEntry.r_offset);

                  // Symbol index of target
                  symold = relp->r_symbolnum;
                  if (relp->r_extern) {
                     if (symold >= this->SymTabNumber) {
                        err.submit(2031); continue; // index out of range
                     }
                     NewRelocEntry.r_sym = NewSymbolIndex[symold];
                  }
                  else {
                     // r_extern = 0, r_symbolnum = section
                     if (symold > NumSectionsNew) {err.submit(2031); continue;}
                     TargetSection = NewSectIndex[symold];
                     NewRelocEntry.r_sym = SectionSymbols[TargetSection];
                     if (relp->r_pcrel) {
                        // Self-relative. 
                        // Inline contains (target address)-(source address)
                        // Subtract this to compensate
                        // Target section address
                        uint64_t TargetSectAddr = NewSectionHeaders[TargetSection].sh_addr;
                        uint64_t SourceAddress = SectAddr + relp->r_address;
                        *inlinep -= int32_t(TargetSectAddr - SourceAddress);
                        *inlinep += 4; // Compensate for subtracting 4 below
                     }
                  }

                  // Find relocation type
                  switch (relp->r_type) {
                  case MAC64_RELOC_UNSIGNED: // absolute address, 32 or 64 bits
                     if (relp->r_length == 2) {
                        NewRelocEntry.r_type = R_X86_64_32S; // 32 bit signed
                     }
                     else if (relp->r_length == 3) {
                        NewRelocEntry.r_type = R_X86_64_64;  // 64 bit
                     }
                     else {
                        err.submit(2030,relp->r_type); continue;
                     }
                     break;
                  case MAC64_RELOC_SIGNED:   // rip-relative, implicit addend = -4
                  case MAC64_RELOC_BRANCH:   // rip-relative, implicit addend = -4
                  case MAC64_RELOC_SIGNED_1: // implicit addend = -4, not -5
                  case MAC64_RELOC_SIGNED_2: // implicit addend = -4, not -6
                  case MAC64_RELOC_SIGNED_4: // implicit addend = -4, not -8
                     // These are all the same:
                     // signed 32-bit rip-relative with implicit -4 addend
                     if (relp->r_length != 2) { // wrong size
                        err.submit(2030,relp->r_type); continue;
                     }
                     NewRelocEntry.r_type = R_X86_64_PC32;
                     // ELF = self-relative, Mac64 = rip-relative. Compensate for difference
                     *inlinep -= 4;
                     break;
                  case MAC64_RELOC_SUBTRACTOR:
                     // relative to arbitrary reference point
                     // must be followed by a X86_64_RELOC_UNSIGNED
                     // check that next record is MAC64_RELOC_UNSIGNED
                     if (oldr == sectp->nreloc || (relp+1)->r_type != MAC64_RELOC_UNSIGNED) {                              
                        err.submit(2050); continue;
                     }
                     // Reference symbol
                     RefSym     = NewRelocEntry.r_sym;
                     RefSection = NewSymbolTable[RefSym].st_shndx;
                     RefOffset  = NewSymbolTable[RefSym].st_value;

                     // Target symbol
                     symold = (relp+1)->r_symbolnum;
                     if (symold >= this->SymTabNumber) {
                        err.submit(2031); continue; // index out of range
                     }
                     TargetSym = NewSymbolIndex[symold];
                     NewRelocEntry.r_sym = TargetSym;

                     // Address relative to arbitrary reference point can be translated
                     // to self-relative address if reference point is in same section as source
                     if (RefSection != newsec) {
                        err.submit(2044); oldr++; relp++; continue;
                     }
                     if (relp->r_length == 2) {
                        *inlinep += int32_t(NewRelocEntry.r_offset) - int32_t(RefOffset);
                     }
                     else if (relp->r_length == 3) {
                        *(int64_t*)inlinep += NewRelocEntry.r_offset - RefOffset;
                        // there is no 64-bit self-relative relocation in ELF,
                        // use 32-bit self-relative and hope there is no carry
                        err.submit(1302); // Warn. This will fail if inline value changes sign
                     }
                     else {                              
                        err.submit(2044);  // wrong size
                     }
                     // self-relative type
                     NewRelocEntry.r_type = R_X86_64_PC32;

                     // increment counters because we used two records
                     relp++; oldr++;
                     break;

                  case MAC64_RELOC_GOT_LOAD: // a rip-relative load of a GOT entry
                     *inlinep = -4;  
                     // Continue into next case
                  case MAC64_RELOC_GOT:      // other GOT references
                     // Make fake GOT entry
                     //NewRelocEntry.r_addend = MakeGOTEntry(NewRelocEntry.r_sym) - 4;
                     *inlinep += MakeGOTEntry(NewRelocEntry.r_sym);
                     NewRelocEntry.r_sym = FakeGOTSymbol;
                     NewRelocEntry.r_type = R_X86_64_PC32;
                     break;
                  }
               }

               // Put relocation record into table
               NewSections[newsecr].Push(&NewRelocEntry, sizeof(NewRelocEntry));
            }
         }
      }
   }
}


template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt,
          class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
void CMAC2ELF<MACSTRUCTURES,ELFSTRUCTURES>::MakeBinaryFile() {
   // Convert subfunction: Make section headers and file header,
   // and combine everything into a single memory buffer.
   uint32_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
   const int WordSize = sizeof(MInt) * 8;

   // 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;
      if (SecSize) { // Don't set size of BSS sections to zero
         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));
}

template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt,
          class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
void CMAC2ELF<MACSTRUCTURES,ELFSTRUCTURES>::MakeImportTables() {
   // Convert subfunction: Fill import tables
   uint32_t oldsec;            // Old section number
   uint32_t Type;              // Old section type
   uint32_t NumEntries;        // Number of entries in import table
   uint32_t EntrySize;         // Entry size of import table
   uint32_t NewSec1;           // New section number of import table
   uint32_t NewSec2;           // New section number of relocation for import table
   uint32_t Offset;            // Offset of relocation source
   uint32_t OldSymbol;         // Old symbol number of import
   uint32_t i;                 // Loop counter
   uint32_t * IndSymTab;       // Pointer to indirect symbol table
   uint32_t IndSymi;           // Index into indirect symbol table
   uint32_t IndSymNum;         // Number of entries in indirect symbol table
   TELF_Relocation NewRelocEntry; // New relocation entry
   const int WordSize = sizeof(MInt) * 8;

   // Machine code of jmp instruction
   static const int8_t JmpInstruction[5] = {int8_t(0xE9), int8_t(0xFC), int8_t(0xFF), int8_t(0xFF), int8_t(0xFF)};

   // Number of indirect symbols
   IndSymNum = this->IndirectSymTabNumber;
   if (IndSymNum == 0) {
      return; // No indirect symbols
   }
   // Find indirect symbol table
   IndSymTab = (uint32_t*)(this->Buf() + this->IndirectSymTabOffset);

   // Find first section header
   TMAC_section * sectp = (TMAC_section*)(this->Buf() + this->SectionHeaderOffset);

   // Loop through section headers
   for (oldsec = 1; oldsec <= this->NumSections; oldsec++, sectp++) {
      // Search for import tables
      Type = sectp->flags & MAC_SECTION_TYPE;
      if (Type >= MAC_S_NON_LAZY_SYMBOL_POINTERS && Type <= MAC_S_SYMBOL_STUBS) {
         // This is an import table

         // Indirect symbol table first entry
         IndSymi = sectp->reserved1;

         // Entry size:
         EntrySize = sectp->reserved2;
         if (EntrySize == 0) EntrySize = WordSize / 8;

         // Find new section
         NewSec1 = NewSectIndex[oldsec];
         NumEntries = uint32_t(NewSectionHeaders[NewSec1].sh_size) / EntrySize;

         // Find new relocation section
         NewSec2 = NewSec1 + 1;
         if (NewSectionHeaders[NewSec2].sh_type != SHT_REL && NewSectionHeaders[NewSec2].sh_type != SHT_RELA) {
            err.submit(9000); // This should be a relocation section
         }
         NewSectionHeaders[NewSec2].sh_link = symtab; // Point to symbol table

         // Offset of first relocation
         Offset = EntrySize & 1; // 1 if EntrySize = 5, otherwise 0

         // Loop through entries
         for (i = 0; i < NumEntries; i++, Offset += EntrySize) {
            // Find symbol
            if (IndSymi >= IndSymNum) {
               err.submit(1303); break; // Import symbol table exhausted
            }
            OldSymbol = IndSymTab[IndSymi];
            if (OldSymbol >= this->SymTabNumber) {
               err.submit(1052); break;
            }
            // Increment pointer to import symbol table
            IndSymi++;

            // Make relocation record
            memset(&NewRelocEntry, 0, sizeof(NewRelocEntry));
            NewRelocEntry.r_offset = Offset;
            if (WordSize == 32) {
               if (EntrySize == 4) {
                  NewRelocEntry.r_type = R_386_32;
               }
               else if (EntrySize == 5) {
                  NewRelocEntry.r_type = R_386_PC32;
               }
               else {
                  err.submit(2045);
               }
            }
            else { // 64 bit
               if (EntrySize == 8) {
                  NewRelocEntry.r_type = R_X86_64_64;
               }
               else if (EntrySize == 5) {
                  NewRelocEntry.r_type = R_X86_64_PC32;
               }
               else {
                  err.submit(2045);
               }
            }
            NewRelocEntry.r_sym = NewSymbolIndex[OldSymbol];

            // Store relocation record
            NewSections[NewSec2].Push(&NewRelocEntry, (WordSize==32) ? sizeof(Elf32_Rel) : sizeof(Elf64_Rela));

            // Insert jmp instruction if EntrySize = 5
            if (EntrySize == 5) {
               if (Offset -1 + EntrySize > NewSections[NewSec1].GetDataSize()) {
                  err.submit(9000); // Outside section
               }
               memcpy(NewSections[NewSec1].Buf()+Offset-1, JmpInstruction, 5);
            }
         }
      }
   }
}


template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt,
          class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
void CMAC2ELF<MACSTRUCTURES,ELFSTRUCTURES>::TranslateAddress(MInt addr, uint32_t & section, uint32_t & offset) {
   // Translate 32-bit address to section + offset
   // (Sections are not necessarily ordered by address)
   uint32_t sec;
   MInt secstart;
   for (sec = 1; sec < NumSectionsNew; sec++) {
      secstart = NewSectionHeaders[sec].sh_addr;
      if (addr >= secstart && addr < secstart + MInt(NewSectionHeaders[sec].sh_size)) {
         // Section found
         section = sec;
         offset = uint32_t(addr - secstart);
         return;
      }
   }
   // Not found
   section = offset = 0; 
}

template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt,
          class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
uint32_t CMAC2ELF<MACSTRUCTURES,ELFSTRUCTURES>::MakeGOTEntry(int symbol) {
   // Make entry in fake GOT for symbol
   uint32_t NumGOTEntries = GOTSymbols.GetNumEntries();
   uint32_t symi;  // Symbol index
   const int WordSize = sizeof(MInt) * 8;

   // Get symbol for start of GOT
   FakeGOTSymbol = SectionSymbols[FakeGOTSection];
   
   // Search for symbol in previous entries
   for (symi = 0; symi < NumGOTEntries; symi++) {
      if (GOTSymbols[symi] == symbol) break;
   }
   if (symi == NumGOTEntries) {
      // Not found. Make new entry
      GOTSymbols.Push(symbol);
   }
   return symi * (WordSize / 8);
}


template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt,
          class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation>
void CMAC2ELF<MACSTRUCTURES,ELFSTRUCTURES>::MakeGOT() {
   // Make fake Global Offset Table
   const int WordSize = sizeof(MInt) * 8;
   if (!HasGOT) return;

   uint32_t NumEntries = GOTSymbols.GetNumEntries();
   NewSections[FakeGOTSection].Push(0, NumEntries*(WordSize/8)); 

   // Make relocations for GOT
   Elf64_Rela NewRelocEntry;
   memset(&NewRelocEntry, 0, sizeof(NewRelocEntry));

   for (uint32_t i = 0; i < NumEntries; i++) {
      NewRelocEntry.r_offset = i * (WordSize/8);
      NewRelocEntry.r_sym = GOTSymbols[i];
      NewRelocEntry.r_type = R_X86_64_64;
      NewSections[FakeGOTSection+1].Push(&NewRelocEntry, sizeof(NewRelocEntry));
   }
}


// Make template instances for 32 and 64 bits
template class CMAC2ELF<MAC32STRUCTURES,ELF32STRUCTURES>;
template class CMAC2ELF<MAC64STRUCTURES,ELF64STRUCTURES>;
