/****************************    macho.cpp    *******************************
* Author:        Agner Fog
* Date created:  2007-01-06
* Last modified: 2008-06-02
* Project:       objconv
* Module:        macho.cpp
* Description:
* Module for reading Mach-O files
*
* Class CMACHO is used for reading, interpreting and dumping Mach-O files.
*
* Copyright 2007-2008 GNU General Public License http://www.gnu.org/licenses
*****************************************************************************/
#include "stdafx.h"

// Machine names
SIntTxt MacMachineNames[] = {
   {MAC_CPU_TYPE_I386,      "Intel 32 bit"},
   {MAC_CPU_TYPE_X86_64,    "Intel 64 bit"},
   {MAC_CPU_TYPE_ARM,       "Arm"},
   {MAC_CPU_TYPE_SPARC,     "Sparc"},
   {MAC_CPU_TYPE_POWERPC,   "Power PC 32 bit"},
   {MAC_CPU_TYPE_POWERPC64, "Power PC 64 bit"}
};

// CPU subtype names
SIntTxt MacCPUSubtypeNames[] = {
   {MAC_CPU_SUBTYPE_POWERPC_ALL,  "Power PC All"},
   {MAC_CPU_SUBTYPE_I386_ALL,     "Intel All"}
};

// File type names
SIntTxt MacFileTypeNames[] = {
   {MAC_OBJECT,     "Relocatable object file"},
   {MAC_EXECUTE,    "demand paged executable file"},
   {MAC_FVMLIB,     "fixed VM shared library file"},
   {MAC_CORE,       "core file"},
   {MAC_PRELOAD,    "preloaded executable file"},
   {MAC_DYLIB,      "dynamicly bound shared library file"},
   {MAC_DYLINKER,   "dynamic link editor"},
   {MAC_BUNDLE,     "dynamicly bound bundle file"}
};

// Command type names
SIntTxt MacCommandTypeNames[] = {
   {MAC_LC_SEGMENT,        "Segment"},
   {MAC_LC_SYMTAB,         "Symbol table"},
   {MAC_LC_SYMSEG,         "gdb symbol table info (obsolete)"},
   {MAC_LC_THREAD,         "thread"},
   {MAC_LC_UNIXTHREAD,     "unix thread"},
   {MAC_LC_LOADFVMLIB,     "load a specified fixed VM shared library"},
   {MAC_LC_IDFVMLIB,       "fixed VM shared library identification"},
   {MAC_LC_IDENT,          "object identification info (obsolete)"},
   {MAC_LC_FVMFILE,        "fixed VM file inclusion (internal use)"},
   {MAC_LC_PREPAGE,        "prepage command (internal use)"},
   {MAC_LC_DYSYMTAB,       "dynamic link-edit symbol table info"},
   {MAC_LC_LOAD_DYLIB,     "load a dynamicly linked shared library"},
   {MAC_LC_ID_DYLIB,       "dynamicly linked shared lib identification"},
   {MAC_LC_LOAD_DYLINKER,  "load a dynamic linker"},
   {MAC_LC_ID_DYLINKER,    "dynamic linker identification"},
   {MAC_LC_PREBOUND_DYLIB, "modules prebound for a dynamicly linked shared library"},
   {MAC_LC_ROUTINES,       "image routines"},
   {MAC_LC_SUB_FRAMEWORK,  "sub framework"},
   {MAC_LC_SUB_UMBRELLA,   "sub umbrella"},
   {MAC_LC_SUB_CLIENT,     "sub client"},
   {MAC_LC_SUB_LIBRARY,    "sub library"},
   {MAC_LC_TWOLEVEL_HINTS, "two-level namespace lookup hints"},
   {MAC_LC_PREBIND_CKSUM,  "prebind checksum"},
   {MAC_LC_LOAD_WEAK_DYLIB&0xFF, "load a dynamically linked shared library, all symbols weak"},
   {MAC_LC_SEGMENT_64,     "64-bit segment"},
   {MAC_LC_ROUTINES_64,    "64-bit image routine"},
   {MAC_LC_UUID,           "uuid"}
};

// Relocation type names, 32 bit
SIntTxt Mac32RelocationTypeNames[] = {
   {MAC32_RELOC_VANILLA,        "Generic"},
   {MAC32_RELOC_PAIR,           "Second entry of a pair"},
   {MAC32_RELOC_SECTDIFF,       "Section diff"},
   {MAC32_RELOC_PB_LA_PTR,      "Prebound lazy ptr"},
   {MAC32_RELOC_LOCAL_SECTDIFF, "SectDif local"}
};

