/**************************** library.cpp ********************************** * Author: Agner Fog * Date created: 2006-08-27 * Last modified: 2017-07-27 * Project: objconv * Module: library.cpp * Description: * This module contains code for reading, writing and manipulating function * libraries (archives) of the UNIX type and OMF type. * * Copyright 2006-2017 GNU General Public License http://www.gnu.org/licenses *****************************************************************************/ #include "stdafx.h" // OMF Library flag names SIntTxt OMFLibraryFlags[] = { {0, "Flag = 0"}, {1, "Case sensitive"} }; CLibrary::CLibrary() { // Constructor CurrentOffset = 0; CurrentNumber = 0; LongNames = 0; LongNamesSize = 0; AlignBy = 0; MemberFileType = 0; RepressWarnings = 0; PageSize = 16; } void CLibrary::Go() { // Do to library whatever the command line says char const * MemberName1 = 0; // Name of library member char const * MemberName2 = 0; // Modified name of library member int action = 0; // Action to take on member int FileType1 = 0; // File type of current member int WordSize1 = 0; // Word size of current member if (cmd.DumpOptions && !(cmd.LibraryOptions & CMDL_LIBRARY_EXTRACTMEM)) { // Dump library, but not its members Dump(); return; } // Remove path form member names and check member type before extracting or adding members AlignBy = 2; if (GetDataSize()) FixNames(); if (err.Number()) return; // Stop if error // check LibraryOptions and whether an output file is specified if (cmd.FileOptions & CMDL_FILE_OUTPUT) { // Output is a library file if ((cmd.OutputFile == 0 || cmd.OutputFile[0] == 0) && !(cmd.LibraryOptions & CMDL_LIBRARY_ADDMEMBER)) { err.submit(2503); // Output file name missing return; } // Check extension if (strstr(cmd.OutputFile, ".lib") && strstr(cmd.OutputFile, ".LIB") && strstr(cmd.OutputFile, ".a")) { err.submit(1101); // Warning wrong extension } if (cmd.OutputType >= IMPORT_LIBRARY_MEMBER) { // Wrong output type if (cmd.OutputType == FILETYPE_ASM) { // Attempt to disassemble whole library err.submit(2620); } else { err.submit(2621); } return; } } // Desired alignment = 2 for COFF and ELF, 8 for Mach-O AlignBy = 2; if (cmd.OutputType == FILETYPE_MACHO_LE) AlignBy = 8; // Determine library type and subtype if (cmd.LibrarySubtype == 0) { switch (cmd.OutputType) { case FILETYPE_OMF: case FILETYPE_OMFLIBRARY: cmd.LibrarySubtype = LIBTYPE_OMF; break; case FILETYPE_COFF: case FILETYPE_DOS: cmd.LibrarySubtype = LIBTYPE_WINDOWS; break; case FILETYPE_ELF: cmd.LibrarySubtype = LIBTYPE_LINUX; break; case FILETYPE_MACHO_LE: case FILETYPE_MACHO_BE: cmd.LibrarySubtype = LIBTYPE_BSD_MAC; break; case FILETYPE_LIBRARY: switch (cmd.InputType) { case FILETYPE_COFF: case FILETYPE_DOS: cmd.LibrarySubtype = LIBTYPE_WINDOWS; break; case FILETYPE_ELF: cmd.LibrarySubtype = LIBTYPE_LINUX; break; case FILETYPE_MACHO_LE: case FILETYPE_MACHO_BE: cmd.LibrarySubtype = LIBTYPE_BSD_MAC; break; } break; default: cmd.LibrarySubtype = LIBTYPE_SHORTNAMES; } } // Reserve space for data buffer DataBuffer.SetSize(GetBufferSize()); // Check options if (!cmd.LibraryOptions) cmd.LibraryOptions = CMDL_LIBRARY_CONVERT; if (cmd.Verbose) { // Tell what we are doing if ((cmd.LibraryOptions & CMDL_LIBRARY_ADDMEMBER) && GetDataSize() == 0) { // Creating library printf("\nCreating library %s (%s)", FileName, GetFileFormatName(cmd.OutputType)); } else if (OutputFileName) { // Print input file name printf("\nInput library: %s", FileName); if (cmd.InputType != cmd.OutputType) { // Converting library type. Print input file type int InType = cmd.InputType; if (InType == FILETYPE_LIBRARY || InType == FILETYPE_OMFLIBRARY) InType = cmd.MemberType; printf(", Format: %s", GetFileFormatName(InType)); if (cmd.DesiredWordSize) printf("%i", cmd.DesiredWordSize); } // Print output file name and type printf(", Output: %s, Format: %s", OutputFileName, GetFileFormatName(cmd.OutputType)); if (cmd.DesiredWordSize) printf("%i", cmd.DesiredWordSize); } else { printf("\nExtracting from library file: %s", FileName); } } // Convert library or extract or add or dump all members StartExtracting(); // Initialize before ExtractMember() // Loop through input library while ((MemberName1 = ExtractMember(&MemberBuffer)) != 0) { // Check if any specific action required for this member action = cmd.SymbolChange(MemberName1, &MemberName2, SYMT_LIBRARYMEMBER); /* if ((action != SYMA_CHANGE_NAME && action != SYMA_EXTRACT_MEMBER) || MemberName2 == 0) { MemberName2 = MemberName1; // No name change } */ MemberBuffer.FileName = MemberName1; MemberBuffer.OutputFileName = MemberName2 ? MemberName2 : MemberName1; if (action == SYMA_DELETE_MEMBER) { // Remove this member from library if (cmd.Verbose) { printf("\nRemoving member %s from library", MemberName1); } continue; } if (action == SYMA_ADD_MEMBER) { // Replace this member with new file // (Message comes later when adding new member) continue; } // Check type of this member FileType1 = MemberBuffer.GetFileType(); if (FileType1 == 0) continue; WordSize1 = MemberBuffer.WordSize; if (!(cmd.LibraryOptions & (CMDL_LIBRARY_EXTRACTMEM | CMDL_LIBRARY_ADDMEMBER))) { // Not adding or extracting members. Apply conversion options to all members if (cmd.SymbolChangesRequested() || FileType1 != cmd.OutputType) { // Check file type before conversion int FileType0 = MemberBuffer.GetFileType(); // Conversion or name change requested MemberBuffer.Go(); // Do required conversion if (err.Number()) break; // Stop if error // Check type again after conversion FileType1 = MemberBuffer.GetFileType(); if (MemberBuffer.OutputFileName == 0 || FileType1 != FileType0) { MemberBuffer.OutputFileName = MemberBuffer.SetFileNameExtension(MemberBuffer.FileName); } MemberBuffer.FileName = MemberBuffer.OutputFileName; } } if (MemberFileType == 0) MemberFileType = FileType1; if (WordSize == 0) WordSize = WordSize1; if (FileType1 != MemberFileType || WordSize1 != WordSize) { if (WordSize1 == 0) { // Library member has no wordsize err.submit(1109, MemberBuffer.FileName); } else { // Library members have different type or word size err.submit(1102); } } if (cmd.LibraryOptions & CMDL_LIBRARY_EXTRACTMEM) { // Extract member(s) if (action == SYMA_EXTRACT_MEMBER || cmd.LibraryOptions == CMDL_LIBRARY_EXTRACTALL) { // Extract this member if (cmd.DumpOptions == 0 && cmd.OutputType != CMDL_OUTPUT_DUMP) { // Write this member to file if (err.Number()) return; // Check first if error if (cmd.SymbolChangesRequested() || FileType1 != cmd.OutputType) { // Conversion or name change requested // Check type before conversion int FileType0 = MemberBuffer.GetFileType(); MemberBuffer.Go(); if (err.Number()) return; // Stop if error // Check type after conversion FileType1 = MemberBuffer.GetFileType(); if (MemberBuffer.OutputFileName == 0 /*|| FileType1 != FileType0*/) { MemberBuffer.OutputFileName = MemberBuffer.SetFileNameExtension(MemberBuffer.FileName); } } if (MemberBuffer.OutputFileName == 0) { MemberBuffer.OutputFileName = MemberBuffer.FileName; } if (cmd.Verbose) { // Tell what we are doing if (MemberName1 == MemberName2) { printf("\nExtracting file %s from library", MemberName1); } else { printf("\nExtracting library member %s to file %s", MemberName1, MemberBuffer.OutputFileName); } } if (WordSize1 == 0) { err.submit(1109, MemberName1); } // Write this member to file MemberBuffer.Write(); } else { // Dump this member MemberBuffer.Go(); } } } else if (cmd.DumpOptions == 0) { // Add member to new library if (MemberName2 == 0) MemberName2 = MemberName1; if (cmd.Verbose) { // Tell what we are doing if (strcmp(MemberName1, MemberName2) != 0) { printf("\nRenaming member %s to %s", MemberName1, MemberName2); } } // Put into new library InsertMember(&MemberBuffer); } } // End of loop through library // Stop if error if (err.Number()) return; if (cmd.LibraryOptions & CMDL_LIBRARY_ADDMEMBER) { // Add object files to library SSymbolChange const * sym; // Loop through file names to add while ((sym = cmd.GetMemberToAdd()) != 0) { MemberBuffer.Reset(); // Reset MemberBuffer // Name of object file MemberBuffer.FileName = sym->Name2; // Name of object file MemberBuffer.OutputFileName = sym->Name1; // Name of new member // Read object file MemberBuffer.Read(); // Stop if read failed if (err.Number()) continue; // Detect file type int NewMemberType = MemberBuffer.GetFileType(); if (cmd.Verbose) { // Tell what we are doing if (sym->Done) { printf("\nReplacing member %s with file %s", sym->Name1, sym->Name2); } else { printf("\nAdding member %s from file %s", sym->Name1, sym->Name2); } if (NewMemberType != cmd.OutputType) { // Converting type printf(". Converting from %s.", GetFileFormatName(NewMemberType)); } } // Do any conversion required MemberBuffer.Go(); // Stop if error if (err.Number()) continue; // Check if file type is right after conversion MemberFileType = MemberBuffer.FileType; if (WordSize == 0) WordSize = MemberBuffer.WordSize; if (MemberFileType != cmd.OutputType) { // Library members have different type err.submit(2504, GetFileFormatName(MemberBuffer.FileType)); continue; } if (MemberBuffer.WordSize != WordSize) { // Library members have different word size err.submit(2505, MemberBuffer.WordSize); continue; } // Put into library MemberBuffer.FileName = MemberBuffer.OutputFileName; InsertMember(&MemberBuffer); } // End of loop through object file names } // Stop if error if (err.Number()) return; if (cmd.LibraryOptions & CMDL_LIBRARY_EXTRACTMEM) { cmd.CheckExtractSuccess(); // Check if members to extract were found } if (cmd.FileOptions & CMDL_FILE_OUTPUT) { // Make output library MakeBinaryFile(); // Take over OutFile buffer *this << OutFile; } else { // No output library OutputFileName = 0; } } void CLibrary::FixNames() { // Rebuild library or fix member names // Dispatch according to library type switch (cmd.InputType) { case FILETYPE_OMF: case FILETYPE_OMFLIBRARY: // Rebuild OMF style library and fix long member names RebuildOMF(); break; case FILETYPE_LIBRARY: default: // Fix names in UNIX style library StripMemberNamesUNIX(); break; } } /* Unused: void CLibrary::Rebuild() { // Rebuild library: fix member names // Dispatch according to library type switch (cmd.InputType) { case FILETYPE_OMF: case FILETYPE_OMFLIBRARY: // Rebuild OMF style library RebuildOMF(); break; case FILETYPE_LIBRARY: default: // Rebuild UNIX style library RebuildUNIX(); break; } } void CLibrary::RebuildUNIX() { // Rebuild UNIX style library // Make member names unique and without path. Rebuild symbol table char const * MemberName1 = 0; // Name of library member uint32 OutputFileType; // Save OutputType before changing it OutputFileType = cmd.OutputType; // Loop through input library CurrentOffset = 8; CurrentNumber = 0; while ((MemberName1 = ExtractMember(&MemberBuffer)) != 0) { // Properties of member MemberBuffer.FileName = MemberBuffer.OutputFileName = MemberName1; // Check if import library if (MemberBuffer.Get(0) == 0xFFFF0000) { // Import library. Cannot do anything sensible err.submit(2507, cmd.InputFile); return; } // Remember member type cmd.MemberType = MemberBuffer.GetFileType(); // Temporarily set OutputType to MemberType cmd.OutputType = cmd.MemberType; // Put into new library InsertMember(&MemberBuffer); } // Avoid getting warnings twice for duplicate symbols RepressWarnings = 1; // Make library header etc. and add all members to OutFile MakeBinaryFile(); RepressWarnings = 0; // Restore OutputType cmd.OutputType = OutputFileType; // Replace file buffer by OutFile *this << OutFile; // Clear buffers used for building OutFile StringBuffer.SetSize(0); StringEntries.SetNum(0); Indexes.SetNum(0); DataBuffer.SetSize(0); } */ void CLibrary::RebuildOMF() { // Rebuild OMF style library. // Removes paths from member names, truncates to 16 characters, and makes unique. // Symbol table is removed, not remade SOMFRecordPointer rec; // Current OMF record char * ModuleName; // Module name SStringEntry se; // String entry record to save COMFFileBuilder NewBuffer; // Buffer for rebuilt library uint32 Align; // Alignment // Remember member file type cmd.MemberType = FILETYPE_OMF; // Initialize record pointer rec.Start(Buf(), 0, GetDataSize()); // Read header if (rec.Type2 != OMF_LIBHEAD) {err.submit(2500); return;} // Does not start with library header // Read library header DictionaryOffset = rec.GetDword(); DictionarySize = rec.GetWord(); if ((uint64)DictionaryOffset + DictionarySize >= GetDataSize()) {err.submit(2035); return;} rec.GetByte(); // Ignore flag // Page size / alignment for members PageSize = rec.End + 1; Align = 1 << FloorLog2(PageSize); // Make power of 2 if (PageSize != Align) { err.submit(2601, PageSize); // Error: not a power of 2 } // Copy library header to new buffer NewBuffer.Push(Buf(), PageSize); // Reset record loop end when DictionaryOffset is known rec.FileEnd = DictionaryOffset; // Next record is start of first module rec.GetNext(); if (rec.Type2 != OMF_THEADR) err.submit(2500); // Member does not start with THEADR // Loop through the records of all OMF modules do { // Check record type switch (rec.Type2) { case OMF_THEADR: // Start of module // Get name ModuleName = rec.GetString(); // Remove path, truncate name and make unique ModuleName = ShortenMemberName(ModuleName); // Remember name to check for duplicates // Not needed any more: se.String = StringBuffer.PushString(ModuleName); se.Member = NewBuffer.GetDataSize(); StringEntries.Push(se); // Make new THEADR record NewBuffer.StartRecord(OMF_THEADR); NewBuffer.PutString(ModuleName); NewBuffer.EndRecord(); break; case OMF_MODEND: case OMF_LIBEND: // End of module or library // Copy MODEND record NewBuffer.Push(Buf() + rec.FileOffset, rec.End + 1); // Align output file by PageSize NewBuffer.Align(PageSize); break; default: // Any other record in module // Copy record unchanged NewBuffer.Push(Buf() + rec.FileOffset, rec.End + 1); break; } } // Point to next record while (rec.GetNext(PageSize)); // End of loop through records // Put dictionary offset in LIBHEAD record DictionaryOffset = NewBuffer.GetDataSize(); NewBuffer.Get(3) = DictionaryOffset; // Take over modified library file *this << NewBuffer; // Empty used buffers StringEntries.SetNum(0); StringBuffer.SetSize(0); } void CLibrary::StripMemberNamesUNIX() { // Remove path from member names, set extension to default char * MemberName1 = 0; // Name of library member // Loop through input library CurrentOffset = 8; CurrentNumber = 0; while ((MemberName1 = ExtractMember(&MemberBuffer)) != 0) { // Properties of member // Check if import library if (MemberBuffer.Get(0) == 0xFFFF0000) { // Import library. Cannot do anything sensible err.submit(2507, cmd.InputFile); return; } if (MemberName1[0] == '/') continue; // names record // remember member type if (cmd.MemberType == 0) { cmd.MemberType = MemberBuffer.GetFileType(); } // Fix name StripMemberName(MemberName1); // Check word size if (cmd.DesiredWordSize == 0) { cmd.DesiredWordSize = MemberBuffer.WordSize; } else if (cmd.DesiredWordSize != MemberBuffer.WordSize && MemberBuffer.WordSize != 0) { err.submit(2012, MemberBuffer.WordSize, cmd.DesiredWordSize); // wrong word size return; } if (cmd.OutputType == FILETYPE_LIBRARY || cmd.OutputType == FILETYPE_OMFLIBRARY) { cmd.OutputType = cmd.MemberType; } } } void CLibrary::Dump() { // Print contents of library // Dispatch according to library type switch (cmd.InputType) { case FILETYPE_LIBRARY: DumpUNIX(); break; // Print contents of UNIX style library case FILETYPE_OMFLIBRARY: DumpOMF(); break; // Print contents of OMF style library default: err.submit(9000); // Should not occur } } void CLibrary::DumpOMF() { // Print contents of OMF style library uint8 Flags; // Dictionary flags uint32 i; // Loop counter uint32 Align; // Member alignment uint32 RecordEnd; // End of OMF record SOMFRecordPointer rec; // Current OMF record char * MemberName; // Name of library member char * SymbolName; // Name of public symbol in member uint32 MemberStart = 0; // Start of member uint32 MemberEnd; // End of member uint32 MemberNum = 0; // Member number uint32 FirstPublic; // Index to first public name of current member CMemoryBuffer Strings; // Local string buffer CSList MemberIndex; // Local member index buffer COMF Member; // Local buffer for member DictionaryOffset = GetDataSize(); // Loop end. This value is changed when library header is read rec.Start(Buf(), 0, DictionaryOffset); // Initialize record pointer PageSize = 0; printf("\nDump of library %s\nExported symbols by member:\n", cmd.InputFile); // Loop through the records of all OMF modules do { // Check record type switch (rec.Type2) { case OMF_LIBHEAD: // Library header. This should be the first record if (PageSize || rec.FileOffset > 0) { err.submit(2600); break; // More than one header } // Read library header DictionaryOffset = rec.GetDword(); DictionarySize = rec.GetWord(); if ((uint64)DictionaryOffset + DictionarySize >= GetDataSize()) {err.submit(2035); return;} Flags = rec.GetByte(); // Page size / alignment for members PageSize = rec.End + 1; Align = 1 << FloorLog2(PageSize); // Make power of 2 if (PageSize != Align) { err.submit(2601, PageSize); // Error: not a power of 2 } // Reset record loop end when DictionaryOffset is known rec.FileEnd = DictionaryOffset; // Print values from LIBHEAD printf("\nOMF Library. Page size %i. %s.", PageSize, Lookup(OMFLibraryFlags, Flags)); break; case OMF_THEADR: // Module header. Member starts here MemberName = rec.GetString(); // Get name MemberStart = rec.FileOffset; // Get start address printf("\nMember %s Offset 0x%X", MemberName, MemberStart);// Print member name break; case OMF_MODEND: // Member ends here. RecordEnd = rec.FileOffset + rec.End + 1;// End of record MemberEnd = RecordEnd; // = member end address // Store member in temporary buffer Member.SetSize(0); Member.Push(Buf() + MemberStart, MemberEnd - MemberStart); // Get public names from member FirstPublic = MemberIndex.GetNumEntries(); Member.PublicNames(&Strings, &MemberIndex, ++MemberNum); // Print public names for (i = FirstPublic; i < MemberIndex.GetNumEntries(); i++) { SymbolName = Strings.Buf() + MemberIndex[i].String; printf("\n %s", SymbolName); } // Align next member by PageSize; MemberEnd = (MemberEnd + PageSize - 1) & - (int32)PageSize; rec.End = MemberEnd - rec.FileOffset - 1; break; case OMF_LIBEND: // Last member should end here RecordEnd = rec.FileOffset + rec.End + 1;// End of record if (RecordEnd != DictionaryOffset) err.submit(2602); break; } } // Go to next record while (rec.GetNext()); // End of loop through records // Check hash table integrity CheckOMFHash(Strings, MemberIndex); // Check if there is an extended library dictionary uint32 ExtendedDictionaryOffset = DictionaryOffset + DictionarySize * 512; if (ExtendedDictionaryOffset > GetDataSize()) { err.submit(2500); // File is truncated } if (ExtendedDictionaryOffset < GetDataSize()) { // Library contains extended dictionary uint32 ExtendedDictionarySize = GetDataSize() - ExtendedDictionaryOffset; uint8 DictionaryType = Get(ExtendedDictionaryOffset); // Read first byte of extended dictionary if (DictionaryType == OMF_LIBEXT) { // Extended dictionary in the official format printf("\nExtended dictionary IBM/MS format. size %i", ExtendedDictionarySize); } else if (ExtendedDictionarySize >= 10 && (DictionaryType == 0xAD || Get(ExtendedDictionaryOffset + 2) == MemberNum)) { // Extended dictionary in the proprietary Borland format, documented only in US Patent 5408665, 1995 printf("\nExtended dictionary Borland format. size %i", ExtendedDictionarySize); } else { // Unknown format printf("\nExtended dictionary size %i, unknown type 0x%02X", ExtendedDictionarySize, DictionaryType); } } } void CLibrary::DumpUNIX() { // Print contents of UNIX style library const char * MemberName = 0; CurrentOffset = 8; CurrentNumber = 0; printf("\nDump of library %s", cmd.InputFile); if (cmd.DumpOptions & DUMP_SECTHDR) { // dump headers SUNIXLibraryHeader * Header = 0; // Member header uint32 MemberSize = 0; // Size of member //uint32 HeaderExtra = 0; // Extra added to size of header //uint32 NameIndex; // Index into long names member char * Name = 0; // Name of member int symindex = 0; // count symbol index records int i; // loop counter // Search for member while (CurrentOffset) { //HeaderExtra = 0; // Extract next library member from input library Header = &Get(CurrentOffset); // Size of member MemberSize = (uint32)atoi(Header->FileSize); // Member name Name = Header->Name; // Terminate name for (i = 0; i < 15; i++) { if (Name[i] == ' ') { Name[i+1] = 0; break; } } if (i == 16) Name[i] = 0; if (strncmp(Name, "//", 2) == 0) { // This is the long names member. printf("\nLongnames header \"%s\". Offset 0x%X, size 0x%X", Name, CurrentOffset + (uint32)sizeof(SUNIXLibraryHeader), MemberSize); } else if (Name[0] == '/' && Name[1] <= ' ') { // Symbol index printf("\nSymbol index %i, \"%s\"", ++symindex, Name); } else if (strncmp(Name, "__.SYMDEF", 9) == 0) { // Mac/BSD Symbol index printf("\nSymbol index %i, \"%s\"", ++symindex, Name); } else if (strncmp(Name, "#1/", 3) == 0) { // Name refers to long name after the header // This variant is used by Mac and some versions of BSD //HeaderExtra = atoi(Name+3); Name += sizeof(SUNIXLibraryHeader); if (strncmp(Name, "__.SYMDEF", 9) == 0) { // Symbol table "__.SYMDEF SORTED" as long name printf("\nSymbol index %i, \"%s\"", ++symindex, Name); } } else break; // Point to next member CurrentOffset = NextHeader(CurrentOffset); } CurrentOffset = 8; CurrentNumber = 0; } printf("\n\nExported symbols by member:\n"); // Loop through library while (CurrentOffset + sizeof(SUNIXLibraryHeader) < DataSize) { // Reset buffers StringBuffer.SetSize(0); MemberBuffer.Reset(); StringEntries.SetNum(0); // Get member name MemberName = ExtractMember(&MemberBuffer); if (MemberName == 0) break; printf("\nMember %s", MemberName); MemberBuffer.FileName = MemberName; // Detect file type of member MemberFileType = MemberBuffer.GetFileType(); WordSize = MemberBuffer.WordSize; printf (" - %s", GetFileFormatName(MemberFileType)); if (WordSize) { printf("-%i", MemberBuffer.WordSize); } else { printf(". Type not specified. Possibly alias record"); } // Get symbol table for specific file type switch (MemberFileType) { case FILETYPE_ELF: if (WordSize == 32) { // Make instance of file parser, 32 bit template CELF elf; MemberBuffer >> elf; // Transfer MemberBuffer to elf object elf.PublicNames(&StringBuffer, &StringEntries, 0); break; } else { // Make instance of file parser, 64 bit template CELF elf; MemberBuffer >> elf; // Transfer MemberBuffer to elf object elf.PublicNames(&StringBuffer, &StringEntries, 0); break; } case FILETYPE_COFF: { CCOFF coff; MemberBuffer >> coff; // Transfer MemberBuffer to coff object coff.PublicNames(&StringBuffer, &StringEntries, 0); break;} case FILETYPE_MACHO_LE: if (WordSize == 32) { // Make instance of file parser, 32 bit template CMACHO mac; MemberBuffer >> mac; // Transfer MemberBuffer to coff object mac.PublicNames(&StringBuffer, &StringEntries, 0); break; } else { // Make instance of file parser, 64 bit template CMACHO mac; MemberBuffer >> mac; // Transfer MemberBuffer to coff object mac.PublicNames(&StringBuffer, &StringEntries, 0); break; } case IMPORT_LIBRARY_MEMBER: { // This is an import library char * name1 = MemberBuffer.Buf() + 20; printf("\n Import %s from %s", name1, name1 + strlen(name1) + 1); break;} default: printf("\n Cannot extract symbol names from this file type"); break; } // Loop through table of public names for (uint32 i = 0; i < StringEntries.GetNumEntries(); i++) { uint32 j = StringEntries[i].String; printf("\n %s", StringBuffer.Buf() + j); } } } uint32 CLibrary::NextHeader(uint32 Offset) { // Loop through library headers. // Input = current offset. Output = next offset SUNIXLibraryHeader * Header; // Member header int32 MemberSize; // Size of member //uint32 HeaderExtra = 0; // Extra added to size of header uint32 NextOffset; // Offset of next header if (Offset + sizeof(SUNIXLibraryHeader) >= DataSize) { // No more members return 0; } // Find header Header = &Get(Offset); // Size of member MemberSize = atoi(Header->FileSize); if (MemberSize < 0 || MemberSize + Offset + sizeof(SUNIXLibraryHeader) > DataSize) { err.submit(2500); // Points outside file return 0; } if (strncmp(Header->Name, "#1/", 3) == 0) { // Name refers to long name after the header // This variant is used by Mac and some versions of BSD. // HeaderExtra is included in MemberSize: // HeaderExtra = atoi(Header->Name+3); } // Get next offset NextOffset = Offset + sizeof(SUNIXLibraryHeader) + MemberSize; // Round up to align by 2 NextOffset = (NextOffset + 1) & ~ 1; // Check if last if (NextOffset >= DataSize) NextOffset = 0; return NextOffset; } void CLibrary::StartExtracting() { // Initialize before ExtractMember() if (cmd.InputType == FILETYPE_OMFLIBRARY) { SOMFRecordPointer rec; // OMF records rec.Start(Buf(), 0, GetDataSize()); // Initialize record pointer if (rec.Type2 != OMF_LIBHEAD) { err.submit(2500); return; // This should not happen } // Read library header DictionaryOffset = rec.GetDword(); // Read dictionary offset DictionarySize = rec.GetWord(); // Read dictionary size rec.GetByte(); // Read flag // Page size / alignment for members PageSize = rec.End + 1; uint32 align = 1 << FloorLog2(PageSize); // Make power of 2 if (PageSize != align) { err.submit(2601, PageSize); // Error: not a power of 2 } CurrentOffset = PageSize; // Offset to first library member } else { // Unix style library. First header at offset 8 CurrentOffset = 8; } // Current member number CurrentNumber = 0; } char * CLibrary::ExtractMember(CFileBuffer * Destination) { // Extract library member // Dispatch according to library type if (cmd.InputType == FILETYPE_OMFLIBRARY || cmd.InputType == FILETYPE_OMF) { return ExtractMemberOMF(Destination); } else { return ExtractMemberUNIX(Destination); } } char * CLibrary::ExtractMemberOMF(CFileBuffer * Destination) { // Extract member of OMF style library uint32 RecordEnd; // End of OMF record SOMFRecordPointer rec; // Current OMF record char * MemberName = 0; // Name of library member uint32 MemberStart = 0; // Start of member uint32 MemberEnd = 0; // End of member if (CurrentOffset >= DictionaryOffset) return 0;// No more members rec.Start(Buf(), CurrentOffset, DictionaryOffset);// Initialize record pointer // Loop through the records of all OMF modules do { // Check record type switch (rec.Type2) { case OMF_THEADR: // Module header. Member starts here MemberName = rec.GetString(); // Get name MemberStart = rec.FileOffset; // Get start address break; case OMF_MODEND: // Member ends here. RecordEnd = rec.FileOffset + rec.End +1;// End of record MemberEnd = RecordEnd; // = member end address // Save member as raw data if (Destination) { Destination->SetSize(0); // Make sure destination buffer is empty Destination->FileType = Destination->WordSize = 0; Destination->Push(Buf() + MemberStart, MemberEnd - MemberStart); } // Align next member by PageSize; rec.GetNext(PageSize); CurrentOffset = rec.FileOffset; // Check name //!!if (MemberName[0] == 0) MemberName = (char*)"NoName!"; // Return member name return MemberName; case OMF_LIBEND: // Last member should end here RecordEnd = rec.FileOffset + rec.End + 1;// End of record if (RecordEnd != DictionaryOffset) err.submit(2602); // No more members: return 0; } } // Go to next record while (rec.GetNext()); // End of loop through records err.submit(2610); // Library end record not found return 0; } char * CLibrary::ExtractMemberUNIX(CFileBuffer * Destination) { // Extract member of UNIX style library // This function is called repeatedly to get each member of library/archive SUNIXLibraryHeader * Header = 0; // Member header uint32 MemberSize = 0; // Size of member uint32 HeaderExtra = 0; // Extra added to size of header uint32 NameIndex; // Index into long names member char * Name = 0; // Name of member int Skip = 1; // Skip record and search for next int i; // Loop counter char * p; // Used for loop through string if (CurrentOffset == 0 || CurrentOffset + sizeof(SUNIXLibraryHeader) >= DataSize) { // No more members return 0; } // Search for member while (Skip && CurrentOffset) { HeaderExtra = 0; // Extract next library member from input library Header = &Get(CurrentOffset); // Size of member MemberSize = (uint32)atoi(Header->FileSize); if (MemberSize + CurrentOffset + sizeof(SUNIXLibraryHeader) > DataSize) { err.submit(2500); // Points outside file return 0; } // Member name Name = Header->Name; if (strncmp(Name, "// ", 3) == 0) { // This is the long names member. Remember its position LongNames = CurrentOffset + sizeof(SUNIXLibraryHeader); LongNamesSize = MemberSize; // The long names are terminated by '/' or 0, depending on system, // but may contain non-terminating '/'. Find out which type we have: // Pointer to LongNames record p = Buf() + LongNames; // Find out whether we have terminating zeroes: if ((LongNamesSize > 1 && p[LongNamesSize-1] == '/') || (p[LongNamesSize-1] <= ' ' && p[LongNamesSize-2] == '/')) { // Names are terminated by '/'. Replace all '/' by 0 in the longnames record for (uint32 j = 0; j < LongNamesSize; j++, p++) { if (*p == '/') *p = 0; } } } else if (strncmp(Name, "/ ", 2) == 0 || strncmp(Name, "__.SYMDEF", 9) == 0) { // This is a symbol index member. // The symbol index is not used because we are always building a new symbol index. } else if (Name[0] == '/' && Name[1] >= '0' && Name[1] <= '9' && LongNames) { // Name contains index into LongNames record NameIndex = atoi(Name+1); if (NameIndex < LongNamesSize) { Name = Buf() + LongNames + NameIndex; } else { Name = (char*)"NoName!"; } Skip = 0; } else if (strncmp(Name, "#1/", 3) == 0) { // Name refers to long name after the header // This variant is used by Mac and some versions of BSD HeaderExtra = atoi(Name+3); Name += sizeof(SUNIXLibraryHeader); if (MemberSize > HeaderExtra) { // The length of the name, HeaderExtra, is included in the // Header->FileSize field. Subtract to get the real file size MemberSize -= HeaderExtra; } if (strncmp(Name, "__.SYMDEF", 9) == 0) { // Symbol table "__.SYMDEF SORTED" as long name Skip = 1; } else { Skip = 0; } } else { // Ordinary short name // Name may be terminated by '/' or space. Replace termination char by 0 for (i = 15; i >= 0; i--) { if (Name[i] == ' ' || Name[i] == '/') Name[i] = 0; else break; } // Terminate name with max length by overwriting Date field, which we are not using Name[16] = 0; Skip = 0; } // Point to next member CurrentOffset = NextHeader(CurrentOffset); // Increment number CurrentNumber += !Skip; } // End of while loop // Save member as raw data if (Destination) { Destination->SetSize(0); // Make sure destination buffer is empty Destination->FileType = Destination->WordSize = 0; Destination->Push((int8*)Header + sizeof(SUNIXLibraryHeader) + HeaderExtra, MemberSize); } // Check name if (Name[0] == 0) Name = (char*)"NoName!"; // Return member name return Name; } void CLibrary::InsertMember(CFileBuffer * member) { // Add member to output library if (cmd.OutputType == FILETYPE_OMF) { InsertMemberOMF(member); // OMF style output library } else { InsertMemberUNIX(member); // UNIX style output library } } void CLibrary::InsertMemberOMF(CFileBuffer * member) { // Add member to OMF library // Check file type if (member->GetFileType() != FILETYPE_OMF) err.submit(9000); // Get word size WordSize = member->WordSize; // Store offset uint32 offset = DataBuffer.GetDataSize(); Indexes.Push(offset); // Store member DataBuffer.Push(member->Buf(), member->GetDataSize()); DataBuffer.Align(PageSize); // Member index uint32 mindex = Indexes.GetNumEntries() - 1; // Get public string table COMF omf; *member >> omf; // Translate member to class OMF omf.PublicNames(&StringBuffer, &StringEntries, mindex); // Make list of public names *member << omf; // Return buffer to member } void CLibrary::InsertMemberUNIX(CFileBuffer * member) { // Add next library member to output library uint32 RawSize = 0; // Size of binary file uint32 AlignmentPadding = 0; // Padding after file char * name = 0; // Name of member int NameLength = 0; // length of name int NameAfter = 0; // length of name after MachO header int i; // loop counter, index // Get word size WordSize = member->WordSize; // Make member header SUNIXLibraryHeader header; memset(&header, ' ', sizeof(SUNIXLibraryHeader)); // Fill with spaces // Name of member if (member->OutputFileName == 0 || *member->OutputFileName == 0) member->OutputFileName = member->FileName; if (cmd.LibrarySubtype == LIBTYPE_SHORTNAMES) { // Make short name name = ShortenMemberName(member->OutputFileName); } else { // Remove path from library member name. Original long name is overwritten name = StripMemberName((char*)(member->OutputFileName)); } NameLength = strlen(name); if (cmd.OutputType == FILETYPE_MACHO_LE && cmd.LibrarySubtype != LIBTYPE_SHORTNAMES) { // Mach-O library stores name after header record. // Name is zero padded to length 4 modulo 8 to align by 8 int pad = 8 - ((NameLength + 4) & 7); NameAfter = NameLength + pad; sprintf(header.Name, "#1/%i ", NameAfter); } else { // ELF and COFF library store names < 16 characters in the name field if (NameLength < 16) { // (Don't use sprintf to write header.Name here: It seems that Gnu sprintf checks the size of the // buffer it is writing to. Gives buffer overrun error when termniating zero goes beyond the name field) memset(header.Name, ' ', 16); memcpy(header.Name, name, NameLength); header.Name[NameLength] = '/'; } else { // store in LongNamesBuffer if (cmd.OutputType == FILETYPE_COFF) { // COFF: Name is zero-terminated i = LongNamesBuffer.PushString(name); } else { // ELF: Name terminated by "/\n" i = LongNamesBuffer.Push(name, NameLength); LongNamesBuffer.Push("/\n", 2); } // store index into long names member sprintf(header.Name, "/%i ", i); } } // Date sprintf(header.Date, "%u ", (uint32)time(0)); // User and group id header.UserID[0] = '0'; header.GroupID[0] = '0'; // File mode strcpy(header.FileMode, "100666"); // Size of binary file RawSize = member->GetDataSize(); // Calculate alignment padding if (AlignBy) { AlignmentPadding = uint32(-int32(RawSize)) & (AlignBy-1); } // File size including name string sprintf(header.FileSize, "%u ", NameAfter + RawSize + AlignmentPadding); // Header end header.HeaderEnd[0] = '`'; header.HeaderEnd[1] = '\n'; // Remove terminating zeroes for (uint32 i = 0; i < sizeof(SUNIXLibraryHeader); i++) { if (((char*)&header)[i] == 0) ((char*)&header)[i] = ' '; } // Store offset uint32 offset = DataBuffer.GetDataSize(); Indexes.Push(offset); // Store member header DataBuffer.Push(&header, sizeof(header)); if (cmd.OutputType == FILETYPE_MACHO_LE) { // Store member name after header if Mach-O if (NameAfter) { // Mach-O library stores name after header record. DataBuffer.PushString(name); } DataBuffer.Align(AlignBy); } // Store member DataBuffer.Push(member->Buf(), RawSize); // Align by padding with '\n' for (uint32 i = 0; i < AlignmentPadding; i++) { DataBuffer.Push("\n", 1); } // Member index uint32 mindex = Indexes.GetNumEntries() - 1; // Get public string table switch(member->GetFileType()) { case FILETYPE_COFF: { CCOFF coff; *member >> coff; // Translate member to type COFF coff.PublicNames(&StringBuffer, &StringEntries, mindex); // Make list of public names *member << coff; // Return buffer to member break;} case FILETYPE_OMF: { COMF omf; *member >> omf; // Translate member to type COFF omf.PublicNames(&StringBuffer, &StringEntries, mindex); // Make list of public names *member << omf; // Return buffer to member break;} case FILETYPE_ELF: if (WordSize == 32) { // Make instance of file parser, 32 bit template CELF elf; *member >> elf; // Translate member to type ELF elf.PublicNames(&StringBuffer, &StringEntries, mindex); // Make list of public names *member << elf; // Return buffer to member break; } else { // Make instance of file parser, 64 bit template CELF elf; *member >> elf; // Translate member to type ELF elf.PublicNames(&StringBuffer, &StringEntries, mindex); // Make list of public names *member << elf; // Return buffer to member break; } case FILETYPE_MACHO_LE: if (WordSize == 32) { // Make instance of file parser, 32 bit template CMACHO mac; *member >> mac; // Translate member to type ELF mac.PublicNames(&StringBuffer, &StringEntries, mindex); // Make list of public names *member << mac; // Return buffer to member break; } else { // Make instance of file parser, 64 bit template CMACHO mac; *member >> mac; // Translate member to type ELF mac.PublicNames(&StringBuffer, &StringEntries, mindex); // Make list of public names *member << mac; // Return buffer to member break; } default: // Type not supported err.submit(2501, GetFileFormatName(member->GetFileType())); break; } } /* unused: char * CLibrary::TruncateMemberName(char const * name) { // Remove path and truncate object file name to 15 characters // This function removes any path from the member name, // changes the extension to the default for the the output file type, // changes any spaces to underscores, and // truncates the member name to 15 characters for the sake of compatibility. // The return value is an ASCII string in a static buffer static char TruncName[32]; // Truncated name int maxlen; // Max length, not including extension char const * p1; // Point to start of name without path char const * extension; // Default extension for file type int i; // Loop counter int len; // String length static int DummyNumber = 0; // Count invalid/null names int FileType; // File type // Remove path len = (int)strlen(name); p1 = name; for (i = len-1; i >= 0; i--) { if (name[i] == '/' || name[i] == '\\' || name[i] == ':') { p1 = name + i + 1; break; } } // Remove extension len = (int)strlen(p1); for (i = len-1; i >= 0; i--) { if (p1[i] == '.') { len = i; break; } } // Check if any name remains if (len == 0) { // No name. Make one sprintf(TruncName, "NoName%i", ++DummyNumber); p1 = TruncName; len = (int)strlen(p1); } // Get file type FileType = cmd.OutputType; if (FileType == CMDL_OUTPUT_DUMP || FileType == 0) FileType = cmd.InputType; if (FileType >= FILETYPE_LIBRARY) FileType = cmd.MemberType; // Get default extension and max length of name without extension if (FileType == FILETYPE_COFF || FileType == FILETYPE_OMF) { maxlen = 11; extension = ".obj"; } else { maxlen = 13; extension = ".o"; } if (len > maxlen) len = maxlen; // Truncate name strncpy(TruncName, p1, len); TruncName[len] = 0; // Remove any spaces or other illegal characters len = (int)strlen(TruncName); for (i = 0; i < len; i++) { if ((uint8)TruncName[i] <= 0x20) TruncName[i] = '_'; } // Add default extension strcpy(TruncName+strlen(TruncName), extension); // Terminate TruncName[15] = 0; return TruncName; } */ char * CLibrary::StripMemberName(char * name) { // Remove path from zero-terminated library member name and set extension to default. // Note: Original long name is overwritten char * p1; // Point to start of name without path const char * extension = 0; // Default extension for file type int i; // Loop counter int len0; // Original string length int len; // String length int nlen; // length of name without extension int elen = 0; // length of extension static int DummyNumber = 0; // Count invalid/null names int FileType; // File type // Length len0 = len = (int)strlen(name); // Remove path p1 = name; for (i = len-1; i >= 0; i--) { if (name[i] == '/' || name[i] == '\\' || name[i] == ':') { p1 = name + i + 1; break; } } len -= i + 1; // move to begin of buffer if (p1 > name) { memmove(name, p1, len + 1); } // Get file type if (cmd.MemberType) { FileType = cmd.MemberType; } else if (cmd.LibraryOptions & CMDL_LIBRARY_EXTRACTMEM) { FileType = cmd.InputType; } else { FileType = cmd.OutputType; } if (FileType == CMDL_OUTPUT_DUMP || FileType == 0) FileType = cmd.InputType; if (FileType >= FILETYPE_LIBRARY) FileType = cmd.MemberType; // Get default extension and max length of name without extension if (FileType == FILETYPE_COFF || FileType == FILETYPE_OMF) { extension = ".obj"; elen = 4; } else if (FileType == FILETYPE_ELF || FileType == FILETYPE_MACHO_LE || FileType == FILETYPE_MACHO_BE) { extension = ".o"; elen = 2; } // find extension for (nlen = len-1; nlen >= 0; nlen--) { if (name[nlen] == '.') { break; } } // Remove any spaces or other illegal characters for (i = 0; i < nlen; i++) { if ((uint8)name[i] <= 0x20 || name[i] == '.') name[i] = '_'; } // Check if any name remains if ((len == 0 && len0 > 12) || nlen == 0) { // No name. Make one sprintf(name, "NoName%i", ++DummyNumber); len = (int)strlen(name); } // Replace extension if (len + elen <= len0 && extension != 0) { strcpy(name + nlen, extension); } // Terminate return name; } char * CLibrary::ShortenMemberName(char const *name) { // Truncate library member name to 15 characters and make unique // The path is removed and the extension set to default. // The original long name is not overwritten static char fixedName[32]; // Modified name char const * p1; // Point to start of name without path char const * extension; // Default extension for file type int i; // Loop counter int len; // Filename length int len0; // Filename length without extension int elen; // length of extension static int RunningNumber = 0; // Enumerate truncated names int FileType; // File type // Length len = (int)strlen(name); // Skip path p1 = name; for (i = len-1; i >= 0; i--) { if (name[i] == '/' || name[i] == '\\' || name[i] == ':') { p1 = name + i + 1; break; } } len = (int)strlen(name); // move to static buffer if (len > 15) len = 15; memcpy(fixedName, p1, len); fixedName[len] = 0; // find extension for (i = len-1; i >= 0; i--) { if (fixedName[i] == '.') { fixedName[i] = 0; break; } } // length without extension len0 = (int)strlen(fixedName); // Remove any spaces or other illegal characters for (i = 0; i < len0; i++) { if ((uint8)fixedName[i] <= 0x20 || fixedName[i] == '.') fixedName[i] = '_'; } // Check if any name remains if (len0 == 0) { // No name. Make one sprintf(fixedName, "NoName_%X", RunningNumber++); len0 = (int)strlen(fixedName); } // Get file type FileType = cmd.OutputType; if (FileType == CMDL_OUTPUT_DUMP || FileType == 0) FileType = cmd.InputType; if (FileType >= FILETYPE_LIBRARY) FileType = cmd.MemberType; // Get default extension and max length of name without extension if (FileType == FILETYPE_COFF || FileType == FILETYPE_OMF) { extension = ".obj"; elen = 4; } else { extension = ".o"; elen = 2; } // Make unique and add extension if (len0 + elen >= 15) { // Name is truncated or possibly identical to some other truncated name. // Insert 2-, 3- or 4-digit running hexadecimal number. if (RunningNumber < 0x100) { sprintf(fixedName + 12 - elen, "_%02X%s", RunningNumber++, extension); } else if (RunningNumber < 0x1000) { sprintf(fixedName + 12 - elen, "%03X%s", RunningNumber++, extension); } else { sprintf(fixedName + 11 - elen, "%04X%s", (RunningNumber++ & 0xFFFF), extension); } } else { // Short name. Just add extension strcpy(fixedName + len0, extension); } // Return static name buffer return fixedName; } /* Unused: int CLibrary::MemberNameExistsUNIX(char * name) { // Check if DataBuffer contains a member with this name char Name1[20], Name2[20]; uint32 i, j; // Terminate name without extension memcpy(Name1, name, 16); for (j = 0; j < 16; j++) { if (Name1[j] == '.' || Name1[j] == '/') Name1[j] = 0; } // Loop through previous members in DataBuffer for (i = 0; i < Indexes.GetNumEntries(); i++) { uint32 offset = Indexes[i]; // Copy name of member i memcpy(Name2, DataBuffer.Buf() + offset, 16); // Terminate name2 for (j = 0; j < 16; j++) { if (Name2[j] == '.' || Name2[j] == '/') Name2[j] = 0; } // Case-insensitive compare of names if (stricmp(Name1, Name2) == 0) { // Identical name found return i + 1; } } // No identical name found return 0; }*/ void CLibrary::SortStringTable() { // Sort the string table in ASCII order // Length of table int32 n = StringEntries.GetNumEntries(); if (n <= 0) return; // Point to table of SStringEntry records SStringEntry * Table = &StringEntries[0]; // String pointers char * s1, * s2; // Simple Shell sort with Sedgewick gaps: int32 i, j, k, gap; for (k = 15; k >= 0; k--) { gap = (1 << 2 * k) | (3 << k >> 1) | 1; // Sedgewick gap grants O(N^4/3) for (i = gap; i < n; i++) { SStringEntry key = Table[i]; char * strkey = StringBuffer.Buf() + key.String; for (j = i - gap; j >= 0 && strcmp(strkey, StringBuffer.Buf() + Table[j].String) < 0; j -= gap) { Table[j + gap] = Table[j]; } Table[j + gap] = key; } } // Now StringEntries has been sorted. Reorder StringBuffer to the sort order. CMemoryBuffer SortedStringBuffer; // Temporary buffer for strings in sort order for (i = 0; i < n; i++) { // Pointer to old string s1 = StringBuffer.Buf() + Table[i].String; // Update table to point to new string Table[i].String = SortedStringBuffer.GetDataSize(); // Put string into SortedStringBuffer SortedStringBuffer.PushString(s1); } if (SortedStringBuffer.GetDataSize() != StringBuffer.GetDataSize()) { // The two string buffers should be same size err.submit(9000); return; } // Copy SortedStringBuffer into StringBuffer memcpy(StringBuffer.Buf(), SortedStringBuffer.Buf(), StringBuffer.GetDataSize()); // Check for duplicate symbols for (i = 0; i < n-1; i++) { s1 = StringBuffer.Buf() + Table[i].String; for (j = i + 1; j < n; j++) { s2 = StringBuffer.Buf() + Table[j].String; if (strcmp(s1,s2) == 0) { // Duplicate found // Compose error string "Modulename1 and Modulename2" uint32 errstring = LongNamesBuffer.GetDataSize(); LongNamesBuffer.PushString(GetModuleName(Table[i].Member)); LongNamesBuffer.SetSize(LongNamesBuffer.GetDataSize()-1); // remove terminating zero LongNamesBuffer.Push(" and ", 5); LongNamesBuffer.PushString(GetModuleName(Table[j].Member)); err.submit(1214, s1, (char*)LongNamesBuffer.Buf() + errstring); LongNamesBuffer.SetSize(errstring); // remove string again i++; // Prevent excessive messages } else { break; } } } } uint32 EndianChange(uint32 n) { // Convert little-endian to big-endian number, or vice versa return (n << 24) | ((n & 0x0000FF00) << 8) | ((n & 0x00FF0000) >> 8) | (n >> 24); } uint32 RoundEven(uint32 n) { // Round up number to nearest even return (n + 1) & uint32(-2); } uint32 Round4(uint32 n) { // Round up number to nearest multiple of 4 return (n + 3) & uint32(-4); } void CLibrary::MakeSymbolTableUnix() { // Make symbol table for COFF, ELF or MACHO library // Uses UNIX archive format for COFF, BSD and Mac uint32 i; // Loop counter uint32 MemberOffset; // Offset to member uint32 LongNameSize = 0; // Length of symbol table name if stored after record int SymbolTableType = cmd.OutputType; // FILETYPE_COFF = 1: COFF // FILETYPE_ELF = 3: ELF // FILETYPE_MACHO_LE = 4: Mac, unsorted // 0x10000004: Mac, sorted // Newer Mac tools require the sorted type, unless there are multiple publics with same name if (SymbolTableType == FILETYPE_MACHO_LE) SymbolTableType |= 0x10000000; // Make symbol table header SUNIXLibraryHeader SymTab; // Symbol table header memset(&SymTab, ' ', sizeof(SymTab)); // Fill with spaces SymTab.Name[0] = '/'; // Name = '/' // The silly Mac linker requires that the symbol table has a date stamp not // older than the .a file. Fix this by post-dating the symbol table: uint32 PostDate = 0; if (SymbolTableType & 0x10000000) PostDate = 100; // Post-date if mac sorted symbol table sprintf(SymTab.Date, "%u ", (uint32)time(0) + PostDate); // Date stamp for symbol table SymTab.UserID[0] = '0'; // UserID = 0 (may be omitted in COFF) SymTab.GroupID[0] = '0'; // GroupID = 0 (may be omitted in COFF) strcpy(SymTab.FileMode, "100666"); // FileMode = 0100666 (may be 0 in COFF) SymTab.HeaderEnd[0] = '`'; // End with "`\n" SymTab.HeaderEnd[1] = '\n'; // File header OutFile.Push("!\n", 8); uint32 NumMembers = Indexes.GetNumEntries(); // Number of members uint32 NumStrings = StringEntries.GetNumEntries(); // Number of symbol names uint32 StringsLen = StringBuffer.GetDataSize(); // Size of string table // Calculate sizes of string index records, not including header // Unsorted index, used in ELF and COFF libraries uint32 Index1Size = (NumStrings+1)*4 + StringsLen; // Sorted index, used in COFF libraries as second member uint32 Index2Size = (NumMembers+2)*4 + NumStrings*2 + StringsLen; // Sorted index, used in Mach-O libraries uint32 Index3Size = Round4(NumStrings*8 + 8 + StringsLen); // Longnames member uint32 LongnamesMemberSize = 0; // Official MS COFF reference says that the "//" longnames member must be present, // even if it is unused, but MS LIB does not make it unless it is needed. // Here, we will include the longnames member only if it is needed if ((SymbolTableType == FILETYPE_COFF || SymbolTableType == FILETYPE_ELF) && LongNamesBuffer.GetDataSize()) { LongnamesMemberSize = sizeof(SUNIXLibraryHeader) + LongNamesBuffer.GetDataSize(); } // Offset to first member uint32 FirstMemberOffset = 0; switch (SymbolTableType) { case FILETYPE_COFF: FirstMemberOffset = 8 + 2*sizeof(SUNIXLibraryHeader) + RoundEven(Index1Size) + RoundEven(Index2Size) + RoundEven(LongnamesMemberSize); break; case FILETYPE_ELF: FirstMemberOffset = 8 + sizeof(SUNIXLibraryHeader) + RoundEven(Index1Size) + RoundEven(LongnamesMemberSize); break; case FILETYPE_MACHO_LE: FirstMemberOffset = 8 + sizeof(SUNIXLibraryHeader) + Index3Size; break; case FILETYPE_MACHO_LE | 0x10000000: // Mac, sorted LongNameSize = 20; FirstMemberOffset = 8 + sizeof(SUNIXLibraryHeader) + Index3Size + LongNameSize; break; default: err.submit(2501, GetFileFormatName(cmd.OutputType)); } // Make unsorted symbol table for COFF or ELF output if (SymbolTableType == FILETYPE_COFF || SymbolTableType == FILETYPE_ELF) { // Put file size into symbol table header sprintf(SymTab.FileSize, "%u ", Index1Size); // Remove terminating zeroes for (i = 0; i < sizeof(SymTab); i++) { if (((char*)&SymTab)[i] == 0) ((char*)&SymTab)[i] = ' '; } // Store header OutFile.Push(&SymTab, sizeof(SymTab)); // Store table of offsets uint32 BigEndian; // Number converted to big-endian BigEndian = EndianChange(NumStrings); OutFile.Push(&BigEndian, sizeof(BigEndian)); // Number of symbols // Loop through strings for (i = 0; i < NumStrings; i++) { // Get record in temporary symbol table SStringEntry * psym = &StringEntries[i]; // Get offset of member in DataBuffer MemberOffset = Indexes[psym->Member]; // Add size of headers to compute member offset in final file BigEndian = EndianChange(MemberOffset + FirstMemberOffset); // Store offset as big endian number OutFile.Push(&BigEndian, sizeof(BigEndian)); } // Store strings OutFile.Push(StringBuffer.Buf(), StringBuffer.GetDataSize()); // Align by 2 if (OutFile.GetDataSize() & 1) { OutFile.Push("\n", 1); } } // Sort string table if (!RepressWarnings && SymbolTableType != FILETYPE_MACHO_LE) SortStringTable(); // Make sorted symbol table, COFF style if (SymbolTableType == FILETYPE_COFF) { if (NumMembers > 0xFFFF) err.submit(2502); // Too many members // Reuse symbol table header, change size entry sprintf(SymTab.FileSize, "%u ", Index2Size); // Remove terminating zeroes for (i = 0; i < sizeof(SymTab); i++) { if (((char*)&SymTab)[i] == 0) ((char*)&SymTab)[i] = ' '; } // Store header OutFile.Push(&SymTab, sizeof(SymTab)); // Store number of members OutFile.Push(&NumMembers, sizeof(NumMembers)); // Store member offsets for (i = 0; i < NumMembers; i++) { MemberOffset = Indexes[i] + FirstMemberOffset; OutFile.Push(&MemberOffset, sizeof(MemberOffset)); } // Store number of symbols OutFile.Push(&NumStrings, sizeof(NumStrings)); // Store member index for each string // Loop through strings for (i = 0; i < NumStrings; i++) { // Get record in temporary symbol table SStringEntry * psym = &StringEntries[i]; // Get member index, 16 bits uint16 MemberI = (uint16)(psym->Member + 1); OutFile.Push(&MemberI, sizeof(MemberI)); } // Store strings OutFile.Push(StringBuffer.Buf(), StringBuffer.GetDataSize()); // Align by 2 if (OutFile.GetDataSize() & 1) { OutFile.Push("\n", 1); } } // Make longnames table member for COFF or ELF output // (The decision whether to include a "//" longnames member is taken above) if (LongnamesMemberSize) { // reuse SymTab strcpy(SymTab.Name, "// "); // Name = "//" memset(SymTab.FileSize, ' ', 10); sprintf(SymTab.FileSize, "%u", LongNamesBuffer.GetDataSize()); // Remove terminating zeroes for (i = 0; i < sizeof(SymTab); i++) { if (((char*)&SymTab)[i] == 0) ((char*)&SymTab)[i] = ' '; } // Store header OutFile.Push(&SymTab, sizeof(SymTab)); // Store data OutFile.Push(LongNamesBuffer.Buf(), LongNamesBuffer.GetDataSize()); // Align by 2 if (OutFile.GetDataSize() & 1) { OutFile.Push("\n", 1); } } // Make sorted or unsorted symbol table, Mach-O style if ((SymbolTableType & 0xFFFF) == FILETYPE_MACHO_LE) { if (SymbolTableType & 0x10000000) { // Sorted table. "__.SYMDEF SORTED" stored as long name memcpy(SymTab.Name, "#1/20 ", 16); // Put file size into symbol table header, including long name length sprintf(SymTab.FileSize, "%u ", Index3Size + LongNameSize); } else { // Unsorted table. "__.SYMDEF" stored as short name memcpy(SymTab.Name, "__.SYMDEF ", 16); // Put file size into symbol table header sprintf(SymTab.FileSize, "%u ", Index3Size); } // Remove terminating zeroes for (i = 0; i < sizeof(SymTab); i++) { if (((char*)&SymTab)[i] == 0) ((char*)&SymTab)[i] = ' '; } // Store header OutFile.Push(&SymTab, sizeof(SymTab)); if (SymbolTableType & 0x10000000) { // Store long name "__.SYMDEF SORTED" OutFile.Push("__.SYMDEF SORTED\0\0\0\0", LongNameSize); } // Store an array of records of string index and member offsets // Store length first uint32 ArrayLength = NumStrings * sizeof(SStringEntry); OutFile.Push(&ArrayLength, sizeof(ArrayLength)); // Loop through strings for (i = 0; i < NumStrings; i++) { // Get record in temporary symbol table SStringEntry * psym = &StringEntries[i]; SStringEntry Record; Record.String = psym->String; Record.Member = Indexes[psym->Member] + FirstMemberOffset; // Store symbol record OutFile.Push(&Record, sizeof(Record)); } // Store length of string table StringsLen = Round4(StringsLen); // Round up to align by 4 OutFile.Push(&StringsLen, sizeof(StringsLen)); // Store strings OutFile.Push(StringBuffer.Buf(), StringBuffer.GetDataSize()); // Align by 4 OutFile.Align(4); // Cross check precalculated size (8 is the size of "!\n" file identifier) if (OutFile.GetDataSize() != Index3Size + sizeof(SymTab) + 8 + LongNameSize) err.submit(9000); } } void CLibrary::MakeBinaryFile() { if (cmd.OutputType == FILETYPE_OMF) { MakeBinaryFileOMF(); // OMF style output library } else { MakeBinaryFileUNIX(); // UNIX style output library } } void CLibrary::MakeBinaryFileOMF() { // Make OMF library uint32 PageSize; // Page size / alignment for output library uint32 temp; // Temporary uint16 temp16; // Temporary uint8 temp8; // Temporary uint32 MemberI; // Member number uint32 MemberOffset; // File offset of member in output file uint32 MemberStart; // Start of member in DataBuffer uint32 MemberEnd; // End of member in DataBuffer uint32 SymbolI; // Public symbol number uint32 DictionaryOffset2; // Offset to hash table CSList MemberPageIndex; // Remember page index of each member // Check number of entries if (DataBuffer.GetNumEntries() >= 0x8000) { err.submit(2606); return; // Error: too big } // Find optimal page size PageSize = DataBuffer.GetDataSize() / (0x8000 - DataBuffer.GetNumEntries()); // Make power of 2, minimum 16 temp = FloorLog2(PageSize) + 1; if (temp < 4) temp = 4; PageSize = 1 << temp; // Make library header temp8 = OMF_LIBHEAD; OutFile.Push(&temp8, 1); // Library header type byte temp16 = PageSize - 3; OutFile.Push(&temp16, 2); // Record length OutFile.Push(0, 6); // Dictionary offset and size: insert later temp8 = 1; OutFile.Push(&temp8, 1); // Flag: case sensitive OutFile.Align(PageSize); // Align for first member // Allocate MemberPageIndex MemberPageIndex.SetNum(Indexes.GetNumEntries()); // Insert members for (MemberI = 0; MemberI < Indexes.GetNumEntries(); MemberI++) { // Find member in DataBuffer MemberStart = Indexes[MemberI]; // Start of member in DataBuffer if (MemberI+1 < Indexes.GetNumEntries()) { // Not last member MemberEnd = Indexes[MemberI+1]; // End of member in DataBuffer = start of next member } else { // Last member MemberEnd = DataBuffer.GetDataSize(); // End of member in DataBuffer = end of DataBuffer } // Put member into output file MemberOffset = OutFile.Push(DataBuffer.Buf() + MemberStart, MemberEnd - MemberStart); // Align next member OutFile.Align(PageSize); // Member page index MemberPageIndex[MemberI] = MemberOffset / PageSize; } // Change member index to member page index in StringEntries // Loop through StringEntries for (SymbolI = 0; SymbolI < StringEntries.GetNumEntries(); SymbolI++) { // Member index MemberI = StringEntries[SymbolI].Member; if (MemberI < MemberPageIndex.GetNumEntries()) { // Change to member page StringEntries[SymbolI].Member = MemberPageIndex[MemberI]; } } // Make OMF_LIBEND record temp8 = OMF_LIBEND; OutFile.Push(&temp8, 1); // Library header type byte temp16 = PageSize - 3; // Length of rest of record OutFile.Push(&temp16, 2); // Record length OutFile.Align(PageSize); // Align // Offset to hash table DictionaryOffset2 = OutFile.GetDataSize(); // Make hash table for public symbols COMFHashTable HashTable; HashTable.MakeHashTable(StringEntries, StringBuffer, OutFile, this); // Make hash table // Insert missing values in library header // Hash table offset OutFile.Get(3) = DictionaryOffset2; // Hash table size OutFile.Get(7) = (OutFile.GetDataSize() - DictionaryOffset2) / OMFBlockSize; } void CLibrary::MakeBinaryFileUNIX() { // Make UNIX library // Combine string index and members into binary file // Reserve file buffer for output file OutFile.SetSize(GetBufferSize()); if (cmd.OutputType == FILETYPE_COFF || cmd.OutputType == FILETYPE_ELF || cmd.OutputType == FILETYPE_MACHO_LE) { // COFF, ELF and MAach-O libraries all use Unix-style archive with // differences in symbol table format // Make symbol table MakeSymbolTableUnix(); // Store all members OutFile.Push(DataBuffer.Buf(), DataBuffer.GetDataSize()); } else { err.submit(2501, GetFileFormatName(cmd.OutputType)); } } void CLibrary::CheckOMFHash(CMemoryBuffer &stringbuf, CSList &index) { // Check if OMF library hash table has correct entries for all symbol names uint32 i; // Loop counter int8 * Name; // Public symbol name COMFHashTable HashTab; // OMF hash table interpreter uint32 NString; // Number of occurrences of Name in hash table uint32 Module; // Module with first occurrence of Name uint32 Conf, ConfSum = 0; // Count number of conflicting entries in hash table // Initialize hash table interpreter HashTab.Init(&Get(DictionaryOffset), DictionarySize); // Loop through public symbol names for (i = 0; i < index.GetNumEntries(); i++) { // Get public name Name = stringbuf.Buf() + index[i].String; // Make hash HashTab.MakeHash(Name); // Search for string NString = HashTab.FindString(Module, Conf); // Count conflicting strings ConfSum += Conf; // Make error message if not 1 occurrence of Name if (NString == 0) err.submit(2603, Name); // Error if not found if (NString > 1) err.submit(1213, NString, Name); // Warning more than one occurrence //printf("\n%i occurence of %s, module offset %i", NString, Name, Module); } printf("\n\nHash table %i blocks x 37 buckets at offet 0x%X.\n Efficiency: %i conflicts for %i entries", DictionarySize, DictionaryOffset, ConfSum, index.GetNumEntries()); } const char * CLibrary::GetModuleName(uint32 Index) { // Get name of module from index (UNIX) or page index (OMF) static char name[32]; if (cmd.OutputType == FILETYPE_OMF || cmd.OutputType == FILETYPE_OMFLIBRARY) { // Get name of module in OMF library if (Index * PageSize < OutFile.GetDataSize() && OutFile.Get(Index * PageSize) == OMF_THEADR) { SOMFRecordPointer rec; // Record pointer rec.Start(OutFile.Buf(), Index * PageSize, OutFile.GetDataSize()); if (rec.Type2 == OMF_THEADR) { // Get module name from THEADR record strncpy(name, rec.GetString(), 16); // Make sure name is not too long name[16] = 0; // Return name return name; } } // No module starts here return "?"; } // UNIX style library. if (Index < Indexes.GetNumEntries()) { // Get offset from Index uint32 Offset = Indexes[Index]; if (Offset < DataBuffer.GetDataSize()) { // Copy name from header memcpy(name, DataBuffer.Buf() + Offset, 16); // Check for long name if (strncmp(name, "#1/", 3) == 0) { // Long name after record memcpy(name, DataBuffer.Buf()+Offset+sizeof(SUNIXLibraryHeader), 16); } else if (name[0] == '/') { // Long name in longnames record uint32 NameIndex = atoi(name+1); if (NameIndex < LongNamesSize) { return LongNamesBuffer.Buf() + NameIndex; } else { return "?"; } } // Find terminating '/' for (int i = 0; i < 16; i++) if (name[i] == '/') name[i] = 0; // Make sure name is not too long name[16] = 0; // return name return name; } } // Error return "?"; }