60a4b1c9ef
git-svn-id: svn://kolibrios.org@9683 a494cfbc-eb01-0410-851d-a64ba20cac60
2104 lines
80 KiB
C++
2104 lines
80 KiB
C++
/**************************** 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<uint32>(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<uint32>(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<uint32>(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<SStringEntry> 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<uint8>(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<uint16>(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<SUNIXLibraryHeader>(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<ELF32STRUCTURES> elf;
|
|
MemberBuffer >> elf; // Transfer MemberBuffer to elf object
|
|
elf.PublicNames(&StringBuffer, &StringEntries, 0);
|
|
break;
|
|
}
|
|
else {
|
|
// Make instance of file parser, 64 bit template
|
|
CELF<ELF64STRUCTURES> 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<MAC32STRUCTURES> mac;
|
|
MemberBuffer >> mac; // Transfer MemberBuffer to coff object
|
|
mac.PublicNames(&StringBuffer, &StringEntries, 0);
|
|
break;
|
|
}
|
|
else {
|
|
// Make instance of file parser, 64 bit template
|
|
CMACHO<MAC64STRUCTURES> 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<SUNIXLibraryHeader>(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<SUNIXLibraryHeader>(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<ELF32STRUCTURES> 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<ELF64STRUCTURES> 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<MAC32STRUCTURES> 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<MAC64STRUCTURES> 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("!<arch>\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 "!<arch>\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<uint32> 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<uint32>(3) = DictionaryOffset2;
|
|
|
|
// Hash table size
|
|
OutFile.Get<uint16>(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<SStringEntry> &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<SOMFHashBlock>(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<uint8>(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 "?";
|
|
}
|