// Relocation type names, 64 bit
SIntTxt Mac64RelocationTypeNames[] = {
   {MAC64_RELOC_UNSIGNED,    "absolute address"},
   {MAC64_RELOC_SIGNED,      "signed 32-bit displ."},
   {MAC64_RELOC_BRANCH,      "Rel. jump 32-bit displ."},
   {MAC64_RELOC_GOT_LOAD,    "MOVQ load of a GOT entry"},
   {MAC64_RELOC_GOT,         "other GOT reference"},
   {MAC64_RELOC_SUBTRACTOR,  "Subtractor"},
   {MAC64_RELOC_SIGNED_1,    "signed 32-bit displacement with -1 addend"},
   {MAC64_RELOC_SIGNED_2,    "signed 32-bit displacement with -2 addend"},
   {MAC64_RELOC_SIGNED_4,    "signed 32-bit displacement with -4 addend"}
};

// Symbol type names
SIntTxt MacSymbolTypeNames[] = {
   {MAC_N_UNDF,    "Undefined, no section"},
   {MAC_N_ABS,     "Absolute, no section"},
   {MAC_N_SECT,    "Defined"},
   {MAC_N_PBUD,    "Prebound undefined (defined in a dylib)"},
   {MAC_N_INDR,    "Indirect"}
};

// Symbol reference type names
SIntTxt MacSymbolReferenceTypeNames[] = {
   {MAC_REF_FLAG_UNDEFINED_NON_LAZY,         "External non lazy"},
   {MAC_REF_FLAG_UNDEFINED_LAZY,             "External lazy (function call)"},
   {MAC_REF_FLAG_DEFINED,                    "Defined public"},
   {MAC_REF_FLAG_PRIVATE_DEFINED,            "Defined private"},
   {MAC_REF_FLAG_PRIVATE_UNDEFINED_NON_LAZY, "Private undefined non lazy"},
   {MAC_REF_FLAG_PRIVATE_UNDEFINED_LAZY,     "Private undefined lazy"}
};

// Symbol descriptor flag names
SIntTxt MacSymbolDescriptorFlagNames[] = {
   {MAC_REFERENCED_DYNAMICALLY, "Referenced dynamically"},
// {MAC_N_DESC_DISCARDED,       "Discarded"},
   {MAC_N_NO_DEAD_STRIP,        "Don't dead-strip"},
   {MAC_N_WEAK_REF,             "Weak external"},
   {MAC_N_WEAK_DEF,             "Weak public"}
};



// Class CMACHO members:
// Constructor
template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt>
CMACHO<MACSTRUCTURES>::CMACHO() {
   // Set everything to zero
   memset(this, 0, sizeof(*this));
}

