Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /****************************  library.cpp  **********************************
  2. * Author:        Agner Fog
  3. * Date created:  2006-08-27
  4. * Last modified: 2017-07-27
  5. * Project:       objconv
  6. * Module:        library.cpp
  7. * Description:
  8. * This module contains code for reading, writing and manipulating function
  9. * libraries (archives) of the UNIX type and OMF type.
  10. *
  11. * Copyright 2006-2017 GNU General Public License http://www.gnu.org/licenses
  12. *****************************************************************************/
  13.  
  14. #include "stdafx.h"
  15.  
  16. // OMF Library flag names
  17. SIntTxt OMFLibraryFlags[] = {
  18.     {0,      "Flag = 0"},
  19.     {1,      "Case sensitive"}
  20. };
  21.  
  22.  
  23. CLibrary::CLibrary() {
  24.     // Constructor
  25.     CurrentOffset = 0;
  26.     CurrentNumber = 0;
  27.     LongNames = 0;
  28.     LongNamesSize = 0;
  29.     AlignBy = 0;
  30.     MemberFileType = 0;
  31.     RepressWarnings = 0;
  32.     PageSize = 16;
  33. }
  34.  
  35.  
  36. void CLibrary::Go() {
  37.     // Do to library whatever the command line says
  38.     char const * MemberName1 = 0;  // Name of library member
  39.     char const * MemberName2 = 0;  // Modified name of library member
  40.     int action = 0;                // Action to take on member
  41.     int FileType1 = 0;             // File type of current member
  42.     int WordSize1 = 0;             // Word size of current member
  43.  
  44.     if (cmd.DumpOptions && !(cmd.LibraryOptions & CMDL_LIBRARY_EXTRACTMEM)) {
  45.         // Dump library, but not its members
  46.         Dump();
  47.         return;
  48.     }
  49.  
  50.     // Remove path form member names and check member type before extracting or adding members
  51.     AlignBy = 2;
  52.     if (GetDataSize()) FixNames();    
  53.  
  54.     if (err.Number()) return; // Stop if error
  55.  
  56.     // check LibraryOptions and whether an output file is specified
  57.     if (cmd.FileOptions & CMDL_FILE_OUTPUT) {
  58.         // Output is a library file
  59.         if ((cmd.OutputFile == 0 || cmd.OutputFile[0] == 0) && !(cmd.LibraryOptions & CMDL_LIBRARY_ADDMEMBER)) {
  60.             err.submit(2503); // Output file name missing
  61.             return;
  62.         }
  63.         // Check extension
  64.         if (strstr(cmd.OutputFile, ".lib") && strstr(cmd.OutputFile, ".LIB") && strstr(cmd.OutputFile, ".a")) {
  65.             err.submit(1101); // Warning wrong extension
  66.         }
  67.         if (cmd.OutputType >= IMPORT_LIBRARY_MEMBER) {
  68.             // Wrong output type
  69.             if (cmd.OutputType == FILETYPE_ASM) {
  70.                 // Attempt to disassemble whole library
  71.                 err.submit(2620);
  72.             }
  73.             else {
  74.                 err.submit(2621);
  75.             }
  76.             return;
  77.         }
  78.     }
  79.  
  80.     // Desired alignment = 2 for COFF and ELF, 8 for Mach-O
  81.     AlignBy = 2;
  82.     if (cmd.OutputType == FILETYPE_MACHO_LE) AlignBy = 8;
  83.  
  84.     // Determine library type and subtype
  85.     if (cmd.LibrarySubtype == 0) {    
  86.         switch (cmd.OutputType) {
  87.         case FILETYPE_OMF: case FILETYPE_OMFLIBRARY:
  88.             cmd.LibrarySubtype = LIBTYPE_OMF;  break;
  89.         case FILETYPE_COFF: case FILETYPE_DOS:
  90.             cmd.LibrarySubtype = LIBTYPE_WINDOWS;  break;
  91.         case FILETYPE_ELF:
  92.             cmd.LibrarySubtype = LIBTYPE_LINUX;  break;
  93.         case FILETYPE_MACHO_LE: case FILETYPE_MACHO_BE:
  94.             cmd.LibrarySubtype = LIBTYPE_BSD_MAC;  break;
  95.         case FILETYPE_LIBRARY:
  96.             switch (cmd.InputType) {
  97.             case FILETYPE_COFF: case FILETYPE_DOS:
  98.                 cmd.LibrarySubtype = LIBTYPE_WINDOWS;  break;
  99.             case FILETYPE_ELF:
  100.                 cmd.LibrarySubtype = LIBTYPE_LINUX;  break;
  101.             case FILETYPE_MACHO_LE: case FILETYPE_MACHO_BE:
  102.                 cmd.LibrarySubtype = LIBTYPE_BSD_MAC;  break;
  103.             }
  104.             break;
  105.         default:
  106.             cmd.LibrarySubtype = LIBTYPE_SHORTNAMES;
  107.         }
  108.     }
  109.  
  110.     // Reserve space for data buffer
  111.     DataBuffer.SetSize(GetBufferSize());
  112.  
  113.     // Check options
  114.     if (!cmd.LibraryOptions) cmd.LibraryOptions = CMDL_LIBRARY_CONVERT;
  115.  
  116.     if (cmd.Verbose) {
  117.         // Tell what we are doing
  118.         if ((cmd.LibraryOptions & CMDL_LIBRARY_ADDMEMBER) && GetDataSize() == 0) {
  119.             // Creating library
  120.             printf("\nCreating library %s (%s)", FileName, GetFileFormatName(cmd.OutputType));
  121.         }
  122.         else if (OutputFileName) {
  123.             // Print input file name
  124.             printf("\nInput library: %s", FileName);
  125.             if (cmd.InputType != cmd.OutputType) {
  126.                 // Converting library type. Print input file type
  127.                 int InType = cmd.InputType;
  128.                 if (InType == FILETYPE_LIBRARY || InType == FILETYPE_OMFLIBRARY) InType = cmd.MemberType;
  129.                 printf(", Format: %s", GetFileFormatName(InType));
  130.                 if (cmd.DesiredWordSize) printf("%i", cmd.DesiredWordSize);
  131.             }
  132.             // Print output file name and type
  133.             printf(", Output: %s, Format: %s", OutputFileName, GetFileFormatName(cmd.OutputType));
  134.             if (cmd.DesiredWordSize) printf("%i", cmd.DesiredWordSize);
  135.         }
  136.         else {
  137.             printf("\nExtracting from library file: %s", FileName);
  138.         }
  139.     }
  140.  
  141.     // Convert library or extract or add or dump all members
  142.     StartExtracting();                           // Initialize before ExtractMember()
  143.  
  144.     // Loop through input library
  145.     while ((MemberName1 = ExtractMember(&MemberBuffer)) != 0) {
  146.  
  147.         // Check if any specific action required for this member
  148.         action = cmd.SymbolChange(MemberName1, &MemberName2, SYMT_LIBRARYMEMBER);
  149.         /*
  150.         if ((action != SYMA_CHANGE_NAME && action != SYMA_EXTRACT_MEMBER) || MemberName2 == 0) {
  151.             MemberName2 = MemberName1; // No name change
  152.         } */
  153.         MemberBuffer.FileName = MemberName1;
  154.         MemberBuffer.OutputFileName = MemberName2 ? MemberName2 : MemberName1;
  155.  
  156.         if (action == SYMA_DELETE_MEMBER) {
  157.             // Remove this member from library
  158.             if (cmd.Verbose) {
  159.                 printf("\nRemoving member %s from library", MemberName1);
  160.             }
  161.             continue;
  162.         }
  163.         if (action == SYMA_ADD_MEMBER) {
  164.             // Replace this member with new file
  165.             // (Message comes later when adding new member)
  166.             continue;
  167.         }
  168.  
  169.         // Check type of this member
  170.         FileType1 = MemberBuffer.GetFileType();
  171.         if (FileType1 == 0) continue;
  172.         WordSize1 = MemberBuffer.WordSize;
  173.  
  174.         if (!(cmd.LibraryOptions & (CMDL_LIBRARY_EXTRACTMEM | CMDL_LIBRARY_ADDMEMBER))) {
  175.             // Not adding or extracting members. Apply conversion options to all members
  176.             if (cmd.SymbolChangesRequested() || FileType1 != cmd.OutputType) {
  177.  
  178.                 // Check file type before conversion
  179.                 int FileType0 = MemberBuffer.GetFileType();
  180.                 // Conversion or name change requested
  181.                 MemberBuffer.Go();                   // Do required conversion
  182.                 if (err.Number()) break;             // Stop if error
  183.                 // Check type again after conversion
  184.                 FileType1 = MemberBuffer.GetFileType();
  185.                 if (MemberBuffer.OutputFileName == 0 || FileType1 != FileType0) {
  186.                     MemberBuffer.OutputFileName = MemberBuffer.SetFileNameExtension(MemberBuffer.FileName);
  187.                 }
  188.                 MemberBuffer.FileName = MemberBuffer.OutputFileName;
  189.             }
  190.         }
  191.         if (MemberFileType == 0) MemberFileType = FileType1;
  192.         if (WordSize == 0) WordSize = WordSize1;
  193.         if (FileType1 != MemberFileType || WordSize1 != WordSize) {
  194.             if (WordSize1 == 0) {
  195.                 // Library member has no wordsize
  196.                 err.submit(1109, MemberBuffer.FileName);
  197.             }
  198.             else {
  199.                 // Library members have different type or word size
  200.                 err.submit(1102);
  201.             }
  202.         }
  203.  
  204.         if (cmd.LibraryOptions & CMDL_LIBRARY_EXTRACTMEM) {
  205.             // Extract member(s)
  206.             if (action == SYMA_EXTRACT_MEMBER || cmd.LibraryOptions == CMDL_LIBRARY_EXTRACTALL) {
  207.                 // Extract this member
  208.                 if (cmd.DumpOptions == 0 && cmd.OutputType != CMDL_OUTPUT_DUMP) {
  209.                     // Write this member to file
  210.                     if (err.Number()) return; // Check first if error
  211.  
  212.                     if (cmd.SymbolChangesRequested() || FileType1 != cmd.OutputType) {
  213.                         // Conversion or name change requested
  214.  
  215.                         // Check type before conversion
  216.                         int FileType0 = MemberBuffer.GetFileType();
  217.                         MemberBuffer.Go();
  218.                         if (err.Number()) return; // Stop if error
  219.                         // Check type after conversion
  220.                         FileType1 = MemberBuffer.GetFileType();
  221.                         if (MemberBuffer.OutputFileName == 0 /*|| FileType1 != FileType0*/) {
  222.                             MemberBuffer.OutputFileName = MemberBuffer.SetFileNameExtension(MemberBuffer.FileName);
  223.                         }
  224.                     }
  225.                     if (MemberBuffer.OutputFileName == 0) {
  226.                         MemberBuffer.OutputFileName = MemberBuffer.FileName;
  227.                     }
  228.                     if (cmd.Verbose) {
  229.                         // Tell what we are doing
  230.                         if (MemberName1 == MemberName2) {
  231.                             printf("\nExtracting file %s from library", MemberName1);
  232.                         }
  233.                         else {
  234.                             printf("\nExtracting library member %s to file %s", MemberName1, MemberBuffer.OutputFileName);
  235.                         }
  236.                     }
  237.                     if (WordSize1 == 0) {
  238.                         err.submit(1109, MemberName1);
  239.                     }
  240.                     // Write this member to file
  241.                     MemberBuffer.Write();
  242.                 }
  243.                 else {
  244.                     // Dump this member
  245.                     MemberBuffer.Go();
  246.                 }
  247.             }
  248.         }
  249.         else if (cmd.DumpOptions == 0) {
  250.             // Add member to new library
  251.             if (MemberName2 == 0) MemberName2 = MemberName1;
  252.             if (cmd.Verbose) {
  253.                 // Tell what we are doing
  254.                 if (strcmp(MemberName1, MemberName2) != 0) {
  255.                     printf("\nRenaming member %s to %s", MemberName1, MemberName2);
  256.                 }
  257.             }
  258.             // Put into new library
  259.             InsertMember(&MemberBuffer);
  260.         }
  261.     } // End of loop through library
  262.     // Stop if error
  263.     if (err.Number()) return;
  264.  
  265.     if (cmd.LibraryOptions & CMDL_LIBRARY_ADDMEMBER) {
  266.         // Add object files to library
  267.         SSymbolChange const * sym;
  268.         // Loop through file names to add
  269.         while ((sym = cmd.GetMemberToAdd()) != 0) {
  270.             MemberBuffer.Reset();                     // Reset MemberBuffer
  271.             // Name of object file
  272.             MemberBuffer.FileName = sym->Name2;       // Name of object file
  273.             MemberBuffer.OutputFileName = sym->Name1; // Name of new member
  274.             // Read object file
  275.             MemberBuffer.Read();
  276.             // Stop if read failed
  277.             if (err.Number()) continue;
  278.             // Detect file type
  279.             int NewMemberType = MemberBuffer.GetFileType();
  280.             if (cmd.Verbose) {
  281.                 // Tell what we are doing
  282.                 if (sym->Done) {
  283.                     printf("\nReplacing member %s with file %s", sym->Name1, sym->Name2);
  284.                 }
  285.                 else {
  286.                     printf("\nAdding member %s from file %s", sym->Name1, sym->Name2);
  287.                 }
  288.                 if (NewMemberType != cmd.OutputType) {
  289.                     // Converting type
  290.                     printf(". Converting from %s.", GetFileFormatName(NewMemberType));
  291.                 }
  292.             }
  293.             // Do any conversion required
  294.             MemberBuffer.Go();
  295.  
  296.             // Stop if error
  297.             if (err.Number()) continue;
  298.  
  299.             // Check if file type is right after conversion
  300.             MemberFileType = MemberBuffer.FileType;
  301.             if (WordSize == 0) WordSize = MemberBuffer.WordSize;
  302.             if (MemberFileType != cmd.OutputType) {
  303.                 // Library members have different type
  304.                 err.submit(2504, GetFileFormatName(MemberBuffer.FileType)); continue;
  305.             }
  306.             if (MemberBuffer.WordSize != WordSize) {
  307.                 // Library members have different word size
  308.                 err.submit(2505, MemberBuffer.WordSize); continue;
  309.             }
  310.             // Put into library
  311.             MemberBuffer.FileName = MemberBuffer.OutputFileName;
  312.             InsertMember(&MemberBuffer);
  313.         } // End of loop through object file names
  314.     }
  315.     // Stop if error
  316.     if (err.Number()) return;
  317.  
  318.     if (cmd.LibraryOptions & CMDL_LIBRARY_EXTRACTMEM) {
  319.         cmd.CheckExtractSuccess();  // Check if members to extract were found
  320.     }
  321.  
  322.     if (cmd.FileOptions & CMDL_FILE_OUTPUT) {
  323.         // Make output library
  324.         MakeBinaryFile();
  325.         // Take over OutFile buffer
  326.         *this << OutFile;
  327.     }
  328.     else {
  329.         // No output library
  330.         OutputFileName = 0;
  331.     }
  332. }
  333.  
  334. void CLibrary::FixNames() {
  335.     // Rebuild library or fix member names
  336.     // Dispatch according to library type
  337.     switch (cmd.InputType) {
  338.     case FILETYPE_OMF: case FILETYPE_OMFLIBRARY:
  339.         // Rebuild OMF style library and fix long member names
  340.         RebuildOMF();  break;
  341.  
  342.     case FILETYPE_LIBRARY:
  343.     default:
  344.         // Fix names in UNIX style library
  345.         StripMemberNamesUNIX();  break;
  346.     }
  347. }
  348.  
  349. /*  Unused:
  350. void CLibrary::Rebuild() {
  351.     // Rebuild library: fix member names
  352.     // Dispatch according to library type
  353.     switch (cmd.InputType) {
  354.     case FILETYPE_OMF: case FILETYPE_OMFLIBRARY:
  355.         // Rebuild OMF style library
  356.         RebuildOMF();  break;
  357.  
  358.     case FILETYPE_LIBRARY:
  359.     default:
  360.         // Rebuild UNIX style library
  361.         RebuildUNIX();  break;
  362.     }
  363. }
  364.  
  365. void CLibrary::RebuildUNIX() {
  366.     // Rebuild UNIX style library
  367.     // Make member names unique and without path. Rebuild symbol table
  368.     char const * MemberName1 = 0;  // Name of library member
  369.     uint32 OutputFileType;
  370.  
  371.     // Save OutputType before changing it
  372.     OutputFileType = cmd.OutputType;
  373.  
  374.     // Loop through input library
  375.     CurrentOffset = 8;  CurrentNumber = 0;
  376.     while ((MemberName1 = ExtractMember(&MemberBuffer)) != 0) {
  377.         // Properties of member
  378.         MemberBuffer.FileName = MemberBuffer.OutputFileName = MemberName1;
  379.         // Check if import library
  380.         if (MemberBuffer.Get<uint32>(0) == 0xFFFF0000) {
  381.             // Import library. Cannot do anything sensible
  382.             err.submit(2507, cmd.InputFile);  return;
  383.         }
  384.         // Remember member type
  385.         cmd.MemberType = MemberBuffer.GetFileType();
  386.         // Temporarily set OutputType to MemberType
  387.         cmd.OutputType = cmd.MemberType;
  388.         // Put into new library
  389.         InsertMember(&MemberBuffer);
  390.     }
  391.     // Avoid getting warnings twice for duplicate symbols
  392.     RepressWarnings = 1;
  393.     // Make library header etc. and add all members to OutFile
  394.     MakeBinaryFile();
  395.     RepressWarnings = 0;
  396.  
  397.     // Restore OutputType
  398.     cmd.OutputType = OutputFileType;
  399.  
  400.     // Replace file buffer by OutFile
  401.     *this << OutFile;
  402.  
  403.     // Clear buffers used for building OutFile
  404.     StringBuffer.SetSize(0);
  405.     StringEntries.SetNum(0);
  406.     Indexes.SetNum(0);
  407.     DataBuffer.SetSize(0);
  408. }
  409. */
  410.  
  411. void CLibrary::RebuildOMF() {
  412.     // Rebuild OMF style library.
  413.     // Removes paths from member names, truncates to 16 characters, and makes unique.
  414.     // Symbol table is removed, not remade
  415.  
  416.     SOMFRecordPointer rec;                        // Current OMF record
  417.     char * ModuleName;                            // Module name
  418.     SStringEntry se;                              // String entry record to save
  419.     COMFFileBuilder NewBuffer;                    // Buffer for rebuilt library
  420.     uint32 Align;                                 // Alignment
  421.  
  422.     // Remember member file type
  423.     cmd.MemberType = FILETYPE_OMF;
  424.  
  425.     // Initialize record pointer
  426.     rec.Start(Buf(), 0, GetDataSize());
  427.  
  428.     // Read header
  429.     if (rec.Type2 != OMF_LIBHEAD) {err.submit(2500); return;} // Does not start with library header
  430.  
  431.     // Read library header
  432.     DictionaryOffset = rec.GetDword();
  433.     DictionarySize = rec.GetWord();
  434.     if ((uint64)DictionaryOffset + DictionarySize >= GetDataSize()) {err.submit(2035); return;}
  435.  
  436.     rec.GetByte(); // Ignore flag
  437.     // Page size / alignment for members
  438.     PageSize = rec.End + 1;
  439.     Align = 1 << FloorLog2(PageSize);       // Make power of 2
  440.     if (PageSize != Align) {
  441.         err.submit(2601, PageSize);          // Error: not a power of 2
  442.     }
  443.  
  444.     // Copy library header to new buffer
  445.     NewBuffer.Push(Buf(), PageSize);
  446.  
  447.     // Reset record loop end when DictionaryOffset is known
  448.     rec.FileEnd = DictionaryOffset;
  449.  
  450.     // Next record is start of first module
  451.     rec.GetNext();
  452.     if (rec.Type2 != OMF_THEADR) err.submit(2500);  // Member does not start with THEADR
  453.  
  454.     // Loop through the records of all OMF modules
  455.     do {
  456.         // Check record type
  457.         switch (rec.Type2) {
  458.  
  459.         case OMF_THEADR:     // Start of module
  460.  
  461.             // Get name
  462.             ModuleName = rec.GetString();
  463.  
  464.             // Remove path, truncate name and make unique
  465.             ModuleName = ShortenMemberName(ModuleName);
  466.  
  467.             // Remember name to check for duplicates
  468.             // Not needed any more:
  469.             se.String = StringBuffer.PushString(ModuleName);
  470.             se.Member = NewBuffer.GetDataSize();
  471.             StringEntries.Push(se);
  472.  
  473.             // Make new THEADR record
  474.             NewBuffer.StartRecord(OMF_THEADR);
  475.             NewBuffer.PutString(ModuleName);
  476.             NewBuffer.EndRecord();
  477.             break;
  478.  
  479.         case OMF_MODEND: case OMF_LIBEND:   // End of module or library
  480.             // Copy MODEND record
  481.             NewBuffer.Push(Buf() + rec.FileOffset, rec.End + 1);
  482.  
  483.             // Align output file by PageSize
  484.             NewBuffer.Align(PageSize);
  485.             break;
  486.  
  487.         default:   // Any other record in module
  488.             // Copy record unchanged
  489.             NewBuffer.Push(Buf() + rec.FileOffset, rec.End + 1);
  490.             break;
  491.         }
  492.     }  // Point to next record
  493.     while (rec.GetNext(PageSize));                // End of loop through records
  494.  
  495.     // Put dictionary offset in LIBHEAD record
  496.     DictionaryOffset = NewBuffer.GetDataSize();
  497.     NewBuffer.Get<uint32>(3) = DictionaryOffset;
  498.  
  499.     // Take over modified library file
  500.     *this << NewBuffer;
  501.  
  502.     // Empty used buffers
  503.     StringEntries.SetNum(0);
  504.     StringBuffer.SetSize(0);
  505. }
  506.  
  507.  
  508. void CLibrary::StripMemberNamesUNIX() {
  509.     // Remove path from member names, set extension to default
  510.     char * MemberName1 = 0;  // Name of library member
  511.  
  512.     // Loop through input library
  513.     CurrentOffset = 8;  CurrentNumber = 0;
  514.     while ((MemberName1 = ExtractMember(&MemberBuffer)) != 0) {
  515.         // Properties of member
  516.         // Check if import library
  517.         if (MemberBuffer.Get<uint32>(0) == 0xFFFF0000) {
  518.             // Import library. Cannot do anything sensible
  519.             err.submit(2507, cmd.InputFile);  return;
  520.         }
  521.         if (MemberName1[0] == '/') continue;  // names record
  522.         // remember member type
  523.         if (cmd.MemberType == 0) {
  524.             cmd.MemberType = MemberBuffer.GetFileType();
  525.         }
  526.         // Fix name
  527.         StripMemberName(MemberName1);
  528.         // Check word size
  529.         if (cmd.DesiredWordSize == 0) {
  530.             cmd.DesiredWordSize = MemberBuffer.WordSize;
  531.         }
  532.         else if (cmd.DesiredWordSize != MemberBuffer.WordSize && MemberBuffer.WordSize != 0) {
  533.             err.submit(2012, MemberBuffer.WordSize, cmd.DesiredWordSize);  // wrong word size
  534.             return;
  535.         }
  536.         if (cmd.OutputType == FILETYPE_LIBRARY || cmd.OutputType == FILETYPE_OMFLIBRARY) {
  537.             cmd.OutputType = cmd.MemberType;
  538.         }
  539.     }
  540. }
  541.  
  542.  
  543. void CLibrary::Dump() {
  544.     // Print contents of library
  545.  
  546.     // Dispatch according to library type
  547.     switch (cmd.InputType) {
  548.     case FILETYPE_LIBRARY:
  549.         DumpUNIX();  break;                        // Print contents of UNIX style library
  550.  
  551.     case FILETYPE_OMFLIBRARY:
  552.         DumpOMF();   break;                        // Print contents of OMF style library
  553.  
  554.     default:
  555.         err.submit(9000);                          // Should not occur
  556.     }
  557. }
  558.  
  559. void CLibrary::DumpOMF() {
  560.     // Print contents of OMF style library
  561.     uint8  Flags;                                 // Dictionary flags
  562.     uint32 i;                                     // Loop counter
  563.     uint32 Align;                                 // Member alignment
  564.     uint32 RecordEnd;                             // End of OMF record
  565.     SOMFRecordPointer rec;                        // Current OMF record
  566.     char * MemberName;                            // Name of library member
  567.     char * SymbolName;                            // Name of public symbol in member
  568.     uint32 MemberStart = 0;                       // Start of member
  569.     uint32 MemberEnd;                             // End of member
  570.     uint32 MemberNum = 0;                         // Member number
  571.     uint32 FirstPublic;                           // Index to first public name of current member
  572.     CMemoryBuffer Strings;                        // Local string buffer
  573.     CSList<SStringEntry> MemberIndex;             // Local member index buffer
  574.     COMF Member;                                  // Local buffer for member
  575.  
  576.     DictionaryOffset = GetDataSize();             // Loop end. This value is changed when library header is read
  577.     rec.Start(Buf(), 0, DictionaryOffset);        // Initialize record pointer
  578.  
  579.     PageSize = 0;
  580.  
  581.     printf("\nDump of library %s\nExported symbols by member:\n", cmd.InputFile);
  582.  
  583.     // Loop through the records of all OMF modules
  584.     do {
  585.         // Check record type
  586.         switch (rec.Type2) {
  587.  
  588.         case OMF_LIBHEAD:  // Library header. This should be the first record
  589.             if (PageSize || rec.FileOffset > 0) {
  590.                 err.submit(2600);  break;            // More than one header
  591.             }
  592.             // Read library header
  593.             DictionaryOffset = rec.GetDword();
  594.             DictionarySize = rec.GetWord();
  595.             if ((uint64)DictionaryOffset + DictionarySize >= GetDataSize()) {err.submit(2035); return;}
  596.             Flags = rec.GetByte();
  597.             // Page size / alignment for members
  598.             PageSize = rec.End + 1;
  599.             Align = 1 << FloorLog2(PageSize);       // Make power of 2
  600.             if (PageSize != Align) {
  601.                 err.submit(2601, PageSize);          // Error: not a power of 2
  602.             }
  603.             // Reset record loop end when DictionaryOffset is known
  604.             rec.FileEnd = DictionaryOffset;
  605.  
  606.             // Print values from LIBHEAD
  607.             printf("\nOMF Library. Page size %i. %s.",
  608.                 PageSize, Lookup(OMFLibraryFlags, Flags));
  609.             break;
  610.  
  611.         case OMF_THEADR: // Module header. Member starts here
  612.             MemberName = rec.GetString();           // Get name
  613.             MemberStart = rec.FileOffset;           // Get start address
  614.             printf("\nMember %s Offset 0x%X", MemberName, MemberStart);// Print member name
  615.             break;
  616.  
  617.         case OMF_MODEND: // Member ends here.
  618.             RecordEnd = rec.FileOffset + rec.End + 1;// End of record
  619.             MemberEnd = RecordEnd;                   // = member end address
  620.  
  621.             // Store member in temporary buffer
  622.             Member.SetSize(0);
  623.             Member.Push(Buf() + MemberStart, MemberEnd - MemberStart);
  624.  
  625.             // Get public names from member
  626.             FirstPublic = MemberIndex.GetNumEntries();
  627.             Member.PublicNames(&Strings, &MemberIndex, ++MemberNum);
  628.  
  629.             // Print public names
  630.             for (i = FirstPublic; i < MemberIndex.GetNumEntries(); i++) {
  631.                 SymbolName = Strings.Buf() + MemberIndex[i].String;
  632.                 printf("\n  %s", SymbolName);
  633.             }
  634.             // Align next member by PageSize;
  635.             MemberEnd = (MemberEnd + PageSize - 1) & - (int32)PageSize;
  636.             rec.End = MemberEnd - rec.FileOffset - 1;
  637.             break;
  638.  
  639.         case OMF_LIBEND: // Last member should end here
  640.             RecordEnd = rec.FileOffset + rec.End + 1;// End of record
  641.             if (RecordEnd != DictionaryOffset) err.submit(2602);
  642.             break;
  643.         }    
  644.     }  // Go to next record
  645.     while (rec.GetNext());                        // End of loop through records
  646.  
  647.     // Check hash table integrity
  648.     CheckOMFHash(Strings, MemberIndex);
  649.  
  650.     // Check if there is an extended library dictionary
  651.     uint32 ExtendedDictionaryOffset = DictionaryOffset + DictionarySize * 512;
  652.  
  653.     if (ExtendedDictionaryOffset > GetDataSize()) {
  654.         err.submit(2500);                          // File is truncated
  655.     }
  656.     if (ExtendedDictionaryOffset < GetDataSize()) {
  657.         // Library contains extended dictionary
  658.         uint32 ExtendedDictionarySize = GetDataSize() - ExtendedDictionaryOffset;
  659.         uint8 DictionaryType = Get<uint8>(ExtendedDictionaryOffset); // Read first byte of extended dictionary
  660.         if (DictionaryType == OMF_LIBEXT) {
  661.             // Extended dictionary in the official format
  662.             printf("\nExtended dictionary IBM/MS format. size %i", ExtendedDictionarySize);
  663.         }
  664.         else if (ExtendedDictionarySize >= 10 && (DictionaryType == 0xAD || Get<uint16>(ExtendedDictionaryOffset + 2) == MemberNum)) {
  665.             // Extended dictionary in the proprietary Borland format, documented only in US Patent 5408665, 1995
  666.             printf("\nExtended dictionary Borland format. size %i", ExtendedDictionarySize);
  667.         }
  668.         else {
  669.             // Unknown format
  670.             printf("\nExtended dictionary size %i, unknown type 0x%02X",
  671.                 ExtendedDictionarySize, DictionaryType);
  672.         }
  673.     }
  674. }
  675.  
  676. void CLibrary::DumpUNIX() {
  677.     // Print contents of UNIX style library
  678.  
  679.     const char * MemberName = 0;
  680.     CurrentOffset = 8;  CurrentNumber = 0;
  681.  
  682.     printf("\nDump of library %s", cmd.InputFile);
  683.  
  684.     if (cmd.DumpOptions & DUMP_SECTHDR) {
  685.         // dump headers
  686.         SUNIXLibraryHeader * Header = 0;    // Member header
  687.         uint32 MemberSize = 0;              // Size of member
  688.         //uint32 HeaderExtra = 0;             // Extra added to size of header
  689.         //uint32 NameIndex;                 // Index into long names member
  690.         char * Name = 0;                    // Name of member
  691.         int symindex = 0;                   // count symbol index records
  692.         int i;                              // loop counter
  693.  
  694.         // Search for member
  695.         while (CurrentOffset) {
  696.             //HeaderExtra = 0;
  697.             // Extract next library member from input library
  698.             Header = &Get<SUNIXLibraryHeader>(CurrentOffset);
  699.             // Size of member
  700.             MemberSize = (uint32)atoi(Header->FileSize);
  701.             // Member name
  702.             Name = Header->Name;
  703.             // Terminate name
  704.             for (i = 0; i < 15; i++) {
  705.                 if (Name[i] == ' ') {
  706.                     Name[i+1] = 0;  break;
  707.                 }
  708.             }
  709.             if (i == 16) Name[i] = 0;
  710.  
  711.             if (strncmp(Name, "//", 2) == 0) {
  712.                 // This is the long names member.
  713.                 printf("\nLongnames header \"%s\". Offset 0x%X, size 0x%X", Name,
  714.                     CurrentOffset + (uint32)sizeof(SUNIXLibraryHeader), MemberSize);
  715.             }
  716.             else if (Name[0] == '/' && Name[1] <= ' ') {
  717.                 // Symbol index
  718.                 printf("\nSymbol index %i, \"%s\"", ++symindex, Name);
  719.             }
  720.             else if (strncmp(Name, "__.SYMDEF", 9) == 0) {
  721.                 // Mac/BSD Symbol index
  722.                 printf("\nSymbol index %i, \"%s\"", ++symindex, Name);
  723.             }
  724.             else if (strncmp(Name, "#1/", 3) == 0) {
  725.                 // Name refers to long name after the header
  726.                 // This variant is used by Mac and some versions of BSD
  727.                 //HeaderExtra = atoi(Name+3);
  728.                 Name += sizeof(SUNIXLibraryHeader);
  729.                 if (strncmp(Name, "__.SYMDEF", 9) == 0) {
  730.                     // Symbol table "__.SYMDEF SORTED" as long name
  731.                     printf("\nSymbol index %i, \"%s\"", ++symindex, Name);
  732.                 }
  733.             }
  734.             else break;
  735.             // Point to next member
  736.             CurrentOffset = NextHeader(CurrentOffset);
  737.         }
  738.         CurrentOffset = 8;  CurrentNumber = 0;
  739.     }
  740.  
  741.     printf("\n\nExported symbols by member:\n");
  742.  
  743.     // Loop through library
  744.     while (CurrentOffset + sizeof(SUNIXLibraryHeader) < DataSize) {
  745.  
  746.         // Reset buffers
  747.         StringBuffer.SetSize(0);
  748.         MemberBuffer.Reset();
  749.         StringEntries.SetNum(0);
  750.  
  751.         // Get member name
  752.         MemberName = ExtractMember(&MemberBuffer);
  753.         if (MemberName == 0) break;
  754.         printf("\nMember %s", MemberName);
  755.         MemberBuffer.FileName = MemberName;
  756.  
  757.         // Detect file type of member
  758.         MemberFileType = MemberBuffer.GetFileType();
  759.  
  760.         WordSize = MemberBuffer.WordSize;
  761.         printf (" - %s", GetFileFormatName(MemberFileType));
  762.         if (WordSize) {
  763.             printf("-%i", MemberBuffer.WordSize);
  764.         }
  765.         else {
  766.             printf(". Type not specified. Possibly alias record");
  767.         }
  768.  
  769.         // Get symbol table for specific file type
  770.         switch (MemberFileType) {
  771.         case FILETYPE_ELF:
  772.             if (WordSize == 32) {
  773.                 // Make instance of file parser, 32 bit template
  774.                 CELF<ELF32STRUCTURES> elf;
  775.                 MemberBuffer >> elf;        // Transfer MemberBuffer to elf object
  776.                 elf.PublicNames(&StringBuffer, &StringEntries, 0);
  777.                 break;
  778.             }
  779.             else {
  780.                 // Make instance of file parser, 64 bit template
  781.                 CELF<ELF64STRUCTURES> elf;
  782.                 MemberBuffer >> elf;        // Transfer MemberBuffer to elf object
  783.                 elf.PublicNames(&StringBuffer, &StringEntries, 0);
  784.                 break;
  785.             }
  786.  
  787.         case FILETYPE_COFF: {
  788.             CCOFF coff;
  789.             MemberBuffer >> coff;       // Transfer MemberBuffer to coff object
  790.             coff.PublicNames(&StringBuffer, &StringEntries, 0);
  791.             break;}
  792.  
  793.         case FILETYPE_MACHO_LE:
  794.             if (WordSize == 32) {
  795.                 // Make instance of file parser, 32 bit template
  796.                 CMACHO<MAC32STRUCTURES> mac;
  797.                 MemberBuffer >> mac;       // Transfer MemberBuffer to coff object
  798.                 mac.PublicNames(&StringBuffer, &StringEntries, 0);
  799.                 break;
  800.             }
  801.             else {
  802.                 // Make instance of file parser, 64 bit template
  803.                 CMACHO<MAC64STRUCTURES> mac;
  804.                 MemberBuffer >> mac;       // Transfer MemberBuffer to coff object
  805.                 mac.PublicNames(&StringBuffer, &StringEntries, 0);
  806.                 break;
  807.             }
  808.  
  809.         case IMPORT_LIBRARY_MEMBER: {
  810.             // This is an import library
  811.             char * name1 = MemberBuffer.Buf() + 20;
  812.             printf("\n  Import %s from %s", name1, name1 + strlen(name1) + 1);
  813.             break;}
  814.  
  815.         default:
  816.             printf("\n   Cannot extract symbol names from this file type");
  817.             break;
  818.         }
  819.  
  820.         // Loop through table of public names
  821.         for (uint32 i = 0; i < StringEntries.GetNumEntries(); i++) {
  822.             uint32 j = StringEntries[i].String;
  823.             printf("\n   %s", StringBuffer.Buf() + j);
  824.         }
  825.     }
  826. }
  827.  
  828.  
  829. uint32 CLibrary::NextHeader(uint32 Offset) {
  830.  
  831.     // Loop through library headers.
  832.     // Input = current offset. Output = next offset
  833.     SUNIXLibraryHeader * Header;   // Member header
  834.     int32 MemberSize;          // Size of member
  835.     //uint32 HeaderExtra = 0;    // Extra added to size of header
  836.     uint32 NextOffset;         // Offset of next header
  837.  
  838.     if (Offset + sizeof(SUNIXLibraryHeader) >= DataSize) {
  839.         // No more members
  840.         return 0;
  841.     }
  842.     // Find header
  843.     Header = &Get<SUNIXLibraryHeader>(Offset);
  844.  
  845.     // Size of member
  846.     MemberSize = atoi(Header->FileSize);
  847.     if (MemberSize < 0 || MemberSize + Offset + sizeof(SUNIXLibraryHeader) > DataSize) {
  848.         err.submit(2500);  // Points outside file
  849.         return 0;
  850.     }
  851.     if (strncmp(Header->Name, "#1/", 3) == 0) {
  852.         // Name refers to long name after the header
  853.         // This variant is used by Mac and some versions of BSD.
  854.         // HeaderExtra is included in MemberSize:
  855.         // HeaderExtra = atoi(Header->Name+3);
  856.     }
  857.  
  858.     // Get next offset
  859.     NextOffset = Offset + sizeof(SUNIXLibraryHeader) + MemberSize;
  860.     // Round up to align by 2
  861.     NextOffset = (NextOffset + 1) & ~ 1;
  862.     // Check if last
  863.     if (NextOffset >= DataSize) NextOffset = 0;
  864.     return NextOffset;
  865. }
  866.  
  867.  
  868. void CLibrary::StartExtracting() {
  869.     // Initialize before ExtractMember()
  870.     if (cmd.InputType == FILETYPE_OMFLIBRARY) {
  871.         SOMFRecordPointer rec;                     // OMF records
  872.         rec.Start(Buf(), 0, GetDataSize());        // Initialize record pointer
  873.         if (rec.Type2 != OMF_LIBHEAD) {
  874.             err.submit(2500);  return;              // This should not happen
  875.         }
  876.         // Read library header
  877.         DictionaryOffset = rec.GetDword();         // Read dictionary offset
  878.         DictionarySize   = rec.GetWord();          // Read dictionary size
  879.         rec.GetByte();                             // Read flag
  880.         // Page size / alignment for members
  881.         PageSize = rec.End + 1;
  882.         uint32 align = 1 << FloorLog2(PageSize);   // Make power of 2
  883.         if (PageSize != align) {
  884.             err.submit(2601, PageSize);             // Error: not a power of 2
  885.         }
  886.         CurrentOffset = PageSize;                  // Offset to first library member
  887.     }
  888.     else {
  889.         // Unix style library. First header at offset 8
  890.         CurrentOffset = 8;
  891.     }
  892.     // Current member number
  893.     CurrentNumber = 0;
  894. }
  895.  
  896.  
  897. char * CLibrary::ExtractMember(CFileBuffer * Destination) {
  898.     // Extract library member
  899.     // Dispatch according to library type
  900.     if (cmd.InputType == FILETYPE_OMFLIBRARY || cmd.InputType == FILETYPE_OMF) {
  901.         return ExtractMemberOMF(Destination);
  902.     }
  903.     else {
  904.         return ExtractMemberUNIX(Destination);
  905.     }
  906. }
  907.  
  908.  
  909. char * CLibrary::ExtractMemberOMF(CFileBuffer * Destination) {
  910.     // Extract member of OMF style library
  911.  
  912.     uint32 RecordEnd;                             // End of OMF record
  913.     SOMFRecordPointer rec;                        // Current OMF record
  914.     char * MemberName = 0;                        // Name of library member
  915.     uint32 MemberStart = 0;                       // Start of member
  916.     uint32 MemberEnd = 0;                         // End of member
  917.  
  918.     if (CurrentOffset >= DictionaryOffset) return 0;// No more members
  919.  
  920.     rec.Start(Buf(), CurrentOffset, DictionaryOffset);// Initialize record pointer
  921.  
  922.     // Loop through the records of all OMF modules
  923.     do {
  924.         // Check record type
  925.         switch (rec.Type2) {
  926.  
  927.         case OMF_THEADR: // Module header. Member starts here
  928.             MemberName  = rec.GetString();          // Get name
  929.             MemberStart = rec.FileOffset;           // Get start address
  930.             break;
  931.  
  932.         case OMF_MODEND: // Member ends here.
  933.             RecordEnd = rec.FileOffset + rec.End +1;// End of record
  934.             MemberEnd = RecordEnd;                  // = member end address
  935.  
  936.             // Save member as raw data
  937.             if (Destination) {
  938.                 Destination->SetSize(0);             // Make sure destination buffer is empty
  939.                 Destination->FileType = Destination->WordSize = 0;
  940.                 Destination->Push(Buf() + MemberStart, MemberEnd - MemberStart);
  941.             }
  942.  
  943.             // Align next member by PageSize;
  944.             rec.GetNext(PageSize);
  945.             CurrentOffset = rec.FileOffset;
  946.  
  947.             // Check name
  948.             //!!if (MemberName[0] == 0) MemberName = (char*)"NoName!";
  949.  
  950.             // Return member name
  951.             return MemberName;
  952.  
  953.         case OMF_LIBEND: // Last member should end here
  954.             RecordEnd = rec.FileOffset + rec.End + 1;// End of record
  955.             if (RecordEnd != DictionaryOffset) err.submit(2602);
  956.  
  957.             // No more members:
  958.             return 0;
  959.         }    
  960.     }  // Go to next record
  961.     while (rec.GetNext());                        // End of loop through records
  962.  
  963.     err.submit(2610);                             // Library end record not found
  964.     return 0;
  965. }
  966.  
  967.  
  968. char * CLibrary::ExtractMemberUNIX(CFileBuffer * Destination) {
  969.     // Extract member of UNIX style library
  970.     // This function is called repeatedly to get each member of library/archive
  971.     SUNIXLibraryHeader * Header = 0;     // Member header
  972.     uint32 MemberSize = 0;              // Size of member
  973.     uint32 HeaderExtra = 0;             // Extra added to size of header
  974.     uint32 NameIndex;                   // Index into long names member
  975.     char * Name = 0;                    // Name of member
  976.     int Skip = 1;                       // Skip record and search for next
  977.     int i;                              // Loop counter
  978.     char * p;                           // Used for loop through string
  979.  
  980.     if (CurrentOffset == 0 || CurrentOffset + sizeof(SUNIXLibraryHeader) >= DataSize) {
  981.         // No more members
  982.         return 0;
  983.     }
  984.  
  985.     // Search for member
  986.     while (Skip && CurrentOffset) {
  987.         HeaderExtra = 0;
  988.         // Extract next library member from input library
  989.         Header = &Get<SUNIXLibraryHeader>(CurrentOffset);
  990.         // Size of member
  991.         MemberSize = (uint32)atoi(Header->FileSize);
  992.         if (MemberSize + CurrentOffset + sizeof(SUNIXLibraryHeader) > DataSize) {
  993.             err.submit(2500);  // Points outside file
  994.             return 0;
  995.         }
  996.         // Member name
  997.         Name = Header->Name;
  998.         if (strncmp(Name, "// ", 3) == 0) {
  999.             // This is the long names member. Remember its position
  1000.             LongNames = CurrentOffset + sizeof(SUNIXLibraryHeader);
  1001.             LongNamesSize = MemberSize;
  1002.             // The long names are terminated by '/' or 0, depending on system,
  1003.             // but may contain non-terminating '/'. Find out which type we have:
  1004.             // Pointer to LongNames record
  1005.             p = Buf() + LongNames;
  1006.             // Find out whether we have terminating zeroes:
  1007.             if ((LongNamesSize > 1 && p[LongNamesSize-1] == '/') || (p[LongNamesSize-1] <= ' ' && p[LongNamesSize-2] == '/')) {
  1008.                 // Names are terminated by '/'. Replace all '/' by 0 in the longnames record
  1009.                 for (uint32 j = 0; j < LongNamesSize; j++, p++) {
  1010.                     if (*p == '/') *p = 0;
  1011.                 }
  1012.             }
  1013.         }
  1014.         else if (strncmp(Name, "/ ", 2) == 0
  1015.             || strncmp(Name, "__.SYMDEF", 9) == 0) {
  1016.                 // This is a symbol index member.
  1017.                 // The symbol index is not used because we are always building a new symbol index.
  1018.         }
  1019.         else if (Name[0] == '/' && Name[1] >= '0' && Name[1] <= '9' && LongNames) {
  1020.             // Name contains index into LongNames record
  1021.             NameIndex = atoi(Name+1);
  1022.             if (NameIndex < LongNamesSize) {
  1023.                 Name = Buf() + LongNames + NameIndex;
  1024.             }
  1025.             else {
  1026.                 Name = (char*)"NoName!";
  1027.             }
  1028.             Skip = 0;
  1029.         }
  1030.         else if (strncmp(Name, "#1/", 3) == 0) {
  1031.             // Name refers to long name after the header
  1032.             // This variant is used by Mac and some versions of BSD
  1033.             HeaderExtra = atoi(Name+3);
  1034.             Name += sizeof(SUNIXLibraryHeader);
  1035.             if (MemberSize > HeaderExtra) {
  1036.                 // The length of the name, HeaderExtra, is included in the
  1037.                 // Header->FileSize field. Subtract to get the real file size
  1038.                 MemberSize -= HeaderExtra;
  1039.             }
  1040.             if (strncmp(Name, "__.SYMDEF", 9) == 0) {
  1041.                 // Symbol table "__.SYMDEF SORTED" as long name
  1042.                 Skip = 1;
  1043.             }
  1044.             else {
  1045.                 Skip = 0;
  1046.             }
  1047.         }
  1048.         else {
  1049.             // Ordinary short name
  1050.             // Name may be terminated by '/' or space. Replace termination char by 0
  1051.             for (i = 15; i >= 0; i--) {
  1052.                 if (Name[i] == ' ' || Name[i] == '/') Name[i] = 0;
  1053.                 else break;
  1054.             }
  1055.             // Terminate name with max length by overwriting Date field, which we are not using
  1056.             Name[16] = 0;
  1057.             Skip = 0;
  1058.         }
  1059.         // Point to next member
  1060.         CurrentOffset = NextHeader(CurrentOffset);
  1061.         // Increment number
  1062.         CurrentNumber += !Skip;
  1063.     }  // End of while loop
  1064.  
  1065.     // Save member as raw data
  1066.     if (Destination) {
  1067.         Destination->SetSize(0);       // Make sure destination buffer is empty
  1068.         Destination->FileType = Destination->WordSize = 0;
  1069.         Destination->Push((int8*)Header + sizeof(SUNIXLibraryHeader) + HeaderExtra, MemberSize);
  1070.     }
  1071.  
  1072.     // Check name
  1073.     if (Name[0] == 0) Name = (char*)"NoName!";
  1074.  
  1075.     // Return member name
  1076.     return Name;
  1077. }
  1078.  
  1079. void CLibrary::InsertMember(CFileBuffer * member) {
  1080.     // Add member to output library
  1081.     if (cmd.OutputType == FILETYPE_OMF) {
  1082.         InsertMemberOMF(member);                   // OMF style output library
  1083.     }
  1084.     else {
  1085.         InsertMemberUNIX(member);                  // UNIX style output library
  1086.     }
  1087. }
  1088.  
  1089.  
  1090. void CLibrary::InsertMemberOMF(CFileBuffer * member) {
  1091.     // Add member to OMF library
  1092.  
  1093.     // Check file type
  1094.     if (member->GetFileType() != FILETYPE_OMF) err.submit(9000);
  1095.  
  1096.     // Get word size
  1097.     WordSize = member->WordSize;
  1098.  
  1099.     // Store offset
  1100.     uint32 offset = DataBuffer.GetDataSize();
  1101.     Indexes.Push(offset);
  1102.  
  1103.     // Store member
  1104.     DataBuffer.Push(member->Buf(), member->GetDataSize());
  1105.     DataBuffer.Align(PageSize);
  1106.  
  1107.     // Member index
  1108.     uint32 mindex = Indexes.GetNumEntries() - 1;
  1109.  
  1110.     // Get public string table
  1111.     COMF omf;
  1112.     *member >> omf;     // Translate member to class OMF
  1113.     omf.PublicNames(&StringBuffer, &StringEntries, mindex); // Make list of public names
  1114.     *member << omf;     // Return buffer to member
  1115. }
  1116.  
  1117.  
  1118. void CLibrary::InsertMemberUNIX(CFileBuffer * member) {
  1119.     // Add next library member to output library
  1120.     uint32 RawSize = 0;                 // Size of binary file
  1121.     uint32 AlignmentPadding = 0;        // Padding after file
  1122.     char * name = 0;                    // Name of member
  1123.     int NameLength = 0;                 // length of name
  1124.     int NameAfter = 0;                  // length of name after MachO header
  1125.     int i;                              // loop counter, index
  1126.  
  1127.     // Get word size
  1128.     WordSize = member->WordSize;
  1129.  
  1130.     // Make member header
  1131.     SUNIXLibraryHeader header;
  1132.     memset(&header, ' ', sizeof(SUNIXLibraryHeader));  // Fill with spaces
  1133.  
  1134.     // Name of member
  1135.     if (member->OutputFileName == 0 || *member->OutputFileName == 0) member->OutputFileName = member->FileName;
  1136.  
  1137.     if (cmd.LibrarySubtype == LIBTYPE_SHORTNAMES) {
  1138.         // Make short name
  1139.         name = ShortenMemberName(member->OutputFileName);
  1140.     }
  1141.     else {
  1142.         // Remove path from library member name. Original long name is overwritten
  1143.         name = StripMemberName((char*)(member->OutputFileName));
  1144.     }
  1145.     NameLength = strlen(name);
  1146.  
  1147.     if (cmd.OutputType == FILETYPE_MACHO_LE && cmd.LibrarySubtype != LIBTYPE_SHORTNAMES) {
  1148.         // Mach-O library stores name after header record.
  1149.         // Name is zero padded to length 4 modulo 8 to align by 8
  1150.         int pad = 8 - ((NameLength + 4) & 7);
  1151.         NameAfter = NameLength + pad;
  1152.         sprintf(header.Name, "#1/%i ", NameAfter);
  1153.     }
  1154.     else {
  1155.         // ELF and COFF library store names < 16 characters in the name field
  1156.         if (NameLength < 16) {
  1157.             // (Don't use sprintf to write header.Name here: It seems that Gnu sprintf checks the size of the
  1158.             // buffer it is writing to. Gives buffer overrun error when termniating zero goes beyond the name field)
  1159.             memset(header.Name, ' ', 16);
  1160.             memcpy(header.Name, name, NameLength);
  1161.             header.Name[NameLength] = '/';
  1162.         }
  1163.         else {
  1164.             // store in LongNamesBuffer
  1165.             if (cmd.OutputType == FILETYPE_COFF) {
  1166.                 // COFF: Name is zero-terminated
  1167.                 i = LongNamesBuffer.PushString(name);
  1168.             }
  1169.             else {
  1170.                 // ELF: Name terminated by "/\n"
  1171.                 i = LongNamesBuffer.Push(name, NameLength);
  1172.                 LongNamesBuffer.Push("/\n", 2);
  1173.             }
  1174.             // store index into long names member
  1175.             sprintf(header.Name, "/%i ", i);
  1176.         }
  1177.     }
  1178.  
  1179.     // Date
  1180.     sprintf(header.Date, "%u ", (uint32)time(0));
  1181.  
  1182.     // User and group id
  1183.     header.UserID[0] = '0';
  1184.     header.GroupID[0] = '0';
  1185.     // File mode
  1186.     strcpy(header.FileMode, "100666");
  1187.     // Size of binary file
  1188.     RawSize = member->GetDataSize();
  1189.     // Calculate alignment padding
  1190.     if (AlignBy) {
  1191.         AlignmentPadding = uint32(-int32(RawSize)) & (AlignBy-1);
  1192.     }
  1193.  
  1194.     // File size including name string
  1195.     sprintf(header.FileSize, "%u ", NameAfter + RawSize + AlignmentPadding);
  1196.  
  1197.     // Header end
  1198.     header.HeaderEnd[0] = '`';
  1199.     header.HeaderEnd[1] = '\n';
  1200.  
  1201.     // Remove terminating zeroes
  1202.     for (uint32 i = 0; i < sizeof(SUNIXLibraryHeader); i++) {
  1203.         if (((char*)&header)[i] == 0) ((char*)&header)[i] = ' ';
  1204.     }
  1205.  
  1206.     // Store offset
  1207.     uint32 offset = DataBuffer.GetDataSize();
  1208.     Indexes.Push(offset);
  1209.  
  1210.     // Store member header
  1211.     DataBuffer.Push(&header, sizeof(header));
  1212.  
  1213.     if (cmd.OutputType == FILETYPE_MACHO_LE) {    
  1214.         // Store member name after header if Mach-O
  1215.         if (NameAfter) {
  1216.             // Mach-O library stores name after header record.
  1217.             DataBuffer.PushString(name);
  1218.         }
  1219.         DataBuffer.Align(AlignBy);
  1220.     }
  1221.  
  1222.     // Store member
  1223.     DataBuffer.Push(member->Buf(), RawSize);
  1224.  
  1225.     // Align by padding with '\n'
  1226.     for (uint32 i = 0; i < AlignmentPadding; i++) {
  1227.         DataBuffer.Push("\n", 1);
  1228.     }
  1229.  
  1230.     // Member index
  1231.     uint32 mindex = Indexes.GetNumEntries() - 1;
  1232.  
  1233.     // Get public string table
  1234.     switch(member->GetFileType()) {
  1235.     case FILETYPE_COFF: {
  1236.         CCOFF coff;
  1237.         *member >> coff;     // Translate member to type COFF
  1238.         coff.PublicNames(&StringBuffer, &StringEntries, mindex); // Make list of public names
  1239.         *member << coff;     // Return buffer to member
  1240.         break;}
  1241.  
  1242.     case FILETYPE_OMF: {
  1243.         COMF omf;
  1244.         *member >> omf;      // Translate member to type COFF
  1245.         omf.PublicNames(&StringBuffer, &StringEntries, mindex); // Make list of public names
  1246.         *member << omf;      // Return buffer to member
  1247.         break;}
  1248.  
  1249.     case FILETYPE_ELF:
  1250.         if (WordSize == 32) {
  1251.             // Make instance of file parser, 32 bit template
  1252.             CELF<ELF32STRUCTURES> elf;
  1253.             *member >> elf;      // Translate member to type ELF
  1254.             elf.PublicNames(&StringBuffer, &StringEntries, mindex); // Make list of public names
  1255.             *member << elf;      // Return buffer to member
  1256.             break;
  1257.         }
  1258.         else {
  1259.             // Make instance of file parser, 64 bit template
  1260.             CELF<ELF64STRUCTURES> elf;
  1261.             *member >> elf;      // Translate member to type ELF
  1262.             elf.PublicNames(&StringBuffer, &StringEntries, mindex); // Make list of public names
  1263.             *member << elf;      // Return buffer to member
  1264.             break;
  1265.         }
  1266.  
  1267.     case FILETYPE_MACHO_LE:
  1268.         if (WordSize == 32) {
  1269.             // Make instance of file parser, 32 bit template
  1270.             CMACHO<MAC32STRUCTURES> mac;
  1271.             *member >> mac;      // Translate member to type ELF
  1272.             mac.PublicNames(&StringBuffer, &StringEntries, mindex); // Make list of public names
  1273.             *member << mac;      // Return buffer to member
  1274.             break;
  1275.         }
  1276.         else {
  1277.             // Make instance of file parser, 64 bit template
  1278.             CMACHO<MAC64STRUCTURES> mac;
  1279.             *member >> mac;      // Translate member to type ELF
  1280.             mac.PublicNames(&StringBuffer, &StringEntries, mindex); // Make list of public names
  1281.             *member << mac;      // Return buffer to member
  1282.             break;
  1283.         }
  1284.  
  1285.     default:                // Type not supported
  1286.         err.submit(2501, GetFileFormatName(member->GetFileType()));
  1287.         break;
  1288.     }
  1289. }
  1290.  
  1291. /*   unused:
  1292. char * CLibrary::TruncateMemberName(char const * name) {
  1293.     // Remove path and truncate object file name to 15 characters
  1294.     // This function removes any path from the member name,
  1295.     // changes the extension to the default for the the output file type,
  1296.     // changes any spaces to underscores, and
  1297.     // truncates the member name to 15 characters for the sake of compatibility.
  1298.     // The return value is an ASCII string in a static buffer
  1299.     static char TruncName[32];          // Truncated name
  1300.     int maxlen;                         // Max length, not including extension
  1301.     char const * p1;                    // Point to start of name without path
  1302.     char const * extension;             // Default extension for file type
  1303.     int i;                              // Loop counter
  1304.     int len;                            // String length
  1305.     static int DummyNumber = 0;         // Count invalid/null names
  1306.     int FileType;                       // File type
  1307.  
  1308.     // Remove path
  1309.     len = (int)strlen(name);  p1 = name;
  1310.     for (i = len-1; i >= 0; i--) {
  1311.         if (name[i] == '/' || name[i] == '\\' || name[i] == ':') {
  1312.             p1 = name + i + 1; break;
  1313.         }
  1314.     }
  1315.     // Remove extension
  1316.     len = (int)strlen(p1);
  1317.     for (i = len-1; i >= 0; i--) {
  1318.         if (p1[i] == '.') {
  1319.             len = i;  break;
  1320.         }
  1321.     }
  1322.  
  1323.     // Check if any name remains
  1324.     if (len == 0) {     // No name. Make one
  1325.         sprintf(TruncName, "NoName%i", ++DummyNumber);
  1326.         p1 = TruncName;  len = (int)strlen(p1);
  1327.     }
  1328.  
  1329.     // Get file type
  1330.     FileType = cmd.OutputType;
  1331.     if (FileType == CMDL_OUTPUT_DUMP || FileType == 0) FileType = cmd.InputType;
  1332.     if (FileType >= FILETYPE_LIBRARY) FileType = cmd.MemberType;
  1333.  
  1334.     // Get default extension and max length of name without extension
  1335.     if (FileType == FILETYPE_COFF || FileType == FILETYPE_OMF) {
  1336.         maxlen = 11;  extension = ".obj";
  1337.     }
  1338.     else {
  1339.         maxlen = 13;  extension = ".o";
  1340.     }
  1341.     if (len > maxlen) len = maxlen;
  1342.  
  1343.     // Truncate name
  1344.     strncpy(TruncName, p1, len);
  1345.     TruncName[len] = 0;
  1346.  
  1347.     // Remove any spaces or other illegal characters
  1348.     len = (int)strlen(TruncName);
  1349.     for (i = 0; i < len; i++) {
  1350.         if ((uint8)TruncName[i] <= 0x20) TruncName[i] = '_';
  1351.     }
  1352.  
  1353.     // Add default extension
  1354.     strcpy(TruncName+strlen(TruncName), extension);
  1355.  
  1356.     // Terminate
  1357.     TruncName[15] = 0;  
  1358.     return TruncName;
  1359. }
  1360. */
  1361.  
  1362. char * CLibrary::StripMemberName(char * name) {
  1363.     // Remove path from zero-terminated library member name and set extension to default.
  1364.     // Note: Original long name is overwritten
  1365.     char * p1;                          // Point to start of name without path
  1366.     const char * extension = 0;         // Default extension for file type
  1367.     int i;                              // Loop counter
  1368.     int len0;                           // Original string length
  1369.     int len;                            // String length
  1370.     int nlen;                           // length of name without extension
  1371.     int elen = 0;                       // length of extension
  1372.     static int DummyNumber = 0;         // Count invalid/null names
  1373.     int FileType;                       // File type
  1374.  
  1375.     // Length
  1376.     len0 = len = (int)strlen(name);
  1377.  
  1378.     // Remove path
  1379.     p1 = name;
  1380.     for (i = len-1; i >= 0; i--) {
  1381.         if (name[i] == '/' || name[i] == '\\' || name[i] == ':') {
  1382.             p1 = name + i + 1; break;
  1383.         }
  1384.     }
  1385.     len -= i + 1;
  1386.  
  1387.     // move to begin of buffer
  1388.     if (p1 > name) {
  1389.         memmove(name, p1, len + 1);
  1390.     }
  1391.        
  1392.     // Get file type
  1393.     if (cmd.MemberType) {
  1394.         FileType = cmd.MemberType;
  1395.     }
  1396.     else if (cmd.LibraryOptions & CMDL_LIBRARY_EXTRACTMEM) {
  1397.         FileType = cmd.InputType;
  1398.     }
  1399.     else {    
  1400.         FileType = cmd.OutputType;
  1401.     }
  1402.     if (FileType == CMDL_OUTPUT_DUMP || FileType == 0) FileType = cmd.InputType;
  1403.     if (FileType >= FILETYPE_LIBRARY) FileType = cmd.MemberType;
  1404.  
  1405.     // Get default extension and max length of name without extension
  1406.     if (FileType == FILETYPE_COFF || FileType == FILETYPE_OMF) {
  1407.         extension = ".obj";  elen = 4;
  1408.     }
  1409.     else if (FileType == FILETYPE_ELF || FileType == FILETYPE_MACHO_LE || FileType == FILETYPE_MACHO_BE) {
  1410.         extension = ".o";  elen = 2;
  1411.     }
  1412.  
  1413.     // find extension
  1414.     for (nlen = len-1; nlen >= 0; nlen--) {
  1415.         if (name[nlen] == '.') {
  1416.             break;
  1417.         }
  1418.     }
  1419.  
  1420.     // Remove any spaces or other illegal characters
  1421.     for (i = 0; i < nlen; i++) {
  1422.         if ((uint8)name[i] <= 0x20 || name[i] == '.') name[i] = '_';
  1423.     }
  1424.  
  1425.     // Check if any name remains
  1426.     if ((len == 0 && len0 > 12) || nlen == 0) {     // No name. Make one
  1427.         sprintf(name, "NoName%i", ++DummyNumber);
  1428.         len = (int)strlen(name);
  1429.     }
  1430.  
  1431.     // Replace extension
  1432.     if (len + elen <= len0 && extension != 0) {
  1433.         strcpy(name + nlen, extension);
  1434.     }
  1435.     // Terminate
  1436.     return name;
  1437. }
  1438.  
  1439.  
  1440. char * CLibrary::ShortenMemberName(char const *name) {
  1441.     // Truncate library member name to 15 characters and make unique
  1442.     // The path is removed and the extension set to default.
  1443.     // The original long name is not overwritten
  1444.     static char fixedName[32];          // Modified name
  1445.     char const * p1;                    // Point to start of name without path
  1446.     char const * extension;             // Default extension for file type
  1447.     int i;                              // Loop counter
  1448.     int len;                            // Filename length
  1449.     int len0;                           // Filename length without extension
  1450.     int elen;                           // length of extension
  1451.     static int RunningNumber = 0;       // Enumerate truncated names
  1452.     int FileType;                       // File type
  1453.  
  1454.     // Length
  1455.     len = (int)strlen(name);
  1456.  
  1457.     // Skip path
  1458.     p1 = name;
  1459.     for (i = len-1; i >= 0; i--) {
  1460.         if (name[i] == '/' || name[i] == '\\' || name[i] == ':') {
  1461.             p1 = name + i + 1; break;
  1462.         }
  1463.     }
  1464.     len = (int)strlen(name);
  1465.  
  1466.     // move to static buffer
  1467.     if (len > 15) len = 15;
  1468.     memcpy(fixedName, p1, len);
  1469.     fixedName[len] = 0;
  1470.  
  1471.     // find extension
  1472.     for (i = len-1; i >= 0; i--) {
  1473.         if (fixedName[i] == '.') {
  1474.             fixedName[i] = 0;  break;
  1475.         }
  1476.     }
  1477.     // length without extension
  1478.     len0 = (int)strlen(fixedName);
  1479.  
  1480.     // Remove any spaces or other illegal characters
  1481.     for (i = 0; i < len0; i++) {
  1482.         if ((uint8)fixedName[i] <= 0x20 || fixedName[i] == '.') fixedName[i] = '_';
  1483.     }
  1484.  
  1485.     // Check if any name remains
  1486.     if (len0 == 0) {     // No name. Make one
  1487.         sprintf(fixedName, "NoName_%X", RunningNumber++);
  1488.         len0 = (int)strlen(fixedName);
  1489.     }
  1490.  
  1491.     // Get file type
  1492.     FileType = cmd.OutputType;
  1493.     if (FileType == CMDL_OUTPUT_DUMP || FileType == 0) FileType = cmd.InputType;
  1494.     if (FileType >= FILETYPE_LIBRARY) FileType = cmd.MemberType;
  1495.  
  1496.     // Get default extension and max length of name without extension
  1497.     if (FileType == FILETYPE_COFF || FileType == FILETYPE_OMF) {
  1498.         extension = ".obj";  elen = 4;
  1499.     }
  1500.     else {
  1501.         extension = ".o";  elen = 2;
  1502.     }
  1503.  
  1504.     // Make unique and add extension
  1505.     if (len0 + elen >= 15) {
  1506.         // Name is truncated or possibly identical to some other truncated name.
  1507.         // Insert 2-, 3- or 4-digit running hexadecimal number.
  1508.         if (RunningNumber < 0x100) {
  1509.             sprintf(fixedName + 12 - elen, "_%02X%s", RunningNumber++, extension);
  1510.         }
  1511.         else if (RunningNumber < 0x1000) {
  1512.             sprintf(fixedName + 12 - elen, "%03X%s", RunningNumber++, extension);
  1513.         }
  1514.         else {
  1515.             sprintf(fixedName + 11 - elen, "%04X%s", (RunningNumber++ & 0xFFFF), extension);
  1516.         }
  1517.     }
  1518.     else {
  1519.         // Short name. Just add extension
  1520.         strcpy(fixedName + len0, extension);
  1521.     }
  1522.  
  1523.     // Return static name buffer
  1524.     return fixedName;
  1525. }
  1526.  
  1527. /*  Unused:
  1528. int CLibrary::MemberNameExistsUNIX(char * name) {
  1529.     // Check if DataBuffer contains a member with this name
  1530.     char Name1[20], Name2[20];
  1531.     uint32 i, j;
  1532.  
  1533.     // Terminate name without extension
  1534.     memcpy(Name1, name, 16);
  1535.     for (j = 0; j < 16; j++) {
  1536.         if (Name1[j] == '.' || Name1[j] == '/') Name1[j] = 0;
  1537.     }
  1538.  
  1539.     // Loop through previous members in DataBuffer
  1540.     for (i = 0; i < Indexes.GetNumEntries(); i++) {
  1541.         uint32 offset = Indexes[i];
  1542.         // Copy name of member i
  1543.         memcpy(Name2, DataBuffer.Buf() + offset, 16);
  1544.         // Terminate name2
  1545.         for (j = 0; j < 16; j++) {
  1546.             if (Name2[j] == '.' || Name2[j] == '/') Name2[j] = 0;
  1547.         }
  1548.         // Case-insensitive compare of names
  1549.         if (stricmp(Name1, Name2) == 0) {
  1550.             // Identical name found
  1551.             return i + 1;
  1552.         }
  1553.     }
  1554.     // No identical name found
  1555.     return 0;
  1556. }*/
  1557.  
  1558.  
  1559. void CLibrary::SortStringTable() {
  1560.     // Sort the string table in ASCII order
  1561.  
  1562.     // Length of table
  1563.     int32 n = StringEntries.GetNumEntries();
  1564.     if (n <= 0) return;
  1565.  
  1566.     // Point to table of SStringEntry records
  1567.     SStringEntry * Table = &StringEntries[0];
  1568.     // String pointers
  1569.     char * s1, * s2;
  1570.  
  1571.     // Simple Shell sort with Sedgewick gaps:
  1572.     int32 i, j, k, gap;
  1573.     for (k = 15; k >= 0; k--) {
  1574.         gap = (1 << 2 * k) | (3 << k >> 1) | 1;  // Sedgewick gap grants O(N^4/3)
  1575.         for (i = gap; i < n; i++) {
  1576.             SStringEntry key = Table[i];
  1577.             char * strkey = StringBuffer.Buf() + key.String;
  1578.             for (j = i - gap; j >= 0 && strcmp(strkey, StringBuffer.Buf() + Table[j].String) < 0; j -= gap) {
  1579.                 Table[j + gap] = Table[j];
  1580.             }
  1581.             Table[j + gap] = key;
  1582.         }
  1583.     }
  1584.  
  1585.     // Now StringEntries has been sorted. Reorder StringBuffer to the sort order.
  1586.     CMemoryBuffer SortedStringBuffer;    // Temporary buffer for strings in sort order
  1587.     for (i = 0; i < n; i++) {
  1588.         // Pointer to old string
  1589.         s1 = StringBuffer.Buf() + Table[i].String;
  1590.         // Update table to point to new string
  1591.         Table[i].String = SortedStringBuffer.GetDataSize();
  1592.         // Put string into SortedStringBuffer
  1593.         SortedStringBuffer.PushString(s1);
  1594.     }
  1595.     if (SortedStringBuffer.GetDataSize() != StringBuffer.GetDataSize()) {
  1596.         // The two string buffers should be same size
  1597.         err.submit(9000); return;
  1598.     }
  1599.     // Copy SortedStringBuffer into StringBuffer
  1600.     memcpy(StringBuffer.Buf(), SortedStringBuffer.Buf(), StringBuffer.GetDataSize());
  1601.  
  1602.     // Check for duplicate symbols
  1603.     for (i = 0; i < n-1; i++) {
  1604.         s1 = StringBuffer.Buf() + Table[i].String;
  1605.         for (j = i + 1; j < n; j++) {
  1606.             s2 = StringBuffer.Buf() + Table[j].String;
  1607.             if (strcmp(s1,s2) == 0) {
  1608.                 // Duplicate found
  1609.                 // Compose error string "Modulename1 and Modulename2"
  1610.                 uint32 errstring = LongNamesBuffer.GetDataSize();
  1611.                 LongNamesBuffer.PushString(GetModuleName(Table[i].Member));
  1612.                 LongNamesBuffer.SetSize(LongNamesBuffer.GetDataSize()-1); // remove terminating zero
  1613.                 LongNamesBuffer.Push(" and ", 5);
  1614.                 LongNamesBuffer.PushString(GetModuleName(Table[j].Member));
  1615.                 err.submit(1214, s1, (char*)LongNamesBuffer.Buf() + errstring);
  1616.                 LongNamesBuffer.SetSize(errstring);  // remove string again
  1617.                 i++;  // Prevent excessive messages
  1618.             }
  1619.             else {
  1620.                 break;
  1621.             }
  1622.         }
  1623.     }
  1624. }
  1625.  
  1626.  
  1627. uint32 EndianChange(uint32 n) {
  1628.     // Convert little-endian to big-endian number, or vice versa
  1629.     return (n << 24) | ((n & 0x0000FF00) << 8) | ((n & 0x00FF0000) >> 8) | (n >> 24);
  1630. }
  1631.  
  1632.  
  1633. uint32 RoundEven(uint32 n) {
  1634.     // Round up number to nearest even
  1635.     return (n + 1) & uint32(-2);
  1636. }
  1637.  
  1638.  
  1639. uint32 Round4(uint32 n) {
  1640.     // Round up number to nearest multiple of 4
  1641.     return (n + 3) & uint32(-4);
  1642. }
  1643.  
  1644.  
  1645. void CLibrary::MakeSymbolTableUnix() {
  1646.     // Make symbol table for COFF, ELF or MACHO library
  1647.     // Uses UNIX archive format for COFF, BSD and Mac
  1648.     uint32 i;                              // Loop counter
  1649.     uint32 MemberOffset;                   // Offset to member
  1650.     uint32 LongNameSize = 0;               // Length of symbol table name if stored after record
  1651.  
  1652.     int SymbolTableType = cmd.OutputType;  // FILETYPE_COFF       = 1: COFF
  1653.     // FILETYPE_ELF        = 3: ELF
  1654.     // FILETYPE_MACHO_LE   = 4: Mac, unsorted
  1655.     //              0x10000004: Mac, sorted
  1656.     // Newer Mac tools require the sorted type, unless there are multiple publics with same name
  1657.     if (SymbolTableType == FILETYPE_MACHO_LE) SymbolTableType |= 0x10000000;
  1658.  
  1659.     // Make symbol table header
  1660.     SUNIXLibraryHeader SymTab;             // Symbol table header
  1661.     memset(&SymTab, ' ', sizeof(SymTab));  // Fill with spaces
  1662.     SymTab.Name[0] = '/';                  // Name = '/'
  1663.     // The silly Mac linker requires that the symbol table has a date stamp not
  1664.     // older than the .a file. Fix this by post-dating the symbol table:
  1665.     uint32 PostDate = 0;
  1666.     if (SymbolTableType & 0x10000000) PostDate = 100; // Post-date if mac sorted symbol table
  1667.     sprintf(SymTab.Date, "%u ", (uint32)time(0) + PostDate); // Date stamp for symbol table
  1668.  
  1669.     SymTab.UserID[0] = '0';                // UserID = 0  (may be omitted in COFF)
  1670.     SymTab.GroupID[0] = '0';               // GroupID = 0 (may be omitted in COFF)
  1671.     strcpy(SymTab.FileMode, "100666");     // FileMode = 0100666 (may be 0 in COFF)
  1672.  
  1673.     SymTab.HeaderEnd[0] = '`';             // End with "`\n"
  1674.     SymTab.HeaderEnd[1] = '\n';
  1675.  
  1676.     // File header
  1677.     OutFile.Push("!<arch>\n", 8);
  1678.  
  1679.     uint32 NumMembers = Indexes.GetNumEntries();        // Number of members
  1680.     uint32 NumStrings = StringEntries.GetNumEntries();  // Number of symbol names
  1681.     uint32 StringsLen = StringBuffer.GetDataSize();     // Size of string table
  1682.  
  1683.     // Calculate sizes of string index records, not including header
  1684.     // Unsorted index, used in ELF and COFF libraries
  1685.     uint32 Index1Size = (NumStrings+1)*4 + StringsLen;
  1686.     // Sorted index, used in COFF libraries as second member
  1687.     uint32 Index2Size = (NumMembers+2)*4 + NumStrings*2 + StringsLen;
  1688.     // Sorted index, used in Mach-O libraries
  1689.     uint32 Index3Size = Round4(NumStrings*8 + 8 + StringsLen);
  1690.     // Longnames member
  1691.     uint32 LongnamesMemberSize = 0;
  1692.     // Official MS COFF reference says that the "//" longnames member must be present,
  1693.     // even if it is unused, but MS LIB does not make it unless it is needed.
  1694.     // Here, we will include the longnames member only if it is needed
  1695.     if ((SymbolTableType == FILETYPE_COFF || SymbolTableType == FILETYPE_ELF) && LongNamesBuffer.GetDataSize()) {
  1696.         LongnamesMemberSize = sizeof(SUNIXLibraryHeader) + LongNamesBuffer.GetDataSize();
  1697.     }
  1698.  
  1699.     // Offset to first member
  1700.     uint32 FirstMemberOffset = 0;
  1701.     switch (SymbolTableType) {
  1702.     case FILETYPE_COFF:
  1703.         FirstMemberOffset = 8 + 2*sizeof(SUNIXLibraryHeader) + RoundEven(Index1Size)
  1704.             + RoundEven(Index2Size) + RoundEven(LongnamesMemberSize);
  1705.         break;
  1706.     case FILETYPE_ELF:
  1707.         FirstMemberOffset = 8 + sizeof(SUNIXLibraryHeader) + RoundEven(Index1Size)
  1708.             + RoundEven(LongnamesMemberSize);
  1709.         break;
  1710.     case FILETYPE_MACHO_LE:
  1711.         FirstMemberOffset = 8 + sizeof(SUNIXLibraryHeader) + Index3Size;
  1712.         break;
  1713.     case FILETYPE_MACHO_LE | 0x10000000: // Mac, sorted
  1714.         LongNameSize = 20;
  1715.         FirstMemberOffset = 8 + sizeof(SUNIXLibraryHeader) + Index3Size + LongNameSize;
  1716.         break;      
  1717.     default:
  1718.         err.submit(2501, GetFileFormatName(cmd.OutputType));
  1719.     }
  1720.  
  1721.     // Make unsorted symbol table for COFF or ELF output
  1722.     if (SymbolTableType == FILETYPE_COFF || SymbolTableType == FILETYPE_ELF) {
  1723.  
  1724.         // Put file size into symbol table header
  1725.         sprintf(SymTab.FileSize, "%u ", Index1Size);
  1726.         // Remove terminating zeroes
  1727.         for (i = 0; i < sizeof(SymTab); i++) {
  1728.             if (((char*)&SymTab)[i] == 0) ((char*)&SymTab)[i] = ' ';
  1729.         }
  1730.  
  1731.         // Store header
  1732.         OutFile.Push(&SymTab, sizeof(SymTab));
  1733.  
  1734.         // Store table of offsets
  1735.         uint32 BigEndian;             // Number converted to big-endian
  1736.         BigEndian = EndianChange(NumStrings);
  1737.         OutFile.Push(&BigEndian, sizeof(BigEndian));  // Number of symbols
  1738.  
  1739.         // Loop through strings
  1740.         for (i = 0; i < NumStrings; i++) {
  1741.             // Get record in temporary symbol table
  1742.             SStringEntry * psym = &StringEntries[i];
  1743.             // Get offset of member in DataBuffer
  1744.             MemberOffset = Indexes[psym->Member];
  1745.             // Add size of headers to compute member offset in final file
  1746.             BigEndian = EndianChange(MemberOffset + FirstMemberOffset);
  1747.             // Store offset as big endian number
  1748.             OutFile.Push(&BigEndian, sizeof(BigEndian));
  1749.         }
  1750.  
  1751.         // Store strings
  1752.         OutFile.Push(StringBuffer.Buf(), StringBuffer.GetDataSize());
  1753.         // Align by 2
  1754.         if (OutFile.GetDataSize() & 1) {
  1755.             OutFile.Push("\n", 1);
  1756.         }
  1757.     }
  1758.  
  1759.     // Sort string table
  1760.     if (!RepressWarnings && SymbolTableType != FILETYPE_MACHO_LE) SortStringTable();
  1761.  
  1762.     // Make sorted symbol table, COFF style
  1763.     if (SymbolTableType == FILETYPE_COFF) {
  1764.         if (NumMembers > 0xFFFF) err.submit(2502);  // Too many members
  1765.  
  1766.         // Reuse symbol table header, change size entry
  1767.         sprintf(SymTab.FileSize, "%u ", Index2Size);
  1768.  
  1769.         // Remove terminating zeroes
  1770.         for (i = 0; i < sizeof(SymTab); i++) {
  1771.             if (((char*)&SymTab)[i] == 0) ((char*)&SymTab)[i] = ' ';
  1772.         }
  1773.         // Store header
  1774.         OutFile.Push(&SymTab, sizeof(SymTab));
  1775.  
  1776.         // Store number of members
  1777.         OutFile.Push(&NumMembers, sizeof(NumMembers));
  1778.  
  1779.         // Store member offsets
  1780.         for (i = 0; i < NumMembers; i++) {
  1781.             MemberOffset = Indexes[i] + FirstMemberOffset;
  1782.             OutFile.Push(&MemberOffset, sizeof(MemberOffset));
  1783.         }
  1784.  
  1785.         // Store number of symbols
  1786.         OutFile.Push(&NumStrings, sizeof(NumStrings));
  1787.  
  1788.         // Store member index for each string
  1789.         // Loop through strings
  1790.         for (i = 0; i < NumStrings; i++) {
  1791.             // Get record in temporary symbol table
  1792.             SStringEntry * psym = &StringEntries[i];
  1793.             // Get member index, 16 bits
  1794.             uint16 MemberI = (uint16)(psym->Member + 1);
  1795.             OutFile.Push(&MemberI, sizeof(MemberI));
  1796.         }
  1797.  
  1798.         // Store strings
  1799.         OutFile.Push(StringBuffer.Buf(), StringBuffer.GetDataSize());
  1800.         // Align by 2
  1801.         if (OutFile.GetDataSize() & 1) {
  1802.             OutFile.Push("\n", 1);
  1803.         }
  1804.     }
  1805.  
  1806.     // Make longnames table member for COFF or ELF output
  1807.     // (The decision whether to include a "//" longnames member is taken above)
  1808.     if (LongnamesMemberSize) {
  1809.         // reuse SymTab
  1810.         strcpy(SymTab.Name, "//       ");    // Name = "//"
  1811.         memset(SymTab.FileSize, ' ', 10);
  1812.         sprintf(SymTab.FileSize, "%u", LongNamesBuffer.GetDataSize());
  1813.  
  1814.         // Remove terminating zeroes
  1815.         for (i = 0; i < sizeof(SymTab); i++) {
  1816.             if (((char*)&SymTab)[i] == 0) ((char*)&SymTab)[i] = ' ';
  1817.         }
  1818.         // Store header
  1819.         OutFile.Push(&SymTab, sizeof(SymTab));
  1820.         // Store data
  1821.         OutFile.Push(LongNamesBuffer.Buf(), LongNamesBuffer.GetDataSize());
  1822.         // Align by 2
  1823.         if (OutFile.GetDataSize() & 1) {
  1824.             OutFile.Push("\n", 1);
  1825.         }
  1826.     }
  1827.  
  1828.     // Make sorted or unsorted symbol table, Mach-O style
  1829.     if ((SymbolTableType & 0xFFFF) == FILETYPE_MACHO_LE) {
  1830.  
  1831.         if (SymbolTableType & 0x10000000) {
  1832.             // Sorted table. "__.SYMDEF SORTED" stored as long name
  1833.             memcpy(SymTab.Name, "#1/20           ", 16);
  1834.             // Put file size into symbol table header, including long name length
  1835.             sprintf(SymTab.FileSize, "%u ", Index3Size + LongNameSize);
  1836.         }
  1837.         else {
  1838.             // Unsorted table. "__.SYMDEF" stored as short name
  1839.             memcpy(SymTab.Name, "__.SYMDEF       ", 16);
  1840.             // Put file size into symbol table header
  1841.             sprintf(SymTab.FileSize, "%u ", Index3Size);
  1842.         }
  1843.  
  1844.         // Remove terminating zeroes
  1845.         for (i = 0; i < sizeof(SymTab); i++) {
  1846.             if (((char*)&SymTab)[i] == 0) ((char*)&SymTab)[i] = ' ';
  1847.         }
  1848.  
  1849.         // Store header
  1850.         OutFile.Push(&SymTab, sizeof(SymTab));
  1851.  
  1852.         if (SymbolTableType & 0x10000000) {
  1853.             // Store long name "__.SYMDEF SORTED"
  1854.             OutFile.Push("__.SYMDEF SORTED\0\0\0\0", LongNameSize);
  1855.         }
  1856.  
  1857.         // Store an array of records of string index and member offsets
  1858.         // Store length first
  1859.         uint32 ArrayLength = NumStrings * sizeof(SStringEntry);
  1860.         OutFile.Push(&ArrayLength, sizeof(ArrayLength));
  1861.  
  1862.         // Loop through strings
  1863.         for (i = 0; i < NumStrings; i++) {
  1864.             // Get record in temporary symbol table
  1865.             SStringEntry * psym = &StringEntries[i];
  1866.             SStringEntry Record;
  1867.             Record.String = psym->String;
  1868.             Record.Member = Indexes[psym->Member] + FirstMemberOffset;
  1869.             // Store symbol record
  1870.             OutFile.Push(&Record, sizeof(Record));
  1871.         }
  1872.  
  1873.         // Store length of string table
  1874.         StringsLen = Round4(StringsLen);             // Round up to align by 4
  1875.         OutFile.Push(&StringsLen, sizeof(StringsLen));
  1876.         // Store strings
  1877.         OutFile.Push(StringBuffer.Buf(), StringBuffer.GetDataSize());
  1878.         // Align by 4
  1879.         OutFile.Align(4);
  1880.         // Cross check precalculated size (8 is the size of "!<arch>\n" file identifier)
  1881.         if (OutFile.GetDataSize() != Index3Size + sizeof(SymTab) + 8 + LongNameSize) err.submit(9000);
  1882.     }
  1883. }
  1884.  
  1885.  
  1886. void CLibrary::MakeBinaryFile() {
  1887.     if (cmd.OutputType == FILETYPE_OMF) {
  1888.         MakeBinaryFileOMF();                       // OMF style output library
  1889.     }
  1890.     else {
  1891.         MakeBinaryFileUNIX();                      // UNIX style output library
  1892.     }
  1893. }
  1894.  
  1895.  
  1896. void CLibrary::MakeBinaryFileOMF() {
  1897.     // Make OMF library
  1898.     uint32 PageSize;                              // Page size / alignment for output library
  1899.     uint32 temp;                                  // Temporary
  1900.     uint16 temp16;                                // Temporary
  1901.     uint8  temp8;                                 // Temporary
  1902.     uint32 MemberI;                               // Member number
  1903.     uint32 MemberOffset;                          // File offset of member in output file
  1904.     uint32 MemberStart;                           // Start of member in DataBuffer
  1905.     uint32 MemberEnd;                             // End of member in DataBuffer
  1906.     uint32 SymbolI;                               // Public symbol number
  1907.     uint32 DictionaryOffset2;                     // Offset to hash table
  1908.     CSList<uint32> MemberPageIndex;               // Remember page index of each member
  1909.  
  1910.     // Check number of entries
  1911.     if (DataBuffer.GetNumEntries() >= 0x8000) {
  1912.         err.submit(2606);  return;                 // Error: too big
  1913.     }
  1914.  
  1915.     // Find optimal page size
  1916.     PageSize = DataBuffer.GetDataSize() / (0x8000 - DataBuffer.GetNumEntries());
  1917.     // Make power of 2, minimum 16
  1918.     temp = FloorLog2(PageSize) + 1;
  1919.     if (temp < 4) temp = 4;
  1920.     PageSize = 1 << temp;
  1921.  
  1922.     // Make library header
  1923.     temp8 = OMF_LIBHEAD;
  1924.     OutFile.Push(&temp8, 1);                      // Library header type byte
  1925.     temp16 = PageSize - 3;
  1926.     OutFile.Push(&temp16, 2);                     // Record length
  1927.     OutFile.Push(0, 6);                           // Dictionary offset and size: insert later
  1928.     temp8 = 1;
  1929.     OutFile.Push(&temp8, 1);                      // Flag: case sensitive
  1930.     OutFile.Align(PageSize);                      // Align for first member
  1931.  
  1932.     // Allocate MemberPageIndex
  1933.     MemberPageIndex.SetNum(Indexes.GetNumEntries());
  1934.  
  1935.     // Insert members
  1936.     for (MemberI = 0; MemberI < Indexes.GetNumEntries(); MemberI++) {
  1937.  
  1938.         // Find member in DataBuffer
  1939.         MemberStart = Indexes[MemberI];            // Start of member in DataBuffer
  1940.         if (MemberI+1 < Indexes.GetNumEntries()) {
  1941.             // Not last member
  1942.             MemberEnd = Indexes[MemberI+1];         // End of member in DataBuffer = start of next member
  1943.         }
  1944.         else {
  1945.             // Last member
  1946.             MemberEnd = DataBuffer.GetDataSize();   // End of member in DataBuffer = end of DataBuffer
  1947.         }
  1948.  
  1949.         // Put member into output file
  1950.         MemberOffset = OutFile.Push(DataBuffer.Buf() + MemberStart, MemberEnd - MemberStart);
  1951.  
  1952.         // Align next member
  1953.         OutFile.Align(PageSize);
  1954.  
  1955.         // Member page index
  1956.         MemberPageIndex[MemberI] = MemberOffset / PageSize;
  1957.     }
  1958.  
  1959.     // Change member index to member page index in StringEntries
  1960.     // Loop through StringEntries
  1961.     for (SymbolI = 0; SymbolI < StringEntries.GetNumEntries(); SymbolI++) {
  1962.         // Member index
  1963.         MemberI = StringEntries[SymbolI].Member;
  1964.         if (MemberI < MemberPageIndex.GetNumEntries()) {
  1965.             // Change to member page
  1966.             StringEntries[SymbolI].Member = MemberPageIndex[MemberI];
  1967.         }
  1968.     }
  1969.  
  1970.     // Make OMF_LIBEND record
  1971.     temp8 = OMF_LIBEND;
  1972.     OutFile.Push(&temp8, 1);                      // Library header type byte
  1973.     temp16 = PageSize - 3;                        // Length of rest of record
  1974.     OutFile.Push(&temp16, 2);                     // Record length
  1975.     OutFile.Align(PageSize);                      // Align
  1976.  
  1977.     // Offset to hash table
  1978.     DictionaryOffset2 = OutFile.GetDataSize();
  1979.  
  1980.     // Make hash table for public symbols
  1981.     COMFHashTable HashTable;
  1982.     HashTable.MakeHashTable(StringEntries, StringBuffer, OutFile, this); // Make hash table
  1983.  
  1984.     // Insert missing values in library header
  1985.     // Hash table offset
  1986.     OutFile.Get<uint32>(3) = DictionaryOffset2;
  1987.  
  1988.     // Hash table size
  1989.     OutFile.Get<uint16>(7) = (OutFile.GetDataSize() - DictionaryOffset2) / OMFBlockSize;
  1990. }
  1991.  
  1992.  
  1993. void CLibrary::MakeBinaryFileUNIX() {
  1994.     // Make UNIX library
  1995.     // Combine string index and members into binary file
  1996.  
  1997.     // Reserve file buffer for output file
  1998.     OutFile.SetSize(GetBufferSize());
  1999.  
  2000.     if (cmd.OutputType == FILETYPE_COFF || cmd.OutputType == FILETYPE_ELF || cmd.OutputType == FILETYPE_MACHO_LE) {
  2001.         // COFF, ELF and MAach-O libraries all use Unix-style archive with
  2002.         // differences in symbol table format
  2003.  
  2004.         // Make symbol table
  2005.         MakeSymbolTableUnix();
  2006.  
  2007.         // Store all members
  2008.         OutFile.Push(DataBuffer.Buf(), DataBuffer.GetDataSize());
  2009.     }
  2010.     else {
  2011.         err.submit(2501, GetFileFormatName(cmd.OutputType));
  2012.     }
  2013. }
  2014.  
  2015.  
  2016. void CLibrary::CheckOMFHash(CMemoryBuffer &stringbuf, CSList<SStringEntry> &index) {
  2017.     // Check if OMF library hash table has correct entries for all symbol names
  2018.     uint32 i;                                     // Loop counter
  2019.     int8 * Name;                                  // Public symbol name
  2020.     COMFHashTable HashTab;                        // OMF hash table interpreter
  2021.     uint32 NString;                               // Number of occurrences of Name in hash table
  2022.     uint32 Module;                                // Module with first occurrence of Name
  2023.     uint32 Conf, ConfSum = 0;                     // Count number of conflicting entries in hash table
  2024.  
  2025.     // Initialize hash table interpreter
  2026.     HashTab.Init(&Get<SOMFHashBlock>(DictionaryOffset), DictionarySize);
  2027.  
  2028.     // Loop through public symbol names
  2029.     for (i = 0; i < index.GetNumEntries(); i++) {
  2030.         // Get public name
  2031.         Name = stringbuf.Buf() + index[i].String;
  2032.         // Make hash
  2033.         HashTab.MakeHash(Name);
  2034.         // Search for string
  2035.         NString = HashTab.FindString(Module, Conf);
  2036.         // Count conflicting strings
  2037.         ConfSum += Conf;
  2038.  
  2039.         // Make error message if not 1 occurrence of Name
  2040.         if (NString == 0) err.submit(2603, Name);  // Error if not found
  2041.         if (NString >  1) err.submit(1213, NString, Name);  // Warning more than one occurrence
  2042.  
  2043.         //printf("\n%i occurence of %s, module offset %i", NString, Name, Module);
  2044.     }
  2045.     printf("\n\nHash table %i blocks x 37 buckets at offet 0x%X.\n Efficiency: %i conflicts for %i entries",
  2046.         DictionarySize, DictionaryOffset, ConfSum, index.GetNumEntries());
  2047. }
  2048.  
  2049.  
  2050. const char * CLibrary::GetModuleName(uint32 Index) {
  2051.     // Get name of module from index (UNIX) or page index (OMF)
  2052.     static char name[32];
  2053.     if (cmd.OutputType == FILETYPE_OMF || cmd.OutputType == FILETYPE_OMFLIBRARY) {
  2054.         // Get name of module in OMF library
  2055.         if (Index * PageSize < OutFile.GetDataSize() && OutFile.Get<uint8>(Index * PageSize) == OMF_THEADR) {
  2056.             SOMFRecordPointer rec;                     // Record pointer
  2057.             rec.Start(OutFile.Buf(), Index * PageSize, OutFile.GetDataSize());
  2058.             if (rec.Type2 == OMF_THEADR) {
  2059.                 // Get module name from THEADR record
  2060.                 strncpy(name, rec.GetString(), 16);
  2061.                 // Make sure name is not too long
  2062.                 name[16] = 0;
  2063.                 // Return name
  2064.                 return name;
  2065.             }
  2066.         }
  2067.         // No module starts here
  2068.         return "?";
  2069.     }
  2070.     // UNIX style library.
  2071.     if (Index < Indexes.GetNumEntries()) {
  2072.         // Get offset from Index
  2073.         uint32 Offset = Indexes[Index];
  2074.         if (Offset < DataBuffer.GetDataSize()) {
  2075.             // Copy name from header
  2076.             memcpy(name, DataBuffer.Buf() + Offset, 16);
  2077.             // Check for long name
  2078.             if (strncmp(name, "#1/", 3) == 0) {
  2079.                 // Long name after record
  2080.                 memcpy(name, DataBuffer.Buf()+Offset+sizeof(SUNIXLibraryHeader), 16);
  2081.             }
  2082.             else if (name[0] == '/') {
  2083.                 // Long name in longnames record
  2084.                 uint32 NameIndex = atoi(name+1);
  2085.                 if (NameIndex < LongNamesSize) {
  2086.                     return LongNamesBuffer.Buf() + NameIndex;
  2087.                 }
  2088.                 else {
  2089.                     return "?";
  2090.                 }
  2091.             }
  2092.  
  2093.             // Find terminating '/'
  2094.             for (int i = 0; i < 16; i++) if (name[i] == '/') name[i] = 0;
  2095.             // Make sure name is not too long
  2096.             name[16] = 0;
  2097.             // return name
  2098.             return name;
  2099.         }
  2100.     }
  2101.     // Error
  2102.     return "?";
  2103. }
  2104.