/****************************    mac2mac.cpp    *****************************
* Author:        Agner Fog
* Date created:  2008-05-25
* Last modified: 2008-05-25
* Project:       objconv
* Module:        mac2mac.cpp
* Description:
* Module for changing symbol names in Mach-O file
*
* Copyright 2008 GNU General Public License http://www.gnu.org/licenses
*****************************************************************************/
#include "stdafx.h"
// All functions in this module are templated to make two versions: 32 and 64 bits.
// See instantiations at the end of this file.


// Constructor
template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt>
CMAC2MAC<MACSTRUCTURES>::CMAC2MAC() {
   // Initialize everything
   memset(this, 0, sizeof(*this));
}


// Convert()
template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt>
void CMAC2MAC<MACSTRUCTURES>::Convert() {
   MakeSymbolTable();                  // Remake symbol tables and string tables
   MakeBinaryFile();                   // Put everyting together into ToFile
   ChangeSegments();                   // Modify section names and relocation table symbol indices
   *this << ToFile;                    // Take over new file buffer
}


// MakeSymbolTable()
template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt>
void CMAC2MAC<MACSTRUCTURES>::MakeSymbolTable() {
   // Remake symbol tables and string table
   int OldScope = 0;                   // Old scope of symbol. 0=local, 1=public, 2=external
   int NewScope;                       // New scope of symbol. 0=local, 1=public, 2=external
   uint32_t symi;                        // Old index of symbol
   const char * Name1;                 // Old symbol name
   const char * Name2;                 // New symbol name
   int action;                         // Action to take on symbol
   int SymType;                        // Symbol type
   int SymDesc;                        // Symbol descriptor
   uint8_t Section;                      // Symbol section

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

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

   // loop through symbol table
   for (symi = 0; symi < this->SymTabNumber; symi++, symp++) {

      // Check indices for first symbol of each scope category
      if (symi == this->iextdefsym && this->nextdefsym) OldScope = 1;
      if (symi == this->iundefsym  && this->nundefsym)  OldScope = 2;
      NewScope = OldScope;

      if (symp->n_strx >= this->StringTabSize) {
         // Index out of range
         err.submit(2112); continue;
      }

      // Get symbol name
      Name1 = strtab + symp->n_strx;

      // Get type, descriptor and section
      SymType = symp->n_type;          // Symbol type
      SymDesc = symp->n_desc;          // Symbol descriptor
      Section = symp->n_sect;          // Symbol section

      // Check if any change required for this symbol
      action = cmd.SymbolChange(Name1, &Name2, SYMT_LOCAL + OldScope);

      switch (action) {
      case SYMA_NOCHANGE:
         // No change
         break;

      case SYMA_MAKE_WEAK:
         // Make symbol weak
         if (cmd.OutputType == FILETYPE_COFF) {
            // PE/COFF format does not support weak publics
            err.submit(2200);
         }
         // Make weak
         if (OldScope == 1) {
            SymDesc |= MAC_N_WEAK_DEF;   // Weak public. Allowed only in coalesced (communal) section
         }
         else if (OldScope == 2) {
            SymDesc |= MAC_N_WEAK_REF;   // Weak external
         }
         else {
            err.submit(1020, Name1);     // Local symbol
         }
         break;

      case SYMA_MAKE_LOCAL:
         // Make public symbol local, make external symbol ignored
         if (OldScope == 1) {
            NewScope = 0;  // Public changed to local
            SymType &= ~MAC_N_EXT;
         }
         else if (OldScope == 2) {
            Section = MAC_NO_SECT;  // External symbol. Set to 0
            SymDesc = 0;
            SymType = MAC_N_UNDF;
         }
         else err.submit(1021, Name1);
         break;

      case SYMA_CHANGE_NAME:
         // Change name of symbol
         Name1 = Name2;  Name2 = 0;
         break;

      case SYMA_ALIAS: 
         // Make alias and keep old name
         if (OldScope != 1) {
            err.submit(1022, Name1); break;
         }
         // Make alias
         NewSymbols[1].AddSymbol(-1, Name2, SymType, SymDesc, Section, symp->n_value);
         break;

      default:
         err.submit(9000); // unknown error
      }

      // Store symbol, possibly modified
      NewSymbols[NewScope].AddSymbol(symi, Name1, SymType, SymDesc, Section, symp->n_value);
   }

   // Put everything into symbol table and string table
   for (NewScope = 0; NewScope < 3; NewScope++) {
      NewSymbols[NewScope].SortList();  // Sort each list alphabetically
      NewSymbols[NewScope].StoreList(&NewSymbolTable, &NewStringTable);
   }

   // Indices to local, public and external symbols
   NewIlocalsym = 0;	                            // index to local symbols
   NewNlocalsym = NewSymbols[0].GetNumEntries(); // number of local symbols 
   NewIextdefsym = NewNlocalsym;	                // index to public symbols
   NewNextdefsym = NewSymbols[1].GetNumEntries();// number of public symbols 
   NewIundefsym = NewNlocalsym + NewNextdefsym;  // index to external symbols
   NewNundefsym = NewSymbols[2].GetNumEntries(); // number of external symbols

   // Calculate difference in size of new tables versus old tables
   // (this calculation is moved to MakeBinaryFile)
   // SizeDifference = NewSymbolTable.GetDataSize + NewStringTable.GetDataSize() 
   // - this->SymTabNumber * sizeof(TMAC_nlist) - this->StringTabSize;
}