template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt>
void CMACHO<MACSTRUCTURES>::ParseFile(){
   // Load and parse file buffer
   FileHeader = *(TMAC_header*)Buf();   // Copy file header

   // Loop through file commands
   uint32 cmd, cmdsize;
   uint32 currentoffset = sizeof(TMAC_header);
   for (uint32 i = 1; i <= FileHeader.ncmds; i++) {
      if (currentoffset >= this->GetDataSize()) {
         err.submit(2016); return;
      }
      uint8 * currentp = (uint8*)(Buf() + currentoffset);
      cmd     = ((MAC_load_command*)currentp) -> cmd;
      cmdsize = ((MAC_load_command*)currentp) -> cmdsize;
      // Interpret specific command type
      switch(cmd) {
         case MAC_LC_SEGMENT: {
            if (WordSize != 32) err.submit(2320); // mixed segment size
            MAC_segment_command_32 * sh = (MAC_segment_command_32*)currentp;
            SegmentOffset = sh->fileoff;              // File offset of segment
            SegmentSize = sh->filesize;               // Size of segment
            NumSections = sh->nsects;                 // Number of sections
            SectionHeaderOffset = currentoffset + sizeof(TMAC_segment_command); // File offset of section headers
            if (!ImageBase && strcmp(sh->segname, "__TEXT")==0) ImageBase = sh->vmaddr; // Find image base
            break;}

         case MAC_LC_SEGMENT_64: {
            if (WordSize != 64) err.submit(2320); // mixed segment size
            MAC_segment_command_64 * sh = (MAC_segment_command_64*)currentp;
            SegmentOffset = (uint32)sh->fileoff;      // File offset of segment
            SegmentSize = (uint32)sh->filesize;       // Size of segment
            NumSections = sh->nsects;                 // Number of sections
            SectionHeaderOffset = currentoffset + sizeof(TMAC_segment_command); // File offset of section headers
            if (!ImageBase && strcmp(sh->segname, "__TEXT")==0) ImageBase = sh->vmaddr; // Find image base
            break;}

         case MAC_LC_SYMTAB: {
            MAC_symtab_command * sh = (MAC_symtab_command*)currentp;
            SymTabOffset = sh->symoff;                // File offset of symbol table
            SymTabNumber = sh->nsyms;                 // Number of entries in symbol table
            StringTabOffset = sh->stroff;             // File offset of string table
            StringTabSize = sh->strsize;              // Size of string table
            break;}

         case MAC_LC_DYSYMTAB: {
            MAC_dysymtab_command * sh = (MAC_dysymtab_command*)currentp;
            ilocalsym = sh->ilocalsym;	               // index to local symbols
            nlocalsym = sh->nlocalsym;	               // number of local symbols 
            iextdefsym = sh->iextdefsym;	            // index to externally defined symbols
            nextdefsym = sh->nextdefsym;	            // number of externally defined symbols 
            iundefsym = sh->iundefsym;	               // index to undefined symbols
            nundefsym = sh->nundefsym;	               // number of undefined symbols
            IndirectSymTabOffset = sh->indirectsymoff;// file offset to the indirect symbol table
            IndirectSymTabNumber = sh->nindirectsyms; // number of indirect symbol table entries
            break;}
      }
      currentoffset += cmdsize;
   }
}

