/****************************  elf2mac.cpp   *********************************
* Author:        Agner Fog
* Date created:  2007-01-10
* Last modified: 2025-10-30
* Project:       objconv
* Module:        elf2mac.cpp
* Description:
* Module for converting ELF file to Mach-O file
*
* Copyright 2007-2025 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, 
          class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt>
   CELF2MAC<ELFSTRUCTURES,MACSTRUCTURES>::CELF2MAC() {
   // Constructor
      memset(this, 0, sizeof(*this));                   // Reset everything
}

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

   // Call the subfunctions
   ToFile.SetFileType(FILETYPE_MACHO_LE);             // Set type of new file
   MakeFileHeader();                                  // Make file header
   MakeSectionsIndex();                               // Make sections index translation table
   FindUnusedSymbols();                               // Check if symbols used, remove unused symbols
   MakeSymbolTable();                                 // Make symbol table and string tables
   MakeSections();                                    // Make sections and relocation tables
   MakeBinaryFile();                                  // Put sections together
   *this << ToFile;                                   // Take over new file buffer
}


template <class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation,
          class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt>
void CELF2MAC<ELFSTRUCTURES,MACSTRUCTURES>::MakeFileHeader() {
   // Convert subfunction: Make file header and load segment command
   TMAC_header NewHeader;                             // new file header
   NewHeader.magic      = (this->WordSize == 32) ? MAC_MAGIC_32 : MAC_MAGIC_64; // Mach magic number identifier
   NewHeader.cputype    = (this->WordSize == 32) ? MAC_CPU_TYPE_I386 : MAC_CPU_TYPE_X86_64;
   NewHeader.cpusubtype = MAC_CPU_SUBTYPE_I386_ALL;
   NewHeader.filetype   = MAC_OBJECT;
   NewHeader.ncmds      = 3;                          // Three commands = segment, symbol table, dynsymtab
   NewHeader.sizeofcmds = 0;                          // Set this later
   NewHeader.flags      = 0;                          // No flags needed

   // put file header in OutFile
   ToFile.Push(&NewHeader, sizeof(NewHeader));
}


template <class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation,
          class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt>
void CELF2MAC<ELFSTRUCTURES,MACSTRUCTURES>::MakeSectionsIndex() {
   // Make sections index translation table and section offset table.

   // We must make these tables before the sections, because they are needed for the
   // symbol tables and relocation tables, and we must make the symbol tables before 
   // the relocation tables, and we must make the relocation tables together with the
   // sections. 
   uint32_t oldsec;                         // Section number in old file
   uint32_t newsec = 0;                     // Section number in new file
   NewSectIndex. SetNum(this->NSections); // Allocate size for section index table
   NewSectIndex. SetZero();               // Initialize
   NewSectOffset.SetNum(this->NSections); // Allocate buffer for section offset table
   NewSectOffset.SetZero();               // Initialize

   MInt NewVirtualAddress = 0;            // Virtual address of new section as specified in Mach-O file

   // First loop through old sections
   for (oldsec = 0; oldsec < this->NSections; oldsec++) {
      NewSectIndex[oldsec]  = 0;
      NewSectOffset[oldsec] = 0;

      // Get section name
      const char * sname = "";
      uint32_t namei = this->SectionHeaders[oldsec].sh_name;
      if (namei >= this->SecStringTableLen) {
         err.submit(2112);
      }
      else sname = this->SecStringTable + namei;

      if (cmd.DebugInfo == CMDL_DEBUG_STRIP) {
         // Check for debug section names
         if (strncmp(sname, ".note",    5) == 0
         ||  strncmp(sname, ".comment", 8) == 0
         ||  strncmp(sname, ".stab",    5) == 0
         ||  strncmp(sname, ".debug",   6) == 0) {
            // Remove this section
            this->SectionHeaders[oldsec].sh_type = SHT_REMOVE_ME;
            cmd.CountDebugRemoved();
            continue;
         }
      }

      if (cmd.ExeptionInfo == CMDL_EXCEPTION_STRIP) {
         // Check for exception section name
         if (strncmp(sname, ".eh_frame", 9) == 0) {
            // Remove this section
            this->SectionHeaders[oldsec].sh_type = SHT_REMOVE_ME;
            cmd.CountExceptionRemoved();
            continue;
         }
      }

      // Search for program data sections only
      if (this->SectionHeaders[oldsec].sh_type != SHT_PROGBITS 
      &&  this->SectionHeaders[oldsec].sh_type != SHT_NOBITS) {
         // Has no data. Ignore
         continue;
      }

      if (this->SectionHeaders[oldsec].sh_size == 0) {
         // Remove empty section
         // The linker has a bug with empty sections
         continue;
      }

      // Section index translation table
      NewSectIndex[oldsec] = newsec++;

      // Calculate virtual memory address of section. This address does not have 
      // much to do with the final address, but it is needed in relocation entries.

      // Alignment
      int NewAlign = FloorLog2((uint32_t)this->SectionHeaders[oldsec].sh_addralign);
      if (NewAlign > 12) NewAlign = 12;   // What is the limit for highest alignment?
      int AlignBy = 1 << NewAlign;

      // Align memory address
      NewVirtualAddress = (NewVirtualAddress + AlignBy - 1) & -(MInt)AlignBy;

      // Virtual memory address of new section
      NewSectOffset[oldsec] = NewVirtualAddress;

      // Increment memory address
      NewVirtualAddress += this->SectionHeaders[oldsec].sh_size;

      // Fix v. 2.14: Align end of memory address by 4
      NewVirtualAddress = (NewVirtualAddress + 3) & MInt(-4);
   }

   // Store number of sections in new file
   NumSectionsNew = newsec;

   // Calculate file offset of first raw data
   RawDataOffset = sizeof(TMAC_header) 
      + sizeof(TMAC_segment_command) 
      + NumSectionsNew * sizeof(TMAC_section)
      + sizeof(MAC_symtab_command) 
      + sizeof(MAC_dysymtab_command);

   // Align end of memory address by 4
   NewVirtualAddress = (NewVirtualAddress + 3) & MInt(-4);

   // Make segment command
   TMAC_segment_command NewSegment;
   memset(&NewSegment, 0, sizeof(NewSegment));
   NewSegment.cmd      = (this->WordSize == 32) ? MAC_LC_SEGMENT : MAC_LC_SEGMENT_64;
   NewSegment.cmdsize  = sizeof(TMAC_segment_command) + NumSectionsNew * sizeof(TMAC_section);
   NewSegment.fileoff  = RawDataOffset;
   NewSegment.nsects   = NumSectionsNew;
   NewSegment.maxprot  = NewSegment.initprot = 7; // 1=read, 2=write, 4=execute
   NewSegment.vmsize   = NewVirtualAddress;
   NewSegment.filesize = 0;                       // Changed later

   // put segment command in OutFile
   CommandOffset = ToFile.Push(&NewSegment, sizeof(NewSegment));
}


template <class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation,
          class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt>
void CELF2MAC<ELFSTRUCTURES,MACSTRUCTURES>::MakeSymbolTable() {
   // Convert subfunction: Symbol table and string tables
   uint32_t oldsec;                  // Section number in old file
   TELF_SectionHeader OldHeader;   // Old section header
   int FoundSymTab = 0;            // Found symbol table
   char * strtab;                    // Old symbol string table
   int8_t * symtab;                  // Old symbol table
   uint32_t symtabsize;              // Size of old symbol table
   int8_t * symtabend;               // End of old symbol table
   uint32_t entrysize;               // Size of each entry in old symbol table
   TELF_Symbol OldSym;             // Old symbol table record
   uint32_t OldSymI;                 // Symbol index in old symbol table
   const char * symname;           // Symbol name
   int NewSection = 0;             // New section index
   int NewType;                    // New symbol type
   int NewDesc;                    // New symbol reference type
   MInt Value;                     // Symbol value
   uint32_t Scope;                   // 0: Local, 1: Public, 2: External 

   // Loop through old sections to find symbol table
   for (oldsec = 0; oldsec < this->NSections; oldsec++) {

      // Search for program data sections only
      if (this->SectionHeaders[oldsec].sh_type == SHT_SYMTAB 
      ||  this->SectionHeaders[oldsec].sh_type == SHT_DYNSYM) {
         FoundSymTab++;

         // Copy symbol table header for convenience
         OldHeader = this->SectionHeaders[oldsec];

         // Find associated string table
         if (OldHeader.sh_link >= (uint32_t)(this->NSections)) {
            err.submit(2035); OldHeader.sh_link = 0;
         }
         strtab = (char*)this->Buf() + (uint32_t)this->SectionHeaders[OldHeader.sh_link].sh_offset;

         // Find old symbol table
         entrysize = (uint32_t)OldHeader.sh_entsize;
         if (entrysize < sizeof(TELF_Symbol)) {err.submit(2033); entrysize = sizeof(TELF_Symbol);}

         symtab = this->Buf() + (uint32_t)OldHeader.sh_offset;
         symtabsize = (uint32_t)OldHeader.sh_size;
         symtabend = symtab + symtabsize;

         if (NewSymTab[0].GetNumEntries() == 0) {
            // make empty symbol record for index 0
            // NOTE on linking an objconv-elf2mac-converted .o with ld_classic vs. with ld_new:
            // - ld_classic can handle this dummy entry with n_type=MAC_N_UNDF(0) without any problem
            // - ld_new (indroduced with XCode 15) terminates with an assert, if it finds this dummy entry 
            //   with n_type=MAC_N_UNDF(0) and there are other non-dummy undefined symbol entries in the .o file:
            //   0  0x1045e81bc  __assert_rtn + 72
            //   1  0x10450d5bc  ld::InputFiles::SliceParser::parseObjectFile(mach_o::Header const*) const + 15360
            //   2  0x10451688c  ld::InputFiles::SliceParser::parse() const + 696
            //   ...
            //   ld: Assertion failed: (firstUndefinedSymbol != 0), function parseObjectFile, file InputFiles.cpp, line 1810.
            // - a working fix for objconv to make the ld_new happy: use MAC_N_ABS(2) for the n_type of the dummy entry!
//#define ELF2MAC_USE_MAC_N_ABS_FOR_DUMMY_SYMBOL
#ifndef ELF2MAC_USE_MAC_N_ABS_FOR_DUMMY_SYMBOL
            NewSymTab[0].AddSymbol(0, "", 0, 0, 0, 0);
#else
            NewSymTab[0].AddSymbol(0, "", MAC_N_ABS, 0, 0, 0);
#endif
         }

         // Loop through old symbol table
         for (OldSymI = 0; symtab < symtabend; symtab += entrysize, OldSymI++) {

            if (OldSymI == 0) continue; // First symbol entry in ELF file is unused

            // Copy 32 bit symbol table entry or convert 64 bit entry
            OldSym = *(TELF_Symbol*)symtab;
 
            // Old symbol type
            int type = OldSym.st_type;

            // Old symbol storage class = binding
            int binding = OldSym.st_bind;

            // Get symbol name
            if (OldSym.st_name < this->SymbolStringTableSize) {
               symname = strtab + OldSym.st_name;
            }
            else {
               err.submit(2112); // String table corrupt
               continue;       // Ignore
            }
            if (symname == 0 || *symname == 0) {
               // Symbol has no name. Give it a name
               // Mac linker messes this up if the symbol doesn't have a unique name.
               char tempbuf[80];
               sprintf(tempbuf, "?unnamed%i", OldSymI);
               int os = UnnamedSymbolsTable.PushString(tempbuf);
               symname = (char*)UnnamedSymbolsTable.Buf() + os;
            }
            
            NewType = NewDesc = 0; // New symbol type

            // Value = address
            Value = OldSym.st_value;

            // Section
            if (OldSym.st_shndx == SHN_UNDEF) {
               NewSection = 0; // External
            }
            else if ((int16_t)(OldSym.st_shndx) == SHN_ABS) {
               NewType |= MAC_N_ABS; // Absolute symbol
               NewDesc |= MAC_N_NO_DEAD_STRIP;
               NewSection = 0; 
            }
            else if ((int16_t)(OldSym.st_shndx) == SHN_COMMON) {
               NewType |= MAC_N_ABS; // Common symbol. Translate to abs and make warning
               NewDesc |= MAC_N_NO_DEAD_STRIP;
               NewSection = 0; 
               err.submit(1053, symname); // Warning. Common symbol
            }            
            else if (OldSym.st_shndx >= this->NSections) {
               err.submit(2036, OldSym.st_shndx); // Special/unknown section index or out of range
            }
            else {
               // Normal section index. 
               // Look up in section index translation table and add 1 because it is 1-based
               NewSection = NewSectIndex[OldSym.st_shndx] + 1;
               // Value must be absolute address. Add section address
               Value += NewSectOffset[OldSym.st_shndx];
            }

            // Convert binding/storage class
            switch (binding) {
            case STB_LOCAL:   // Local
               Scope = S_LOCAL;
               if (!(NewType & MAC_N_ABS)) NewType |= MAC_N_SECT;
               break;

            case STB_GLOBAL:
               if (NewSection || (NewType & MAC_N_ABS)) {
                  // Public
                  Scope = S_PUBLIC;
                  NewType |= MAC_N_EXT;
                  if (!(NewType & MAC_N_ABS)) NewType |= MAC_N_SECT;
               }
               else {
                  // External
                  Scope = S_EXTERNAL;
                  NewType |= MAC_N_EXT;
               }
               NewDesc |= MAC_REF_FLAG_UNDEFINED_NON_LAZY;
               break;

            case STB_WEAK:
               if (NewSection) {
                  // Weak public
                  Scope = S_PUBLIC;
                  NewType |= MAC_N_EXT | MAC_N_SECT;
                  NewDesc |= MAC_N_WEAK_DEF;
                  if (this->WordSize == 32) {
                     err.submit(1051, symname);  // Weak public only allowed in coalesced section of MachO-32
                  }
               }
               else {
                  // Weak external
                  Scope = S_EXTERNAL;
                  NewType |= MAC_N_EXT;
                  NewDesc |= MAC_N_WEAK_REF;
               }
               break;

            default:
               Scope = S_LOCAL;
               err.submit(2037, binding); // Other. Not supported
            }

            // Make record depending on type
            switch (type) {
            case STT_OBJECT: case STT_NOTYPE:
               // Data object
               break;

            case STT_GNU_IFUNC:
               err.submit(1063); // Warning: Gnu indirect function cannot be converted
               // continue in next case:

            case STT_FUNC:
               // Function
               break;

            case STT_FILE: 
               // File name record. Ignore
               continue;

            case STT_SECTION:
               // Section name record. (Has no name)
               break;

            case STT_COMMON:
            default:
               err.submit(2038, type); // Symbol type not supported
               continue;
            }

            // Discard unused symbols
            if (Scope != S_PUBLIC && !OldSymbolUsed[OldSymI]) continue;

            // Store new symbol record in the appropriate table
            if (Scope > 2) err.submit(9000);

            NewSymTab[Scope].AddSymbol(OldSymI, symname, NewType, NewDesc, NewSection, Value);

            // Store scope in OldSymbolScope
            if (OldSymI < NumOldSymbols) {
               OldSymbolScope[OldSymI] = Scope;
            }
         } // End OldSymI loop
      }
   } // End search for symbol table
   if (FoundSymTab == 0) err.submit(2034); // Symbol table not found
   if (FoundSymTab  > 1) err.submit(1032); // More than one symbol table found
}


template <class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation,
          class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt>
void CELF2MAC<ELFSTRUCTURES,MACSTRUCTURES>::Elf2MacRelocations(Elf32_Shdr & OldRelHeader, MAC_section_32 & NewHeader, uint32_t NewRawDataOffset, uint32_t oldsec) {
   // Convert 32-bit relocations from ELF to MAC
   // (This function has two template instances, only the 32-bit instance is used)

   Elf32_Rela OldRelocation;  // Old relocation table entry
   MAC_scattered_relocation_info scat; // Scattered relocation entry
   memset(&scat, 0, sizeof(scat));

   // Get pointer to old relocation table
   int8_t * reltab = this->Buf() + OldRelHeader.sh_offset;
   int8_t * reltabend = reltab + OldRelHeader.sh_size;

   // Get entry size
   uint32_t entrysize = (uint32_t)OldRelHeader.sh_entsize;
   uint32_t expectedentrysize = (OldRelHeader.sh_type == SHT_REL) ? sizeof(Elf32_Rel) : sizeof(Elf32_Rela);
   if (entrysize < expectedentrysize) {err.submit(2033); entrysize = expectedentrysize;}

   // File pointer to relocations
   NewHeader.reloff = NewRelocationTab.GetNumEntries()*sizeof(MAC_relocation_info);  // Offset to first relocation table added later

   // Loop through relocation table entries
   for (; reltab < reltabend; reltab += entrysize) {

      // Copy relocation entry with or without addend
      OldRelocation.r_addend = 0;
      memcpy(&OldRelocation, reltab, entrysize);

      // Find inline addend
      uint32_t InlinePosition = (uint32_t)(NewRawDataOffset + OldRelocation.r_offset);

      // Check that address is valid
      if (InlinePosition >= this->GetDataSize()) {
         // Address is invalid
         err.submit(2032);  break;
      }

      // Pointer to inline addend
      int32_t * piaddend = (int32_t*)(NewRawData.Buf() + InlinePosition);

      // Add old addend if any
      *piaddend += (int32_t)OldRelocation.r_addend;

      // Define relocation parameters
      uint32_t  r_address = 0;      // section-relative offset to relocation source
      uint32_t  r_symbolnum = 0;    // symbol index if r_extern == 1 or section ordinal if r_extern == 0
      // uint32_t  r_value = 0;        // value of relocation target
      // int     r_scattered = 0;    // use scattered relocation
      int     r_pcrel = 0;        // self relative
      int     r_length = 2;       // size of source: 0=byte, 1=2 bytes, 2=4 bytes, 3=8 bytes
      int     r_extern = 0;       // public or external
      int     r_type = 0;         // if not 0, machine specific relocation type
      int     Scope = 0;          // Symbol scope: 0 = local, 1 = public, 2 = external

      // source offset
      r_address = (uint32_t)OldRelocation.r_offset;

      // target scope
      if (OldRelocation.r_sym < NumOldSymbols) {
         Scope = OldSymbolScope[OldRelocation.r_sym];
      }

      // Get r_extern: 0 = local target referenced by address,
      //               1 = external symbol referenced by symbol table index
      switch (Scope) {
      case S_LOCAL:  // Local target must be referenced by address
         r_extern = 0;  break;

      case S_PUBLIC:  // Public target is optionally referenced by index or by address
         r_extern = 0;
         // r_extern = 1; is not allowed!
         break;

      case S_EXTERNAL:  // External target is always referenced by index
         r_extern = 1;  break;
      }

      // Get zero-based index into NewSymTab[Scope]
      int newindex = NewSymTab[Scope].TranslateIndex(OldRelocation.r_sym);
      if (newindex < 0) {
         // Symbol not found or wrong type
         err.submit(2031); 
         break;
      }
      if (r_extern) {
         // r_symbolnum is zero based index into combined symbol tables.
         // Add number of entries in preceding NewSymTab tables to index 
         // into NewSymTab[Scope]
         r_symbolnum = newindex + NumSymbols[Scope];
      }
      else {
         // r_extern = 0. r_symbolnum = target section
         r_symbolnum = NewSymTab[Scope][newindex].n_sect;

         // Absolute address of target stored inline in source
         *piaddend  += (uint32_t)NewSymTab[Scope][newindex].n_value;
      }

      // Get relocation type and fix addend
      switch(OldRelocation.r_type) {
      case R_386_NONE:    // Ignored
         continue;

      case R_386_IRELATIVE:
         err.submit(1063); // Warning: Gnu indirect function cannot be converted
         // continue in next case?:
      case R_386_32:       // 32-bit absolute virtual address
         r_type  = MAC32_RELOC_VANILLA;  
         break;

      case R_386_PC32:   // 32-bit self-relative
         r_type  = MAC32_RELOC_VANILLA;  
         r_pcrel = 1;
         // Mach-O 32 bit format requires that self-relative addresses must have
         // self-relative values already before relocation. Therefore
         // the source address is subtracted.
         // (The PC reference point is the end of the source = start
         // of source + 4, but ELF files have the same offset so no further
         // correction is needed when converting from ELF file).

         // !! ToDo: Self-relative relocations plus offset to local symbol in a different section 
         // sometimes causes problems in Mac linker, perhaps because it fails to determine 
         // correctly which section the target is in. Use a relocation with a reference point
         // instead. This probably occurs only in assembler-coded self-relative 32-bit code.
         // (Use asmlib A_strtoupper and A_strcspn as test cases - they fail if dummy data
         // at the end of .data section is removed)
         *piaddend -= r_address + (uint32_t)NewHeader.addr;
         break;

      case R_UNSUPPORTED_IMAGEREL:  // 32-bit image-relative
         // This occurs only when converting from COFF (via ELF)
         // Needs scattered relocation entry
         scat.r_address   = r_address;
         scat.r_length    = 2;
         scat.r_pcrel     = 0;
         scat.r_scattered = 1;
         scat.r_type      = MAC32_RELOC_SECTDIFF;
         scat.r_value     = r_symbolnum;
         // Store first entry of scattered pair
         NewRelocationTab.Push(&scat, sizeof(scat));
         NewHeader.nreloc++;
         // Make subtractor record for image base
         scat.r_type      = MAC32_RELOC_PAIR;
         scat.r_value     = GetImagebaseSymbol();
         // Store second entry of scattered pair
         NewRelocationTab.Push(&scat, sizeof(scat));
         NewHeader.nreloc++;
         continue;

      case R_386_GOT32: case R_386_GLOB_DAT: case R_386_GOTOFF: case R_386_GOTPC:
         // Global offset table
         err.submit(2042);     // cannot convert position-independent code
         err.ClearError(2042); // report this error only once
         r_type = 0;
         break;

      case R_386_PLT32: case R_386_JMP_SLOT: 
         // procedure linkage table
         err.submit(2043);     // cannot convert import table
         err.ClearError(2043); // report this error only once
         r_type = 0;
         break;

      default:      // Unknown or unsupported relocation method
         err.submit(2030, OldRelocation.r_type); 
         r_type = 0;  break;
      }

      if (!r_pcrel) {
         // Warn for position dependent code. 
         // This warning is currently turned off in error.cpp.
         err.submit(1050, this->SymbolName(OldRelocation.r_sym)); 
         // Write this error only once
         err.ClearError(1050);
      }

      // Make relocation entry
      MAC_relocation_info rel;
      memset(&rel, 0, sizeof(rel));

      // Make non-scattered relocation entry
      rel.r_address   = r_address; 
      rel.r_symbolnum = r_symbolnum; 
      rel.r_pcrel     = r_pcrel; 
      rel.r_length    = r_length; 
      rel.r_extern    = r_extern; 
      rel.r_type      = r_type; 

      // Store relocation entry
      NewRelocationTab.Push(&rel, sizeof(rel));
      NewHeader.nreloc++;

      // Remember that symbol is used
      // if (SymbolsUsed && OldRelocation.r_type && NewRelocation.r_symbolnum < ?) {
      // SymbolsUsed[NewRelocation.r_symbolnum]++;}

   } // End of relocations loop
}

template <class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation,
          class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt>
void CELF2MAC<ELFSTRUCTURES,MACSTRUCTURES>::Elf2MacRelocations(Elf64_Shdr & OldRelHeader, MAC_section_64 & NewHeader, uint32_t NewRawDataOffset, uint32_t oldsec) {
   // Convert 64-bit relocations from ELF to MAC
   // (This function has two template instances, only the 64-bit instance is used)

   // Make relocation entry for dummy subtractor
   MAC_relocation_info relsub;
   memset(&relsub, 0, sizeof(relsub));

   Elf64_Rela OldRelocation;  // Old relocation table entry

   // Get pointer to old relocation table
   int8_t * reltab = this->Buf() + OldRelHeader.sh_offset;
   int8_t * reltabend = reltab + OldRelHeader.sh_size;

   // Get entry size
   uint32_t entrysize = (uint32_t)OldRelHeader.sh_entsize;
   uint32_t expectedentrysize = (OldRelHeader.sh_type == SHT_REL) ? sizeof(Elf64_Rel) : sizeof(Elf64_Rela);
   if (entrysize < expectedentrysize) {err.submit(2033); entrysize = expectedentrysize;}

   // File pointer to relocations
   NewHeader.reloff = NewRelocationTab.GetNumEntries()*sizeof(MAC_relocation_info);  // Offset to first relocation table added later

   // Loop through relocation table entries
   for (; reltab < reltabend; reltab += entrysize) {

      // Copy relocation entry with or without addend
      OldRelocation.r_addend = 0;
      memcpy(&OldRelocation, reltab, entrysize);

      // Find inline addend
      uint32_t InlinePosition = (uint32_t)(NewRawDataOffset + OldRelocation.r_offset);

      // Check that address is valid
      if (InlinePosition >= this->GetDataSize()) {
         // Address is invalid
         err.submit(2032);
         break;
      }

      // Pointer to inline addend
      int32_t * piaddend = (int32_t*)(NewRawData.Buf() + InlinePosition);

      // Add old addend if any
      *piaddend += (uint32_t)OldRelocation.r_addend;

      // Define relocation parameters
      uint32_t  r_address = 0;      // section-relative offset to relocation source
      uint32_t  r_symbolnum = 0;    // symbol index if r_extern == 1 or section ordinal if r_extern == 0
      // uint32_t  r_value = 0;        // value of relocation target
      // int     r_scattered = 0;    // scattered relocations not used in 64 bit
      int     r_pcrel = 0;        // self relative
      int     r_length = 2;       // size of source: 0=byte, 1=2 bytes, 2=4 bytes, 3=8 bytes
      int     r_extern = 0;       // public or external
      int     r_type = 0;         // if not 0, machine specific relocation type
      int     Scope = 0;          // Symbol scope: 0 = local, 1 = public, 2 = external

      // source offset
      r_address = (uint32_t)OldRelocation.r_offset;

      // target scope
      if (OldRelocation.r_sym < NumOldSymbols) {
         Scope = OldSymbolScope[OldRelocation.r_sym];
      }

      // Get r_extern: 0 = local target referenced by address,
      //               1 = public or external symbol referenced by symbol table index
      switch (Scope) {
      case S_LOCAL:   // Local target
         // r_extern = 0;  // Local target must be referenced by address
         // Note: the description in reloc.h says that local targets are addressed
         // relative to any preceding public target. If there is no preceding label
         // then referenced by address in the segment. However, the Gnu compiler
         // uses reference to a local symbol and sets r_extern = 1 to indicate that
         // it refers to a symbol record, not to an address. I have chosen to use the
         // latter method because it is simpler, though undocumented.
         r_extern = 1;
         break;

      case S_PUBLIC:  // Public target is optionally referenced by index or by address
         r_extern = 1;
         break;

      case S_EXTERNAL:  // External target is always referenced by index
         r_extern = 1;  
         break;
      }

      // Get zero-based index into NewSymTab[Scope]
      int newindex = NewSymTab[Scope].TranslateIndex(OldRelocation.r_sym);
      if (newindex < 0) {
         // Symbol not found or wrong type
         err.submit(2031); 
         break;
      }

      // r_symbolnum is zero based index into combined symbol tables.
      // Add number of entries in preceding NewSymTab tables to index 
      // into NewSymTab[Scope]
      r_symbolnum = newindex + NumSymbols[Scope];

      // Get relocation type and fix addend, 64 bit
      switch(OldRelocation.r_type) {
      case R_X86_64_NONE:    // Ignored
         continue;

      case R_X86_64_64:      
         // 64-bit absolute virtual address
         r_type  = MAC64_RELOC_UNSIGNED;  r_length = 3;
         break;

      case R_X86_64_IRELATIVE:
         err.submit(1063); // Warning: Gnu indirect function cannot be converted
         // continue in next case?:
      case R_X86_64_32: case R_X86_64_32S: {
         // 32-bit absolute virtual address
         // Note: The linker doesn't accept a 32-bit absolute address
         // Make address relative to the image base, and add the value of the image base to compensate
         if (cmd.ImageBase == 0) {
            // Default image base if not specified
            cmd.ImageBase = 0x400000;
         }

         // Make subtractor relocation entry for image base
         relsub.r_address   = r_address;
         relsub.r_symbolnum = GetImagebaseSymbol();
         relsub.r_length    = 2; 
         relsub.r_extern    = 1; 
         relsub.r_type      = MAC64_RELOC_SUBTRACTOR; 

         NewRelocationTab.Push(&relsub, sizeof(relsub));
         NewHeader.nreloc++;
         // Add image base to compensate for subtracted image base
         *piaddend += cmd.ImageBase;
         
         // Now we can add the address we really want:
         r_type  = MAC64_RELOC_UNSIGNED;  
         r_length = 2;
         
         // Warn that image base must be set to the specified value
         char ImageBaseHex[32];
         sprintf(ImageBaseHex, "%X", cmd.ImageBase); // write value as hexadecimal
         err.submit(1300, ImageBaseHex);  err.ClearError(1300);
         break;}       
 
      case R_X86_64_PC32:   // 32-bit self-relative
         r_type  = MAC64_RELOC_BRANCH;
         // MAC64_RELOC_SIGNED does the same, but the linker complains if external symbol
         r_length = 2;  
         r_pcrel = 1;
         // Difference between EIP-relative and self-relative relocation = size of address field
         // Adjust inline addend for different relocation method:
         *piaddend += 4;
         break;

      case R_UNSUPPORTED_IMAGEREL:  // 32-bit image-relative
         // This occurs only when converting from COFF (via ELF)
         // Make subtractor relocation entry for image base
         relsub.r_address   = r_address;
         relsub.r_symbolnum = GetImagebaseSymbol();
         relsub.r_length    = 2; 
         relsub.r_extern    = 1; 
         relsub.r_type      = MAC64_RELOC_SUBTRACTOR; 

         NewRelocationTab.Push(&relsub, sizeof(relsub));
         NewHeader.nreloc++;
         
         // Second record adds the target address
         r_type  = MAC64_RELOC_UNSIGNED;  
         r_length = 2;
         break;

      case R_X86_64_GLOB_DAT: case R_X86_64_GOTPCREL:
         // Create 64-bit GOT entry??
         r_type  = MAC64_RELOC_GOT;  r_length = 2;
         break;         

      case R_X86_64_GOT32:
         // 32-bit GOT entry
         err.submit(2042);     // cannot convert 32-bit GOT
         err.ClearError(2042); // report this error only once
         r_type = 0;
         break;

      case R_X86_64_PLT32: case R_X86_64_JUMP_SLOT: 
         // procedure linkage table
         err.submit(2043);     // cannot convert import table
         err.ClearError(2043); // report this error only once
         r_type = 0;
         break;

      case R_X86_64_COPY: case R_X86_64_RELATIVE:
      default:      // Unknown or unsupported relocation method
         err.submit(2030, OldRelocation.r_type); 
         r_type = 0;  break;
      }

      // Make relocation entry
      MAC_relocation_info rel;
      memset(&rel, 0, sizeof(rel));

      // Make non-scattered relocation entry
      rel.r_address   = r_address; 
      rel.r_symbolnum = r_symbolnum; 
      rel.r_pcrel     = r_pcrel; 
      rel.r_length    = r_length; 
      rel.r_extern    = r_extern; 
      rel.r_type      = r_type; 

      // Store relocation entry
      NewRelocationTab.Push(&rel, sizeof(rel));
      NewHeader.nreloc++;

      // Remember that symbol is used
      // if (SymbolsUsed && OldRelocation.r_type && NewRelocation.r_symbolnum < ?) {
      // SymbolsUsed[NewRelocation.r_symbolnum]++;}

   } // End of relocations loop
}

template <class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation,
          class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt>
void CELF2MAC<ELFSTRUCTURES,MACSTRUCTURES>::MakeSections() {
   // Convert subfunction: Make sections and relocation tables
   uint32_t oldsec;                  // Section number in old file
   uint32_t relsec;                  // Relocation section in old file
   TMAC_section NewHeader;         // New section header
   TELF_SectionHeader OldHeader;   // Old section header
   TELF_SectionHeader OldRelHeader;// Old relocation section header
   uint32_t NewVirtualAddress = 0;   // Virtual address of new section
   uint32_t NewRawDataOffset = 0;    // Offset into NewRawData of section. 
   // NewRawDataOffset is different from NewVirtualAddress if alignment of sections in 
   // the object file is different from alignment of sections in memory

   // Count cumulative number of symbols in each scope
   NumSymbols[0] = 0;
   NumSymbols[1] = NumSymbols[0] + NewSymTab[0].GetNumEntries();
   NumSymbols[2] = NumSymbols[1] + NewSymTab[1].GetNumEntries();
   NumSymbols[3] = NumSymbols[2] + NewSymTab[2].GetNumEntries();
   if (NumSymbols[3] >= 0x1000000) err.submit(2051); // Too many symbols, max = 2^24

   NewSectHeadOffset = ToFile.GetDataSize();

   // Second loop through old sections
   for (oldsec = 0; oldsec < this->NSections; oldsec++) {

      // Copy old header for convenience
      OldHeader = this->SectionHeaders[oldsec];

      if (OldHeader.sh_size == 0) {
         // Remove empty section
         // The linker has a bug with empty sections
         continue;
      }

      // Search for program data sections only
      if (OldHeader.sh_type == SHT_PROGBITS || OldHeader.sh_type == SHT_NOBITS) {

         // Reset new section header
         memset(&NewHeader, 0, sizeof(NewHeader));

         // Section name
         const char * sname = "";
         uint32_t namei = OldHeader.sh_name;
         if (namei >= this->SecStringTableLen) err.submit(2112);
         else sname = this->SecStringTable + namei;

         // Translate section name and truncate to 16 characters
         if (!stricmp(sname,".text") || !stricmp(sname,"_text")) {
            strcpy(NewHeader.sectname, "__text");
            strcpy(NewHeader.segname,  "__TEXT");
         }
         else if (!stricmp(sname,".data") || !stricmp(sname,"_data")) {
            strcpy(NewHeader.sectname, "__data");
            strcpy(NewHeader.segname,  "__DATA");
         }
         else if (!strnicmp(sname+1,"bss", 3)) {
            strcpy(NewHeader.sectname, "__bss");
            strcpy(NewHeader.segname,  "__DATA");
         }
         else if (!strnicmp(sname+1,"const", 5) || !strnicmp(sname+1,"rodata", 6)) {
            strcpy(NewHeader.sectname, "__const");
            strcpy(NewHeader.segname,  "__DATA");
         }
         else if (!strnicmp(sname, ELF_CONSTRUCTOR_NAME, 5)) {
            // Constructors
            strcpy(NewHeader.sectname, MAC_CONSTRUCTOR_NAME);
            strcpy(NewHeader.segname,  "__DATA");
            NewHeader.flags = MAC_S_MOD_INIT_FUNC_POINTERS;
         }
         else if (OldHeader.sh_flags & SHF_EXECINSTR) {
            // Other code section
            if (strlen(NewHeader.sectname) > 16) err.submit(1040, NewHeader.sectname); // Warning: name truncated
            strncpy(NewHeader.sectname, sname, 16);
            strcpy(NewHeader.segname,  "__TEXT");
         }
         else {
            // Other data section. Truncate name to 16 characters
            if (strlen(NewHeader.sectname) > 16) err.submit(1040, NewHeader.sectname); // Warning: name truncated
            strncpy(NewHeader.sectname, sname, 16);
            strcpy(NewHeader.segname,  "__DATA");
         }
         if (NewHeader.sectname[0] == '.') {
            // Make sure name begins with '_'
            NewHeader.sectname[0] = '_';
         }

         // Raw data
         NewHeader.size = OldHeader.sh_size;  // section size in file

         // File  to raw data for section
         NewHeader.offset = NewRawData.GetDataSize() + RawDataOffset;

         if (OldHeader.sh_size && OldHeader.sh_type != SHT_NOBITS) { // Not for .bss segment
            // Copy raw data
            NewRawDataOffset = NewRawData.Push(this->Buf()+(uint32_t)OldHeader.sh_offset, (uint32_t)OldHeader.sh_size); 
            NewRawData.Align(4);
         }

         // Section flags
         if (OldHeader.sh_flags & SHF_EXECINSTR) {
            NewHeader.flags |= MAC_S_ATTR_PURE_INSTRUCTIONS | MAC_S_ATTR_SOME_INSTRUCTIONS;
         }
         else if (OldHeader.sh_type == SHT_NOBITS) {
            NewHeader.flags |= MAC_S_ZEROFILL;              // .bss segment
         }

         // Alignment
         NewHeader.align = FloorLog2((uint32_t)OldHeader.sh_addralign);
         if (NewHeader.align > 12) NewHeader.align = 12;   // What is the limit for highest alignment?
         int AlignBy = 1 << NewHeader.align;

         // Align memory address
         NewVirtualAddress = (NewVirtualAddress + AlignBy - 1) & -AlignBy;

         // Virtual memory address of new section 
         NewHeader.addr = NewVirtualAddress; 
         NewVirtualAddress += (uint32_t)OldHeader.sh_size;

         // Find relocation table for this section by searching through all sections
         for (relsec = 1; relsec < this->NSections; relsec++) {

            // Get section header
            OldRelHeader = this->SectionHeaders[relsec];            
            
            // Check if this is a relocations section referring to oldsec
            if ((OldRelHeader.sh_type == SHT_REL || OldRelHeader.sh_type == SHT_RELA) // if section is relocation
            && OldRelHeader.sh_info == oldsec) { // and if section refers to current section
               Elf2MacRelocations(OldRelHeader, NewHeader, NewRawDataOffset, oldsec);
            }
         } // End of search for relocation table

         // Align raw data for next section
         NewRawData.Align(4);

         // Fix v. 2.14: adjust NewVirtualAddress to match above alignment
         NewVirtualAddress = (NewVirtualAddress + 3) & MInt(-4);

         // Store section header in file
         ToFile.Push(&NewHeader, sizeof(NewHeader));

      } // End of if section has program data

   } // End of loop through old sections

} // End of function MakeSections


template <class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation,
          class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt>
void CELF2MAC<ELFSTRUCTURES,MACSTRUCTURES>::FindUnusedSymbols() {
   // Check if symbols used, remove unused symbols

   // Allocate table OldSymbolScope and OldSymbolUsed
   NumOldSymbols = this->SymbolTableEntries;
   if (NumOldSymbols > 0 && NumOldSymbols < 0x1000000) {
      OldSymbolScope.SetNum(NumOldSymbols);
      OldSymbolScope.SetZero();
      OldSymbolUsed.SetNum(NumOldSymbols);
      OldSymbolUsed.SetZero();
   }

   // Loop through section headers
   for (uint32_t sc = 0; sc < this->NSections; sc++) {
      uint32_t entrysize = uint32_t(this->SectionHeaders[sc].sh_entsize);
      // printf("\n%2i Name: %-18s ", sc, SecStringTable + sheader.sh_name);

      if ((this->SectionHeaders[sc].sh_type==SHT_REL || this->SectionHeaders[sc].sh_type==SHT_RELA)) {
         // Relocation list
         int8_t * reltab = this->Buf() + uint32_t(this->SectionHeaders[sc].sh_offset);
         int8_t * reltabend = reltab + uint32_t(this->SectionHeaders[sc].sh_size);
         uint32_t expectedentrysize = this->SectionHeaders[sc].sh_type == SHT_RELA ? 
            sizeof(TELF_Relocation) :              // Elf32_Rela, Elf64_Rela
            sizeof(TELF_Relocation) - this->WordSize/8;  // Elf32_Rel,  Elf64_Rel
         if (entrysize < expectedentrysize) {err.submit(2033); entrysize = expectedentrysize;}

         // Loop through entries
         for (; reltab < reltabend; reltab += entrysize) {
            int isymbol = ((TELF_Relocation*)reltab)->r_sym;
            // printf("\n>SymbolUsed: %i, Name: %s",isymbol,SymbolName(isymbol)); 
            // Remember symbol used
            OldSymbolUsed[isymbol]++;
         }
      }
   }
}

template <class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation,
          class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt>
void CELF2MAC<ELFSTRUCTURES,MACSTRUCTURES>::MakeBinaryFile() {
   // Convert subfunction: Putting sections together
   // File header, segment command and section headers have been inserted.
   int i;

   // Update segment header for segment size in file
   ((TMAC_segment_command*)(ToFile.Buf()+CommandOffset))->filesize = NewRawData.GetDataSize();

   // Make symbol table command
   MAC_symtab_command symtab;
   memset(&symtab, 0, sizeof(symtab));
   symtab.cmd = MAC_LC_SYMTAB;
   symtab.cmdsize = sizeof(symtab);
   // symoff, nsyms, stroff, strsize inserted later
   // Store symtab command
   NewSymtabOffset = ToFile.Push(&symtab, sizeof(symtab));

   // Make MAC_dysymtab_command command
   MAC_dysymtab_command dysymtab;
   memset(&dysymtab, 0, sizeof(dysymtab));
   dysymtab.cmd        = MAC_LC_DYSYMTAB;
   dysymtab.cmdsize    = sizeof(dysymtab);
   dysymtab.ilocalsym  = 0;                            // index to local symbols
   dysymtab.nlocalsym  = NewSymTab[0].GetNumEntries(); // number of local symbols 
   dysymtab.iextdefsym = dysymtab.nlocalsym;           // index to externally defined symbols
   dysymtab.nextdefsym = NewSymTab[1].GetNumEntries(); // number of externally defined symbols 
   dysymtab.iundefsym  = dysymtab.iextdefsym + dysymtab.nextdefsym;	// index to public symbols
   dysymtab.nundefsym  = NewSymTab[2].GetNumEntries(); // number of public symbols
   // Store MAC_dysymtab_command command
   ToFile.Push(&dysymtab, sizeof(dysymtab));

   // Store section data
   uint32_t Current = ToFile.Push(NewRawData.Buf(), NewRawData.GetDataSize());
   if (Current != RawDataOffset) err.submit(9000);

   ToFile.Align(4);

   // Store relocation tables
   uint32_t Reltabs = ToFile.Push(NewRelocationTab.Buf(), NewRelocationTab.GetDataSize());

   // Initialize new string table. First string is empty
   NewStringTable.Push(0, 1);

   // Store symbol tables and make string table
   // Tables are not sorted alphabetically yet. This will be done subsequently
   // by CMAC2MAC
   uint32_t Symtabs = ToFile.GetDataSize();
   uint32_t NumSyms = 0;
   for (i = 0; i < 3; i++) {
      NumSyms += NewSymTab[i].GetNumEntries();
      NewSymTab[i].StoreList(&ToFile, &NewStringTable);
   }

   // Store string table
   uint32_t StringTab = ToFile.Push(NewStringTable.Buf(), NewStringTable.GetDataSize());

   // Set missing values in file header
   ((TMAC_header*)ToFile.Buf())->sizeofcmds = RawDataOffset - sizeof(TMAC_header);

   // Adjust relocation offsets in section headers
   TMAC_section* sectp = (TMAC_section*)(ToFile.Buf() + NewSectHeadOffset);
   for (i = 0; i < NumSectionsNew; i++, sectp++) {
      sectp->reloff += Reltabs;
   }

   // Set missing symoff, nsyms, stroff, strsize in symtab command
   MAC_symtab_command * symtabp = (MAC_symtab_command*)(ToFile.Buf() + NewSymtabOffset);
   symtabp->symoff  = Symtabs;
   symtabp->nsyms   = NumSyms;
   symtabp->stroff  = StringTab;
   symtabp->strsize = NewStringTable.GetDataSize();
}

template <class TELF_Header, class TELF_SectionHeader, class TELF_Symbol, class TELF_Relocation,
          class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt>
int CELF2MAC<ELFSTRUCTURES,MACSTRUCTURES>::GetImagebaseSymbol() {
   // Get symbol table index of __mh_execute_header = image base
   // Create this symbol table entry if it doesn't exist

   const char * ImageBaseName = "__mh_execute_header";
   static int ImagebaseSymbol = -1;

   if (ImagebaseSymbol >= 0) {
      // Found previously
      return ImagebaseSymbol;
   }
   // Search for name among external symbols
   int index2 = NewSymTab[2].Search(ImageBaseName);
   if (index2 >= 0) {
      // found 
      ImagebaseSymbol = index2 + NumSymbols[2];
      return ImagebaseSymbol;
   }
   // Not found. Create symbol
   NewSymTab[2].AddSymbol(NumOldSymbols, ImageBaseName, MAC_N_EXT, 0, 0, 0);
   ImagebaseSymbol = NewSymTab[2].TranslateIndex(NumOldSymbols) + NumSymbols[2];
   NumSymbols[3]++;
   return ImagebaseSymbol;
}


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