template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt>
int CMAC2MAC<MACSTRUCTURES>::NewSymbolIndex(int32_t OldIndex) {
   // Convert subfunction: Translate old to new symbol index
   int NewIndex;
   int Scope;
   // Search for symbol in all scopes
   for (Scope = 0; Scope < 3; Scope++) {
      NewIndex = NewSymbols[Scope].TranslateIndex(OldIndex);
      if (NewIndex >= 0) {
         // OldIndex found. Add offset into appropriate table
         if (Scope == 1) NewIndex += NewIextdefsym;
         else if (Scope == 2) NewIndex += NewIundefsym;
         return NewIndex;
      }
   }
   //err.submit(2031);
   return -1;
}


template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt>
uint32_t CMAC2MAC<MACSTRUCTURES>::NewFileOffset(uint32_t OldOffset) {
   // Convert subfunction: Translate old to new file offset
   if (OldOffset <= NewSymtabOffset) {
      // Before symbol table. No change
      return OldOffset;
   }
   if (OldOffset >= OldTablesEnd) {
      // After string table. Add size difference
      return OldOffset + SizeDifference;
   }
   // Between symbol table and string table. 
   // The possibility of something between these two tables has not been accounted for
   err.submit(2052);
   return 0;
}


// MakeBinaryFile()
template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt>
void CMAC2MAC<MACSTRUCTURES>::MakeBinaryFile() {
   uint32_t OldSymtabEnd;                // End of old symbol table
   uint32_t OldStringtabEnd;             // End of old string table
   const int WordSize = sizeof(MInt) * 8;  // Word size, 32 or 64 bits

   // Offset to symbol table and string table
   NewSymtabOffset = this->SymTabOffset;
   if (this->StringTabOffset && this->StringTabOffset < NewSymtabOffset) NewSymtabOffset = this->StringTabOffset;
   if (NewSymtabOffset == 0) NewSymtabOffset = this->GetDataSize();

   // Copy all headers and all data until TablesOffset
   ToFile.Push(this->Buf(), NewSymtabOffset);
   ToFile.Align(WordSize/8);
   NewSymtabOffset = ToFile.GetDataSize(); 

   // Copy new symbol table
   ToFile.Push(NewSymbolTable.Buf(), NewSymbolTable.GetDataSize());

   // Copy new string table
   NewStringtabOffset = ToFile.GetDataSize();
   ToFile.Push(NewStringTable.Buf(), NewStringTable.GetDataSize());
   ToFile.Align(2);
   NewStringtabEnd = ToFile.GetDataSize();

   // Find end of old tables
   OldSymtabEnd = this->SymTabOffset + this->SymTabNumber * sizeof(TMAC_nlist);
   OldStringtabEnd = this->StringTabOffset + this->StringTabSize;
   OldTablesEnd = OldStringtabEnd;
   if (OldSymtabEnd > OldTablesEnd) OldTablesEnd = OldSymtabEnd;
   if (OldTablesEnd == 0) OldTablesEnd = this->GetDataSize();

   // Size difference between new and old tables
   SizeDifference = NewStringtabEnd - OldTablesEnd;

   // Is there anything in the old file after these tables?
   if (OldTablesEnd && this->GetDataSize() > OldTablesEnd) {
      // There is something after these tables. Copy it
      ToFile.Push(this->Buf() + OldTablesEnd, this->GetDataSize() - OldTablesEnd);
   }
}