// Debug dump
template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt>
void CMACHO<MACSTRUCTURES>::Dump(int options) {
   uint32 icmd;                        // Command index
   int32  isec1;                       // Section index within segment
   int32  isec2;                       // Section index global
   int32  nsect;                        // Number of sections in segment

   if (options & DUMP_FILEHDR) {
      // File header
      printf("\nDump of Mach-O file %s", FileName);
      printf("\n-----------------------------------------------");
      printf("\nFile size: 0x%X", this->GetDataSize());
      printf("\nFile header:");
      printf("\n  CPU type: %s, subtype: %s",
         Lookup(MacMachineNames, FileHeader.cputype), 
         Lookup(MacCPUSubtypeNames, FileHeader.cpusubtype));
      
      printf("\n  File type: %s - %s", 
         GetFileFormatName(FileType), Lookup(MacFileTypeNames, FileHeader.filetype));

      printf("\n  Number of load commands: %i, Size of commands: 0x%X, Flags: %X",
         FileHeader.ncmds, FileHeader.sizeofcmds, FileHeader.flags);
   }

   uint32 cmd;                         // Load command
   uint32 cmdsize;                     // Command size
   // Pointer to current position
   uint8 * currentp = (uint8*)(Buf() + sizeof(TMAC_header));

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

      if (options & DUMP_SECTHDR) {
         // Dump command header
         printf("\n\nCommand %i: %s, size: 0x%X", icmd,
         Lookup(MacCommandTypeNames, cmd), cmdsize);

         // Interpret specific command type
         switch(cmd) {
            case MAC_LC_SEGMENT: {
               MAC_segment_command_32 * sh = (MAC_segment_command_32*)currentp;
               printf("\n  Name: %s, Memory address 0x%X, Memory size 0x%X"
                  "\n  File offset 0x%X, File size 0x%X, Maxprot 0x%X, Initprot 0x%X"
                  "\n  Number of sections %i, Flags 0x%X",
                  sh->segname, sh->vmaddr, sh->vmsize,
                  sh->fileoff, sh->filesize, sh->maxprot, sh->initprot, 
                  sh->nsects, sh->flags);
               break;}

            case MAC_LC_SEGMENT_64: {
               MAC_segment_command_64 * sh = (MAC_segment_command_64*)currentp;
               printf("\n  Name: %s, \n  Memory address 0x%08X%08X, Memory size 0x%08X%08X"
                  "\n  File offset 0x%08X%08X, File size 0x%08X%08X\n  Maxprot 0x%X, Initprot 0x%X"
                  "\n  Number of sections %i, Flags 0x%X",
                  sh->segname, (uint32)(sh->vmaddr>>32), (uint32)sh->vmaddr, 
                  (uint32)(sh->vmsize>>32), (uint32)sh->vmsize,
                  (uint32)(sh->fileoff>>32), (uint32)sh->fileoff, 
                  (uint32)(sh->filesize>>32), (uint32)sh->filesize, 
                  sh->maxprot, sh->initprot, 
                  sh->nsects, sh->flags);
               break;}

            case MAC_LC_SYMTAB: {
               MAC_symtab_command * sh = (MAC_symtab_command*)currentp;
               printf("\n  Symbol table offset 0x%X, number of symbols %i,"
                  "\n  String table offset 0x%X, String table size 0x%X",
                  sh->symoff, sh->nsyms, sh->stroff, sh->strsize);
               break;}

            case MAC_LC_DYSYMTAB: {
               MAC_dysymtab_command * sh = (MAC_dysymtab_command*)currentp;
               printf("\n  Index to local symbols %i, number of local symbols %i,"
                  "\n  Index to external symbols %i, number of external symbols %i,"
                  "\n  Index to undefined symbols %i, number of undefined symbols %i,"
                  "\n  File offset to TOC 0x%X, number of entries in TOC %i,",
                  sh->ilocalsym, sh->nlocalsym, sh->iextdefsym, sh->nextdefsym, 
                  sh->iundefsym, sh->nundefsym, sh->tocoff, sh->ntoc);
               printf("\n  File offset to module table 0x%X, Number of module table entries %i,"
                  "\n  Offset to referenced symbol table 0x%X, Number of referenced symtab entries %i"
                  "\n  Offset to indirect symbol table 0x%X, Number of indirect symtab entries %i"
                  "\n  Offset to external relocation entries 0x%X, Number of external reloc. entries %i"
                  "\n  Offset to local relocation entries 0x%X, Number of local reloc. entries %i",
                  sh->modtaboff, sh->nmodtab, sh->extrefsymoff, sh->nextrefsyms, 
                  sh->indirectsymoff, sh->nindirectsyms, sh->extreloff, sh->nextrel,
                  sh->locreloff, sh->nlocrel);	
               break;}
         }

      }
      currentp += cmdsize;
   }

   // Dump section headers
   if (options & DUMP_SECTHDR) {
      printf("\n\nSections:");

      // Reset current pointer
      currentp = (uint8*)(Buf() + sizeof(TMAC_header));
      isec2 = 0;

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

         if (cmd == MAC_LC_SEGMENT) {
            // This is a 32-bit segment command
            // Number of sections in segment
            nsect   = ((MAC_segment_command_32*)currentp) -> nsects;

            // Find first section header
            MAC_section_32 * sectp = (MAC_section_32*)(currentp + sizeof(MAC_segment_command_32));

            // Loop through section headers
            for (isec1 = 1; isec1 <= nsect; isec1++, sectp++) {
               printf("\n\nSection %i: Name: %s, Segment: %s.", 
                  ++isec2, sectp->sectname, sectp->segname);
               printf("\n  Memory address 0x%X, Size 0x%X, File offset 0x%X"
                  "\n  Alignment %i, Reloc. ent. offset 0x%X, Num reloc. %i"
                  "\n  Flags 0x%X, reserved1 0x%X, reserved2 0x%X",
                  sectp->addr, sectp->size, sectp->offset, 1 << sectp->align,
                  sectp->reloff, sectp->nreloc, sectp->flags, 
                  sectp->reserved1, sectp->reserved2);

               if (sectp->nreloc && (options & DUMP_RELTAB)) {
                  // Dump relocations
                  printf("\n  Relocations:");
                  if (sectp->reloff >= this->GetDataSize()) {err.submit(2035); break;}
                  MAC_relocation_info * relp = (MAC_relocation_info*)(Buf() + sectp->reloff);
                  for (uint32 r = 1; r <= sectp->nreloc; r++, relp++) {
                     if (relp->r_address & R_SCATTERED) {
                        // scattered relocation into
                        MAC_scattered_relocation_info * scatp = (MAC_scattered_relocation_info*)relp;

                        if (!(scatp->r_type & MAC32_RELOC_PAIR)) {
                           printf ("\n    Offset: 0x%X, Value: 0x%X, Length: %i, Scat. Type: %s",
                              scatp->r_address, scatp->r_value, 1 << scatp->r_length, 
                              Lookup(Mac32RelocationTypeNames, scatp->r_type));
                           if (scatp->r_address < sectp->size) {
                              printf(", Inline: 0x%X", *(int32*)(Buf()+sectp->offset+scatp->r_address));
                           }
                        }
                        else {
                           // Second entry of a pair
                           printf ("\n     Offset2: 0x%X, Value2: 0x%X, Length2: %i",
                              scatp->r_address, scatp->r_value, 1 << scatp->r_length);
                        }
                        if (scatp->r_pcrel) printf(", PC relative");
                     }
                     else {
                        // non-scattered
                        if (relp->r_extern) printf ("\n    Symbol number %i, ", relp->r_symbolnum);
                        else printf ("\n    Section: %i, ", relp->r_symbolnum);
                        printf ("Offset: 0x%X, ", relp->r_address);
                        if (relp->r_pcrel) printf ("PC relative, ");
                        printf ("\n     Length: %i, Extern: %i, Type: %s",
                           1 << relp->r_length, relp->r_extern,
                           Lookup(Mac32RelocationTypeNames, relp->r_type));
                        if (relp->r_address < sectp->size) {
                           printf(", Inline: 0x%X", *(int32*)(Buf()+sectp->offset+relp->r_address));
                        }
                     }
                  }
               }
            }
         }
         if (cmd == MAC_LC_SEGMENT_64) {
            // This is a 64-bit segment command
            // Number of sections in segment
            nsect   = ((MAC_segment_command_64*)currentp) -> nsects;

            // Find first section header
            MAC_section_64 * sectp = (MAC_section_64*)(currentp + sizeof(MAC_segment_command_64));

            // Loop through section headers
            for (isec1 = 1; isec1 <= nsect; isec1++, sectp++) {
               printf("\n\nSection %i: Name: %s, Segment: %s.", 
                  ++isec2, sectp->sectname, sectp->segname);
               printf("\n  Memory address 0x%X, Size 0x%X, File offset 0x%X"
                  "\n  Alignment %i, Reloc. ent. offset 0x%X, Num reloc. %i"
                  "\n  Flags 0x%X, reserved1 0x%X, reserved2 0x%X",
                  (uint32)sectp->addr, (uint32)sectp->size, sectp->offset, 1 << sectp->align,
                  sectp->reloff, sectp->nreloc, sectp->flags, 
                  sectp->reserved1, sectp->reserved2);

               if (sectp->nreloc && (options & DUMP_RELTAB)) {
                  // Dump relocations
                  printf("\n  Relocations:");
                  MAC_relocation_info * relp = (MAC_relocation_info*)(Buf() + sectp->reloff);
                  for (uint32 r = 1; r <= sectp->nreloc; r++, relp++) {
                     if (relp->r_address & R_SCATTERED) {
                        // scattered relocation into (not used in 64-bit Mach-O)
                        MAC_scattered_relocation_info * scatp = (MAC_scattered_relocation_info*)relp;
                        if (!(scatp->r_type & MAC32_RELOC_PAIR)) {
                           printf ("\n    Unexpected scattered relocation. Offset: 0x%X, Value: 0x%X, Length: %i, Scat. Type: %s",
                              scatp->r_address, scatp->r_value, 1 << scatp->r_length, 
                              Lookup(Mac64RelocationTypeNames, scatp->r_type));
                           if (scatp->r_address < sectp->size) {
                              printf(", Inline: 0x%X", *(int32*)(Buf()+sectp->offset+scatp->r_address));
                           }
                        }
                        else {
                           // Second entry of a pair
                           printf ("\n     Offset2: 0x%X, Value2: 0x%X, Length2: %i",
                              scatp->r_address, scatp->r_value, 1 << scatp->r_length);
                        }
                        if (scatp->r_pcrel) printf(", PC relative");
                     }
                     else {
                        // non-scattered
                        if (relp->r_extern) printf ("\n    Symbol number %i, ", relp->r_symbolnum);
                        else printf ("\n    Section: %i, ", relp->r_symbolnum);
                        printf ("Offset: 0x%X, ", relp->r_address);
                        if (relp->r_pcrel) printf ("PC relative, ");
                        printf ("\n     Length: %i, Extern: %i, Type: %s",
                           1 << relp->r_length, relp->r_extern,
                           Lookup(Mac64RelocationTypeNames, relp->r_type));
                        if (relp->r_type != MAC64_RELOC_SUBTRACTOR && relp->r_address < sectp->size) {
                           // Print inline addend
                           if (relp->r_length == 3) {
                              // 8 bytes inline addend
                              printf(", Inline: 0x%08X%08X", *(int32*)(Buf()+sectp->offset+relp->r_address+4), *(int32*)(Buf()+sectp->offset+relp->r_address));
                           }
                           else {
                              // 4 bytes inline addend
                              printf(", Inline: 0x%08X", *(int32*)(Buf()+sectp->offset+relp->r_address));
                           }
                        }
                     }
                  }
               }
            }
         }
         currentp += cmdsize;
      }
   }

   // pointer to string table
   char * strtab = (char*)(Buf() + StringTabOffset); 
   // pointer to symbol table
   TMAC_nlist * symp0 = (TMAC_nlist*)(Buf() + SymTabOffset);

   // Dump symbol table
   if (options & DUMP_SYMTAB) {
      printf("\n\nSymbol table:");
      uint32 i;
      TMAC_nlist * symp;

      // loop through symbol table
      for (i = 0, symp = symp0; i < SymTabNumber; i++, symp++) {

         // Header for first symbol of each category: (alphabetical within each category)
         if (i == ilocalsym && nlocalsym)   printf("\n\n  Local symbols:");
         if (i == iextdefsym && nextdefsym) printf("\n\n  Public symbols:");
         if (i == iundefsym && nundefsym)   printf("\n\n  External symbols:");

         if (symp->n_strx < StringTabSize && !(symp->n_type & MAC_N_STAB)) {
            printf("\n  %2i %s, Section %i, Value 0x%X\n    ",
               i, strtab + symp->n_strx, symp->n_sect, uint32(symp->n_value));
         }
         else {
            printf("\n  String table offset: 0x%X, Section %i, Value 0x%X\n    ",
               symp->n_strx, symp->n_sect, uint32(symp->n_value));
         }

         if (symp->n_type & MAC_N_STAB) {
            printf ("Debug symbol, stab = 0x%X, ", symp->n_type);
         }
         else {
            if (symp->n_type & MAC_N_PEXT) printf ("Private external (limited global scope), ");
            if (symp->n_type & MAC_N_EXT ) printf ("External, ");
            printf("%s", Lookup(MacSymbolTypeNames, symp->n_type & MAC_N_TYPE));
         }
         printf("\n    Reference type: %s,  Flags: ",
            Lookup(MacSymbolReferenceTypeNames, symp->n_desc & MAC_REF_TYPE));
         for (uint32 f = MAC_REFERENCED_DYNAMICALLY; f <= MAC_N_WEAK_DEF; f <<= 1) {
            if (symp->n_desc & f) {
               printf("%s, ", Lookup(MacSymbolDescriptorFlagNames, f));
            }
         }
      }
      // Check if indirect symbol table is valid
      if (IndirectSymTabNumber && IndirectSymTabOffset + IndirectSymTabNumber*4 < this->GetDataSize()) {

         // Write indirect symbol table
         printf("\n\n  Indirect symbols:");

         // loop through indirect symbol table
         uint32 * IndSymip = (uint32*)(Buf() + IndirectSymTabOffset);

         for (i = 0; i < IndirectSymTabNumber; i++, IndSymip++) {

            // Check if index within symbol table
            if (*IndSymip >= SymTabNumber) {
               //err.submit(2016); 
               printf("\n   Unknown(0x%X)", *IndSymip);
               continue;
            }
            // Find record
            TMAC_nlist * pIndSym = symp0 + *IndSymip;
            // Find name
            uint32 StringIndex = pIndSym->n_strx;
            if (StringIndex >= StringTabSize) {
               err.submit(2035); continue;
            }
            // print name
            printf("\n   %s", strtab + StringIndex);
            // print type, etc.
            printf(", type 0x%X, sect %i, desc 0x%X, val 0x%X",
               pIndSym->n_type, pIndSym->n_sect, pIndSym->n_desc, uint32(pIndSym->n_value));
         }
      }
   }

   // Dump string table
   if (options & DUMP_STRINGTB) {
      printf("\n\nString table:");
      uint32 str = 0, istr = 0;
      while (str < StringTabSize) {
         char * p = (char*)(Buf() + StringTabOffset + str);
         printf("\n  %3i: %s", str, p);
         istr++;  str += (uint32)strlen(p) + 1;
      }
   }

}

template <class TMAC_header, class TMAC_segment_command, class TMAC_section, class TMAC_nlist, class MInt>
void CMACHO<MACSTRUCTURES>::PublicNames(CMemoryBuffer * Strings, CSList<SStringEntry> * Index, int m) {
   // Make list of public names
   uint32 i;
   SStringEntry se;                    // Entry in Index

   // Interpret header:
   ParseFile();

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

   // loop through public symbol table
   TMAC_nlist * symp = (TMAC_nlist*)(Buf() + SymTabOffset + iextdefsym * sizeof(TMAC_nlist));
   for (i = 0; i < nextdefsym; i++, symp++) {
      if (symp->n_strx < StringTabSize && !(symp->n_type & MAC_N_STAB)) {
         // Public symbol found
         se.Member = m;
         // Store name
         se.String = Strings->PushString(strtab + symp->n_strx);         
         // Store name index
         Index->Push(se);
      }
   }
}

// Member functions for class MacSymbolTableBuilder

template <class TMAC_nlist, class MInt>
MacSymbolTableBuilder<TMAC_nlist, MInt>::MacSymbolTableBuilder() {                       // Constructor
   sorted = 0;
}

template <class TMAC_nlist, class MInt>
void MacSymbolTableBuilder<TMAC_nlist, MInt>::AddSymbol(int OldIndex, const char * name, int type, int Desc, int section, MInt value) {
   // Add symbol to list
   MacSymbolRecord<TMAC_nlist> rec;
   memset(&rec, 0, sizeof(rec));                 // Set to zero
   rec.n_type = (uint8)type;                     // Copy values
   rec.n_sect = (uint8)section;
   rec.n_desc = (int16)Desc;
   rec.n_value = value;
   rec.Name = StringBuffer.PushString(name);     // Copy name and store index
   rec.OldIndex = OldIndex;                      // Remember old index
   Push(&rec, sizeof(rec));                      // Put in memory buffer
   sorted = 0;                                   // Remember not sorted
}