// ChangeSegments()
template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt>
void CMAC2MAC<MACSTRUCTURES>::ChangeSegments() {
   // Convert subfunction: Change section names if needed and adjust all relocation tables

   uint32_t FileOffset;                  // Current offset into file
   uint32_t lcmd;                        // Load command
   uint32_t cmdsize = 0;                 // Command size
   uint32_t icmd;                        // Loop counter
   int action;                         // Name change action
   char * Name1;                       // Old name
   const char * Name2;                 // New name

   FileOffset = sizeof(TMAC_header);
   // Loop through file commands
   for (icmd = 1; icmd <= this->FileHeader.ncmds; icmd++, FileOffset += cmdsize) {
      lcmd    = ((MAC_load_command*)(ToFile.Buf() + FileOffset)) -> cmd;
      cmdsize = ((MAC_load_command*)(ToFile.Buf() + FileOffset)) -> cmdsize;

      // Interpret specific command type
      switch(lcmd) {
      case MAC_LC_SEGMENT: { // 32-bit segment
         MAC_segment_command_32 * sh = (MAC_segment_command_32*)(ToFile.Buf() + FileOffset);
         Name1 = sh->segname;
         // Check if any change required for this symbol
         action = cmd.SymbolChange(Name1, &Name2, SYMT_SECTION);
         if (action == SYMA_CHANGE_NAME) {
            // Change segment name
            if (strlen(Name2) > 16) err.submit(1040);
            strncpy(Name1, Name2, 16);
         }
         // Change section names and relocations in all sections under this segment
         ChangeSections(FileOffset + sizeof(MAC_segment_command_32), sh->nsects);
         break;}

      case MAC_LC_SEGMENT_64: { // 64-bit segment
         MAC_segment_command_64 * sh = (MAC_segment_command_64*)(ToFile.Buf() + FileOffset);
         Name1 = sh->segname;
         // Check if any change required for this symbol
         action = cmd.SymbolChange(Name1, &Name2, SYMT_SECTION);
         if (action == SYMA_CHANGE_NAME) {
            // Change segment name
            if (strlen(Name2) > 16) err.submit(1040);
            strncpy(Name1, Name2, 16);
         }
         // Change section names and relocations in all sections under this segment
         ChangeSections(FileOffset + sizeof(MAC_segment_command_64), sh->nsects);
         break;}

      case MAC_LC_SYMTAB: { // Symbol table header
         MAC_symtab_command * sh = (MAC_symtab_command*)(ToFile.Buf() + FileOffset);
         // Change table addresses
         sh->symoff = NewSymtabOffset;
         sh->nsyms = NewSymbolTable.GetDataSize() / sizeof(TMAC_nlist);
         sh->stroff = NewStringtabOffset;
         sh->strsize = NewStringtabEnd - NewStringtabOffset;
         break;}

      case MAC_LC_DYSYMTAB: { // dynamic link-edit symbol table
         MAC_dysymtab_command * sh = (MAC_dysymtab_command*)(ToFile.Buf() + FileOffset);
         // Change indices to symbol tables
         sh->ilocalsym = NewIlocalsym;
         sh->nlocalsym = NewNlocalsym;
         sh->iextdefsym = NewIextdefsym;
         sh->nextdefsym = NewNextdefsym;
         sh->iundefsym = NewIundefsym;            
         sh->nundefsym = NewNundefsym;

         // Change table addresses
         sh->tocoff = NewFileOffset(sh->tocoff);
         sh->modtaboff = NewFileOffset(sh->modtaboff);
         sh->extrefsymoff = NewFileOffset(sh->extrefsymoff);
         sh->indirectsymoff = NewFileOffset(sh->indirectsymoff);
         sh->extreloff = NewFileOffset(sh->extreloff);
         sh->locreloff = NewFileOffset(sh->locreloff);

         if (sh->nindirectsyms) {
            // Change symbol indices in import table
            ChangeImportTable(sh->indirectsymoff, sh->nindirectsyms);
         }
         break;}
      }
   }
}