template <class TMAC_nlist, class MInt>
void MacSymbolTableBuilder<TMAC_nlist, MInt>::SortList() {
   // Sort the list
   if (sorted) return; // allready sorted

   MacSymbolRecord<TMAC_nlist> * p = (MacSymbolRecord<TMAC_nlist>*)Buf();     // Point to list

   // Simple Bubble sort:
   int i, j;  const char * s1, * s2;
   MacSymbolRecord<TMAC_nlist> temp;
   for (i = 0; i < (int)GetNumEntries(); i++) {
      for (j = 0; j < (int)GetNumEntries() - i - 1; j++) {
         s1 = StringBuffer.Buf() + p[j].Name;
         s2 = StringBuffer.Buf() + p[j+1].Name;
         if (strcmp(s1, s2) > 0) {
            // Swap records
            temp = p[j];
            p[j] = p[j+1];
            p[j+1] = temp;
         }
      }
   }
   sorted = 1;
}

template <class TMAC_nlist, class MInt>
int MacSymbolTableBuilder<TMAC_nlist, MInt>::TranslateIndex(int OldIndex) {
   // Translate old index to new index (0-based)
   // Returns -1 if not found

   // Don't sort list. This would change indices if __mh_executer_header added later
   // if (!sorted) SortList();

   MacSymbolRecord<TMAC_nlist> * p = (MacSymbolRecord<TMAC_nlist>*)Buf();     // Point to list

   // Search through list for OldIndex
   for (int i = 0; i < (int)GetNumEntries(); i++) {
      if (p[i].OldIndex == OldIndex) {
         // Match found
         return i;
      }
   }
   // Not found
   return -1;
}

template <class TMAC_nlist, class MInt>
void MacSymbolTableBuilder<TMAC_nlist, MInt>::StoreList(CMemoryBuffer * SymbolTable, CMemoryBuffer * StringTable) {
   // Store sorted list in buffers

   // Don't sort list unless commanded to do so. Will mess up indices
   // if (!sorted) SortList();                           // Make sure list is sorted

   MacSymbolRecord<TMAC_nlist> * p = (MacSymbolRecord<TMAC_nlist>*)Buf();     // Point to list

   for (uint32 i = 0; i < GetNumEntries(); i++, p++) {
      p->n_strx = StringTable->PushString(StringBuffer.Buf()+p->Name);   // Put name in string table
      SymbolTable->Push(p, sizeof(TMAC_nlist));        // Store only the TMAC_nlist part of the record in SymbolTable
   }
}

template <class TMAC_nlist, class MInt>
int MacSymbolTableBuilder<TMAC_nlist, MInt>::Search(const char * name) {
   // Search for name. Return -1 if not found.
   MacSymbolRecord<TMAC_nlist> * p = (MacSymbolRecord<TMAC_nlist>*)Buf();     // Point to list
   for (int i = 0; i < (int)GetNumEntries(); i++) {
      if (strcmp(StringBuffer.Buf()+p[i].Name, name) == 0) {
         return i;  // Found
      }
   }
   return -1;   // Not found
}