template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt>
void CMAC2MAC<MACSTRUCTURES>::ChangeSections(uint32_t HeaderOffset, uint32_t Num) {
   // Convert subfunction: Change section names and relocation records if needed
   int action;                         // Name change action
   char * Name1;                       // Old name
   const char * Name2;                 // New name
   uint32_t isec1;                       // Section index
   TMAC_section * secp;                // Pointer to section header
   uint32_t irel;                        // Relocation index
   MAC_relocation_info * relp;         // Pointer to relocation record

   // Loop through section headers
   for (isec1 = 0; isec1 < Num; isec1++) {
      // Find section header
      secp = (TMAC_section*)(ToFile.Buf() + HeaderOffset + isec1*sizeof(TMAC_section));

      // Segment name
      Name1 = secp->segname;
      action = cmd.SymbolChange(Name1, &Name2, SYMT_SECTION);
      if (action == SYMA_CHANGE_NAME) {
         // Change segment name
         if (strlen(Name2) > 16) err.submit(1040);
         strncpy(Name1, Name2, 16);
      }

      // Section name
      Name1 = secp->sectname;
      action = cmd.SymbolChange(Name1, &Name2, SYMT_SECTION);
      if (action == SYMA_CHANGE_NAME) {
         // Change section name
         if (strlen(Name2) > 16) err.submit(1040);
         strncpy(Name1, Name2, 16);
      }

      // Update file offset
      secp->offset = NewFileOffset(secp->offset);

      if (secp->nreloc) {
         // This section has relocations
         // Update relocations offset
         secp->reloff = NewFileOffset(secp->reloff);

         // Pointer to relocation records
         relp = (MAC_relocation_info*)(ToFile.Buf() + secp->reloff);

         // Loop through relocations, if any
         for (irel = 0; irel < secp->nreloc; irel++, relp++) {
            // Only non-scattered r_extern relocations have symbol index
            if (!(relp->r_address & R_SCATTERED) && relp->r_extern) {
               // Update symbol index
               relp->r_symbolnum = NewSymbolIndex(relp->r_symbolnum);
            }
         }
      }
   }
}


template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt>
void CMAC2MAC<MACSTRUCTURES>::ChangeImportTable(uint32_t FileOffset, uint32_t Num) {
   // Convert subfunction: Change symbol indices in import table if needed
   uint32_t i;                           // Index
   uint32_t * p;                         // pointer to current entry

   // Find first entry
   p = (uint32_t*)(ToFile.Buf() + FileOffset);

   // Loop through table
   for (i = 0;  i < Num; i++, p++) {
      // Translate symbol index
      *p = NewSymbolIndex(*p);
   }
}


// Make template instances for 32 and 64 bits
template class CMAC2MAC<MAC32STRUCTURES>;
template class CMAC2MAC<MAC64STRUCTURES>;