template <class TMAC_nlist, class MInt>
MacSymbolRecord<TMAC_nlist> & MacSymbolTableBuilder<TMAC_nlist, MInt>::operator[] (uint32 i) {
   // Access member
   uint32 Offset = i * sizeof(MacSymbolRecord<TMAC_nlist>);
   if (i + sizeof(MacSymbolRecord<TMAC_nlist>) > this->GetDataSize()) {
      err.submit(9003);  Offset = 0;
   }
   return Get<MacSymbolRecord<TMAC_nlist> >(Offset);
}


/****** Class CMACUNIV for parsing Macintosh universal binary *************/
CMACUNIV::CMACUNIV() {
   // Default constructor
}


void CMACUNIV::Go(int options) {
   // Apply command options to all components

   // Check file size
   if (GetDataSize() < 28) return;

   // Read number of components
   uint32 NumComponents = EndianChange(Get<MAC_UNIV_FAT_HEADER>(0).num_arch);
   if (NumComponents == 0 || NumComponents > 10) {
      // Number of components too big or too small
      err.submit(2701, NumComponents);
      return;
   }

   uint32 i;                                     // Component number
   uint32 fo;                                    // File offset of component pointer
   CConverter ComponentBuffer;                   // Used for converting component
   CConverter OutputBuffer;                      // Temporary storage of output file
   int DesiredWordSize = cmd.DesiredWordSize;    // Desired word size, if specified on command line

   // Loop through components
   for (i = 0, fo = sizeof(MAC_UNIV_FAT_HEADER); i < NumComponents; i++, fo += sizeof(MAC_UNIV_FAT_ARCH)) {

      // Get component pointer
      MAC_UNIV_FAT_ARCH & ComponentPointer = Get<MAC_UNIV_FAT_ARCH>(fo);

      // Get offset and size of component
      uint32 ComponentOffset = EndianChange(ComponentPointer.offset);
      uint32 ComponentSize   = EndianChange(ComponentPointer.size);

      // Check within range
      if (ComponentOffset + ComponentSize > GetDataSize()) {
         err.submit(2016);
         return;
      }

      // Put component into buffer
      ComponentBuffer.Reset();
      ComponentBuffer.Push(Buf() + ComponentOffset, ComponentSize);

      // Indicate component
      printf("\n\n\nComponent file number %i:\n", i + 1);

      // Check type
      uint32 ComponentType = ComponentBuffer.GetFileType();
      if (DesiredWordSize && DesiredWordSize != ComponentBuffer.WordSize) {
         err.submit(1151, ComponentBuffer.WordSize);
      }
      else if (ComponentType != FILETYPE_MACHO_LE) {
         // Format not supported
         printf("  Format not supported: %s", GetFileFormatName(ComponentType));
      }
      else {
         // Format OK. Handle component
         if (cmd.DumpOptions == 0 && OutputBuffer.GetDataSize()) {
            // More than one component that can be converted
            err.submit(1150);
         }
         else {
            // Transfer filenames
            ComponentBuffer.FileName = FileName;
            ComponentBuffer.OutputFileName = OutputFileName;
            // Do command
            ComponentBuffer.Go();
            // Is there an output file?
            if (cmd.DumpOptions == 0) {
               // Save output file
               ComponentBuffer >> OutputBuffer;
            }
         }
      }
   }
   // Is there an output file?
   if (OutputBuffer.GetDataSize()) {
      // Take over output file and skip remaining components
      *this << OutputBuffer;
   }
}


// Make template instances for 32 and 64 bits
template class CMACHO<MAC32STRUCTURES>;
template class CMACHO<MAC64STRUCTURES>;
template class MacSymbolTableBuilder<MAC_nlist_32, int32>;
template class MacSymbolTableBuilder<MAC_nlist_64, int64>;
