/**************************** cmdline.cpp ********************************** * Author: Agner Fog * Date created: 2006-07-25 * Last modified: 2018-01-29 * Project: objconv * Module: cmdline.cpp * Description: * This module is for interpretation of command line options * Also contains symbol change function * * Copyright 2006-2018 GNU General Public License http://www.gnu.org/licenses *****************************************************************************/ #include "stdafx.h" // List of recognized output file type options static SIntTxt TypeOptionNames[] = { {CMDL_OUTPUT_ELF, "elf"}, {CMDL_OUTPUT_PE, "pe"}, {CMDL_OUTPUT_PE, "coff"}, {CMDL_OUTPUT_PE, "cof"}, {CMDL_OUTPUT_PE, "win"}, {CMDL_OUTPUT_OMF, "omf"}, {CMDL_OUTPUT_MACHO, "mac"}, {CMDL_OUTPUT_MACHO, "macho"}, {CMDL_OUTPUT_MACHO, "mach-o"}, {CMDL_OUTPUT_MACHO, "mach"}, {CMDL_OUTPUT_MASM, "asm"}, {CMDL_OUTPUT_MASM, "masm"}, {CMDL_OUTPUT_MASM, "tasm"}, {CMDL_OUTPUT_MASM, "nasm"}, {CMDL_OUTPUT_MASM, "yasm"}, {CMDL_OUTPUT_MASM, "gasm"}, {CMDL_OUTPUT_MASM, "gas"} }; // List of subtype names static SIntTxt SubtypeNames[] = { {SUBTYPE_MASM, "asm"}, {SUBTYPE_MASM, "masm"}, {SUBTYPE_MASM, "tasm"}, {SUBTYPE_YASM, "nasm"}, {SUBTYPE_YASM, "yasm"}, {SUBTYPE_GASM, "gasm"}, {SUBTYPE_GASM, "gas"} }; // List of standard names that are always translated const uint32 MaxType = FILETYPE_MACHO_LE; // Standard names in 32-bit mode const char * StandardNames32[][MaxType+1] = { // 0, COFF, OMF, ELF, MACHO {0,"___ImageBase","___ImageBase","__executable_start","__mh_execute_header"} }; // Standard names in 64-bit mode // COFF removes an underscore in 32-bit. There is no 64-bit OMF const char * StandardNames64[][MaxType+1] = { // 0, COFF, OMF, ELF, MACHO {0,"__ImageBase", "", "__executable_start","__mh_execute_header"} }; const int NumStandardNames = sizeof(StandardNames32) / sizeof(StandardNames32[0]); // Command line interpreter CCommandLineInterpreter cmd; // Instantiate command line interpreter CCommandLineInterpreter::CCommandLineInterpreter() { // Default constructor memset(this, 0, sizeof(*this)); // Set all to zero Verbose = CMDL_VERBOSE_YES; // How much diagnostics to print on screen DumpOptions = DUMP_NONE; // Dump options DebugInfo = CMDL_DEBUG_DEFAULT; // Strip or convert debug info ExeptionInfo = CMDL_EXCEPTION_DEFAULT; // Strip or preserve exception handling info SegmentDot = CMDL_SECTIONDOT_NOCHANGE; // Change underscore/dot in beginning of segment names Underscore = CMDL_UNDERSCORE_NOCHANGE; // Add/remove underscores in symbol names LibraryOptions = CMDL_LIBRARY_DEFAULT; // Library options } CCommandLineInterpreter::~CCommandLineInterpreter() { // Destructor } void CCommandLineInterpreter::ReadCommandLine(int argc, char * argv[]) { // Read command line for (int i = 1; i < argc; i++) { ReadCommandItem(argv[i]); } if (ShowHelp || (InputFile == 0 && OutputFile == 0) /* || !OutputType */) { // No useful command found. Print help Help(); ShowHelp = 1; return; } // Check file options FileOptions = CMDL_FILE_INPUT; if (LibraryOptions == CMDL_LIBRARY_ADDMEMBER) { // Adding object files to library. Library may not exist FileOptions = CMDL_FILE_IN_IF_EXISTS; } if (DumpOptions || ((LibraryOptions & CMDL_LIBRARY_EXTRACTMEM) && !(LibraryOptions & CMDL_LIBRARY_ADDMEMBER))) { // Dumping or extracting. Output file not used if (OutputFile) err.submit(1103); // Output file name ignored OutputFile = 0; } else { // Output file required FileOptions |= CMDL_FILE_OUTPUT; } if ((LibraryOptions & CMDL_LIBRARY_ADDMEMBER) && !(LibraryOptions & CMDL_LIBRARY_CONVERT)) { // Adding library members only. Output file may have same name as input file FileOptions |= CMDL_FILE_IN_OUT_SAME; } // Check output type if (!OutputType) { // Output type not defined yet if (LibraryOptions & (CMDL_LIBRARY_CONVERT | CMDL_LIBRARY_ADDMEMBER)) { OutputType = FILETYPE_LIBRARY; } } } void CCommandLineInterpreter::ReadCommandItem(char * string) { // Read one option from command line // Skip leading whitespace while (*string != 0 && *string <= ' ') string++; if (*string == 0) return; // Empty string // Look for option prefix and response file prefix const char OptionPrefix1 = '-'; // Option must begin with '-' #if defined (_WIN32) || defined (__WINDOWS__) const char OptionPrefix2 = '/'; // '/' allowed instead of '-' in Windows only #else const char OptionPrefix2 = '-'; #endif const char ResponseFilePrefix = '@'; // Response file name prefixed by '@' if (*string == OptionPrefix1 || *string == OptionPrefix2) { // Option prefix found. This is a command line option InterpretCommandOption(string+1); } else if (*string == ResponseFilePrefix) { // Response file prefix found. Read more options from response file ReadCommandFile(string+1); } else { // No prefix found. This is an input or output file name InterpretFileName(string); } int loc, last = SymbolList.GetNumEntries() - 1; if (last > 0) { // relocate last entry (binary insertion sort): SSymbolChange * List = (SSymbolChange *)SymbolList.Buf(); SSymbolChange key = List[last]; SymbolBinSearch(key.Name1, last, &loc); memmove(List + loc + 1, List + loc, (last - loc) * sizeof(SSymbolChange)); List[loc] = key; } } void CCommandLineInterpreter::ReadCommandFile(char * filename) { // Read commands from file if (*filename <= ' ') { err.submit(1001); return; // Warning: empty filename } // Check if too many response file buffers (possibly because file includes itself) if (++NumBuffers > MAX_COMMAND_FILES) {err.submit(2107); return;} // Allocate buffer for response files. if (ResponseFiles.GetNumEntries() == 0) { ResponseFiles.SetNum(MAX_COMMAND_FILES); ResponseFiles.SetZero(); } // Read response file into new buffer ResponseFiles[NumBuffers-1].FileName = filename; ResponseFiles[NumBuffers-1].Read(); // Get buffer with file contents char * buffer = ResponseFiles[NumBuffers-1].Buf(); char * ItemBegin, * ItemEnd; // Mark begin and end of token in buffer // Check if buffer is allocated if (buffer) { // Parse contents of response file for tokens while (*buffer) { // Skip whitespace while (*buffer != 0 && uint8(*buffer) <= uint8(' ')) buffer++; if (*buffer == 0) break; // End of buffer found ItemBegin = buffer; // Find end of token ItemEnd = buffer+1; while (uint8(*ItemEnd) > uint8(' ')) ItemEnd++; if (*ItemEnd == 0) { buffer = ItemEnd; } else { buffer = ItemEnd + 1; *ItemEnd = 0; // Mark end of token } // Found token. // Check if it is a comment beginning with '#' or '//' if (ItemBegin[0] == '#' || (ItemBegin[0] == '/' && ItemBegin[1] == '/' )) { // This is a comment. Skip to end of line ItemEnd = buffer; while (*ItemEnd != 0 && *ItemEnd != '\n') { ItemEnd++; } if (*ItemEnd == 0) { buffer = ItemEnd; } else { buffer = ItemEnd + 1; } continue; } // Not a comment. Interpret token ReadCommandItem(ItemBegin); } } } void CCommandLineInterpreter::InterpretFileName(char * string) { // Interpret input or output filename from command line switch (libmode) { case 1: // First filename after -lib = inputfile and outputfile InputFile = string; libmode = 2; return; case 2: // Second or later filename after -lib = object file to add to library AddObjectToLibrary(string, string); return; } // libmode = 0: Ordinary input or output file if (!InputFile) { // Input file not specified yet InputFile = string; } else if (!OutputFile) { // Output file not specified yet OutputFile = string; } else { // Both input and output files already specified err.submit(2001); } } void CCommandLineInterpreter::InterpretCommandOption(char * string) { // Interpret one option from command line if (*string <= ' ') { err.submit(1001); return; // Warning: empty option } // Detect option type switch(string[0]) { case 'f': case 'F': // output file format if (string[1] == 'd') { // -fd == deprecated dump option InterpretDumpOption(string+2); break; } InterpretOutputTypeOption(string+1); break; case 'v': case 'V': // verbose/silent InterpretVerboseOption(string+1); break; case 'd': case 'D': // dump option InterpretDumpOption(string+1); break; // Debug info option //InterpretDebugInfoOption(string+1); break; case 'x': case 'X': // Exception handler info option InterpretExceptionInfoOption(string+1); break; case 'h': case 'H': case '?': // Help ShowHelp = 1; break; case 'e': case 'E': // Error option case 'w': case 'W': // Warning option InterpretErrorOption(string); break; case 'n': case 'N': // Symbol name change option case 'a': case 'A': // Symbol name alias option InterpretSymbolNameChangeOption(string); break; case 'i': case 'I': // Imagebase if ((string[1] | 0x20) == 'm') { InterpretImagebaseOption(string); } break; case 'l': case 'L': // Library option InterpretLibraryOption(string); break; case 'c': // Count instruction codes supported // This is an easter egg: You can only get it if you know it's there if (strncmp(string,"countinstructions", 17) == 0) { CDisassembler::CountInstructions(); exit(0); } default: // Unknown option err.submit(1002, string); } } void CCommandLineInterpreter::InterpretLibraryOption(char * string) { // Interpret options for manipulating library/archive files // Check for -lib command if (stricmp(string, "lib") == 0) { // Found -lib command if (InputFile) { libmode = 2; // Input file already specified. Remaining file names are object files to add } else { libmode = 1; // The rest of the command line must be interpreted as library name and object file names } return; } SSymbolChange sym = {0,0,0,0}; // Symbol change record int i; // Loop counter // Check for member name and optional new name in this command char * name1 = 0, * name2 = 0, separator; if ((string[2] == ':' || string[2] == '|') && string[3]) { // name1 found separator = string[2]; name1 = string+3; // Search for second separator or end name2 = name1 + 1; while (name2[0] != 0) { if (name2[0] == separator) { *name2 = 0; // Mark end of name1 if (name2[1]) { // name2 found name2++; // Name2 starts here break; } } name2++; } if (name2 == 0 || name2[0] == 0 || name2[0] == separator) { // name 2 is blank //name2 = name1; name2 = 0; } else { // Check if name2 ends with separator for (i = 0; i < (int)strlen(name2); i++) { if (name2[i] == separator) name2[i] = 0; } } } // Check for duplicate name if (SymbolIsInList(name1)) { // This symbol is already in list err.submit(2017, name1); return; } sym.Name1 = name1; // Store names in symbol change record sym.Name2 = name2; switch (string[1]) { case 'a': case 'A': // Add input file to library if (name1) { AddObjectToLibrary(name1, name2); } else err.submit(2004, string); break; case 'x': case 'X': // Extract member(s) from library if (name1) { // Extract specified member cmd.LibraryOptions = CMDL_LIBRARY_EXTRACTMEM; sym.Action = SYMA_EXTRACT_MEMBER; SymbolList.Push(&sym, sizeof(sym)); } else { // Extract all members cmd.LibraryOptions = CMDL_LIBRARY_EXTRACTALL; } break; case 'd': case 'D': // Delete member from library if (name1) { // Delete specified member cmd.LibraryOptions = CMDL_LIBRARY_CONVERT; sym.Action = SYMA_DELETE_MEMBER; SymbolList.Push(&sym, sizeof(sym)); } else err.submit(2004, string); break; case 's': case 'S': // Use short member names for compatibility cmd.LibrarySubtype = LIBTYPE_SHORTNAMES; break; default: err.submit(2004, string); // Unknown option } } void CCommandLineInterpreter::AddObjectToLibrary(char * filename, char * membername) { // Add object file to library if (!filename || !*filename) { err.submit(2004, filename-1); return; // Empty string } if (!membername || !*membername) membername = filename; SSymbolChange Sym = {0,0,0,0}; // Symbol change record Sym.Name2 = filename; // Object file name if (!MemberNamesAllocated) { // Allocate space for truncated member names const int SafetySpace = 1024; // Get size of response files if (ResponseFiles.GetNumEntries()) { MemberNamesAllocated = ResponseFiles[0].GetDataSize() + ResponseFiles[1].GetDataSize(); } // Allocate this size + SafetySpace MemberNames.SetSize(MemberNamesAllocated + SafetySpace); // Remember allocated buffer size MemberNamesAllocated = MemberNames.GetBufferSize(); } // Truncate name and store it in MemberNames //uint32 Name1Offset = MemberNames.PushString(CLibrary::TruncateMemberName(membername)); uint32 Name1Offset = MemberNames.PushString(membername); Sym.Name1 = (char*)(MemberNames.Buf() + Name1Offset); CLibrary::StripMemberName(Sym.Name1); // Note: Sym.Name1 points to allocated memory in violation of good programming practice. // Check that it is not reallocated: if (MemberNames.GetBufferSize() != MemberNamesAllocated) { err.submit(2506); // Cannot reallocate MemberNames because we have pointers to in in SymbolList return; } // Check for duplicate name if (SymbolIsInList(Sym.Name1)) { // This symbol is already in list err.submit(2017, Sym.Name1); return; } // Store options cmd.LibraryOptions |= CMDL_LIBRARY_ADDMEMBER; Sym.Action = SYMA_ADD_MEMBER; // Store SYMA_ADD_MEMBER record in symbol list SymbolList.Push(&Sym, sizeof(Sym)); } void CCommandLineInterpreter::InterpretOutputTypeOption(char * string) { // Interpret output file format option from command line int opt; for (opt = 0; opt < TableSize(TypeOptionNames); opt++) { int len = (int)strlen(TypeOptionNames[opt].b); if (strncmp(string, TypeOptionNames[opt].b, len) == 0) { // Match found if (OutputType) err.submit(2003, string); // More than one output type specified if (DumpOptions) err.submit(2007); // Both dump and convert specified // Save desired output type OutputType = TypeOptionNames[opt].a; // Check if name is followed by a word size int wordsize = 0; if (string[len]) wordsize = atoi(string+len); switch (wordsize) { case 0: // No word size specified break; case 32: case 64: // Valid word size DesiredWordSize = wordsize; break; default: // Illegal word size err.submit(2002, wordsize); } break; // Finished searching } } // Check if found if (opt >= TableSize(TypeOptionNames)) err.submit(2004, string-1); if (OutputType == CMDL_OUTPUT_MASM) { // Get subtype for (opt = 0; opt < TableSize(SubtypeNames); opt++) { int len = (int)strlen(SubtypeNames[opt].b); if (strncmp(string, SubtypeNames[opt].b, len) == 0) { // Match found SubType = SubtypeNames[opt].a; break; } } } } void CCommandLineInterpreter::InterpretVerboseOption(char * string) { // Interpret silent/verbose option from command line Verbose = atoi(string); } void CCommandLineInterpreter::InterpretDumpOption(char * string) { // Interpret dump option from command line if (OutputType || DumpOptions) err.submit(2007); // Both dump and convert specified char * s1 = string; while (*s1) { switch (*(s1++)) { case 'f': case 'F': // dump file header DumpOptions |= DUMP_FILEHDR; break; case 'h': case 'H': // dump section headers DumpOptions |= DUMP_SECTHDR; break; case 's': case 'S': // dump symbol table DumpOptions |= DUMP_SYMTAB; break; case 'r': case 'R': // dump relocations DumpOptions |= DUMP_RELTAB; break; case 'n': case 'N': // dump string table DumpOptions |= DUMP_STRINGTB; break; case 'c': case 'C': // dump comment records (currently only for OMF) DumpOptions |= DUMP_COMMENT; break; default: err.submit(2004, string-1); // Unknown option } } if (DumpOptions == 0) DumpOptions = DUMP_FILEHDR; OutputType = CMDL_OUTPUT_DUMP; if (OutputType && OutputType != CMDL_OUTPUT_DUMP) err.submit(2007); // Both dump and convert specified OutputType = CMDL_OUTPUT_DUMP; } void CCommandLineInterpreter::InterpretDebugInfoOption(char * string) { // Interpret debug info option from command line if (strlen(string) > 1) err.submit(2004, string-1); // Unknown option switch (*string) { case 's': case 'S': case 'r': case 'R': // Strip (remove) DebugInfo = CMDL_DEBUG_STRIP; break; case 'p': case 'P': // Preserve DebugInfo = CMDL_DEBUG_PRESERVE; break; case 'l': case 'L': // (Not supported) DebugInfo = CMDL_DEBUG_LINNUM; break; case 'c': case 'C': // (Not supported) DebugInfo = CMDL_DEBUG_SYMBOLS; break; default: err.submit(2004, string-1); // Unknown option } } void CCommandLineInterpreter::InterpretExceptionInfoOption(char * string) { // Interpret exception handler info option from command line if (strlen(string) > 1) err.submit(2004, string-1); // Unknown option switch (*string) { case 's': case 'S': case 'r': case 'R': // Strip (remove) ExeptionInfo = CMDL_EXCEPTION_STRIP; break; case 'p': case 'P': // Preserve ExeptionInfo = CMDL_EXCEPTION_PRESERVE; break; default: err.submit(2004, string-1); // Unknown option } } void CCommandLineInterpreter::InterpretErrorOption(char * string) { // Interpret warning/error option from command line if (strlen(string) < 3) { err.submit(2004, string); return; // Unknown option } int newstatus; // New status for this error number switch (string[1]) { case 'd': case 'D': // Disable newstatus = 0; break; case 'w': case 'W': // Treat as warning newstatus = 1; break; case 'e': case 'E': // Treat as error newstatus = 2; break; default: err.submit(2004, string); // Unknown option return; } if (string[2] == 'x' || string[2] == 'X') { // Apply new status to all non-fatal messages for (SErrorText * ep = ErrorTexts; ep->Status < 9; ep++) { ep->Status = newstatus; // Change status of all errors } } else { int ErrNum = atoi(string+2); if (ErrNum == 0 && string[2] != '0') { err.submit(2004, string); return; // Unknown option } // Search for this error number SErrorText * ep = err.FindError(ErrNum); if (ep->Status & 0x100) { // Error number not found err.submit(1003, ErrNum); return; // Unknown error number } // Change status of this error ep->Status = newstatus; } } void CCommandLineInterpreter::InterpretSymbolNameChangeOption(char * string) { // Interpret various options for changing symbol names SSymbolChange sym = {0,0,0,0}; // Symbol change record string[0] |= 0x20; // change first letter to lower case // Check for symbol names in this command char * name1 = 0, * name2 = 0; if (string[2] == ':' && string[3]) { // name1 found name1 = string+3; // Search for second ':' or end name2 = name1 + 1; while (name2[0] != 0) { if (name2[0] == ':') { *name2 = 0; // Mark end of name1 if (name2[1]) { // name2 found name2++; // Name2 starts here break; } } name2++; } if (name2 && name2[0]) { // name2 found. check if it ends with ':' for (uint32 i = 0; i < (uint32)strlen(name2); i++) { if (name2[i] == ':') name2[i] = 0; } } if (name2[0] == 0) name2 = 0; } // Check for duplicate name if (name1 && SymbolIsInList(name1)) { // This symbol is already in list err.submit(2015, name1); return; } switch (string[1]) { case 'u': case 'U': // underscore option switch (string[2]) { case 0: Underscore = CMDL_UNDERSCORE_CHANGE; if (string[0] == 'a') Underscore |= CMDL_KEEP_ALIAS; break; case '+': case 'a': case 'A': Underscore = CMDL_UNDERSCORE_ADD; if (string[0] == 'a') Underscore |= CMDL_KEEP_ALIAS; break; case '-': case 'r': case 'R': Underscore = CMDL_UNDERSCORE_REMOVE; if (string[0] == 'a') Underscore |= CMDL_KEEP_ALIAS; break; default: err.submit(2004, string); // Unknown option } break; case 'd': case 'D': // section name dot option SegmentDot = CMDL_SECTIONDOT_CHANGE; break; case 'r': case 'R': // name replace option if (name1 == 0 || name2 == 0 || *name1 == 0 || *name2 == 0) { err.submit(2008, string); return; } sym.Name1 = name1; sym.Name2 = name2; sym.Action = SYMA_CHANGE_NAME; if (string[0] == 'a') sym.Action |= SYMA_ALIAS; SymbolList.Push(&sym, sizeof(sym)); SymbolChangeEntries++; break; case 'p': case 'P': // prefix replace option if (name1 == 0 || *name1 == 0) { err.submit(2008, string); return; } if (name2 == 0) name2 = (char*)""; sym.Name1 = name1; sym.Name2 = name2; sym.Action = SYMA_CHANGE_PREFIX; if (string[0] == 'a') sym.Action |= SYMA_ALIAS; PrefixSuffixList.Push(&sym, sizeof(sym)); SymbolChangeEntries++; break; case 's': case 'S': // suffix replace option if (name1 == 0 || *name1 == 0) { err.submit(2008, string); return; } if (name2 == 0) name2 = (char*)""; sym.Name1 = name1; sym.Name2 = name2; sym.Action = SYMA_CHANGE_SUFFIX; if (string[0] == 'a') sym.Action |= SYMA_ALIAS; PrefixSuffixList.Push(&sym, sizeof(sym)); SymbolChangeEntries++; break; case 'w': case 'W': // Weaken symbol if (name1 == 0 || *name1 == 0 || name2) { err.submit(2009, string); return; } sym.Name1 = name1; sym.Action = SYMA_MAKE_WEAK; SymbolList.Push(&sym, sizeof(sym)); SymbolChangeEntries++; break; case 'l': case 'L': // Make symbol local or hidden if (name1 == 0 || *name1 == 0 || name2) { err.submit(2009, string); return; } sym.Name1 = name1; sym.Action = SYMA_MAKE_LOCAL; SymbolList.Push(&sym, sizeof(sym)); SymbolChangeEntries++; break; default: err.submit(2004, string); // Unknown option } } void CCommandLineInterpreter::InterpretImagebaseOption(char * string) { // Interpret image base option char * p = strchr(string, '='); if ((strnicmp(string, "imagebase", 9) && strnicmp(string, "image_base", 10)) || !p) { // Unknown option err.submit(1002, string); return; } if (ImageBase) err.submit(2330); // Imagebase specified more than once p++; // point to number following '=' // Loop through string to interpret hexadecimal number while (*p) { char letter = *p | 0x20; // lower case letter if (*p >= '0' && *p <= '9') { // 0 - 9 hexadecimal digit ImageBase = (ImageBase << 4) + *p - '0'; } else if (letter >= 'a' && letter <= 'f') { // A - F hexadecimal digit ImageBase = (ImageBase << 4) + letter - 'a' + 10; } else if (letter == 'h') { // Hexadecimal number may end with 'H' break; } else if (letter == 'x' || letter == ' ') { // Hexadecimal number may begin with 0x if (ImageBase) { // 'x' preceded by number other than 0 err.submit(1002, string); break; } } else { // Any other character not allowed err.submit(1002, string); break; } // next character p++; } if (ImageBase & 0xFFF) { // Must be divisible by page size err.submit(2331, string); } if ((int32)ImageBase <= 0) { // Cannot be zero or > 2^31 err.submit(2332, string); } } SSymbolChange const * CCommandLineInterpreter::GetMemberToAdd() { // Get names of object files to add to library // replaced will be set to 1 if a member with the same name is replaced // Search through SymbolList, continuing from last CurrentSymbol while (CurrentSymbol < SymbolList.GetDataSize()) { // Get pointer to current symbol record SSymbolChange * Sym = (SSymbolChange *)(SymbolList.Buf() + CurrentSymbol); // Increment pointer CurrentSymbol += sizeof(SSymbolChange); // Check record type if (Sym->Action == SYMA_ADD_MEMBER) { // Name found return Sym; } } // No more names found return 0; } void CCommandLineInterpreter::CheckExtractSuccess() { // Check if library members to extract were found // Search through SymbolList for extract records for (uint32 i = 0; i < SymbolList.GetDataSize(); i += sizeof(SSymbolChange)) { SSymbolChange * Sym = (SSymbolChange *)(SymbolList.Buf() + i); if (Sym->Action == SYMA_EXTRACT_MEMBER && Sym->Done == 0) { // Member has not been extracted err.submit(1104, Sym->Name1); } } } void CCommandLineInterpreter::CheckSymbolModifySuccess() { // Check if symbols to modify were found // Search through SymbolList for symbol change records for (uint32 i = 0; i < SymbolList.GetDataSize(); i += sizeof(SSymbolChange)) { SSymbolChange * Sym = (SSymbolChange *)(SymbolList.Buf() + i); if (Sym->Action >= SYMA_MAKE_WEAK && Sym->Action < SYMA_ADD_MEMBER && Sym->Done == 0) { // Member has not been extracted err.submit(1106, Sym->Name1); } if (Sym->Action == SYMA_DELETE_MEMBER && Sym->Done == 0) { // Member has not been extracted err.submit(1105, Sym->Name1); } } } int CCommandLineInterpreter::SymbolIsInList(char const * name) { // Check if name is already in symbol list int unused; return SymbolBinSearch(name, SymbolList.GetNumEntries(), &unused); } int CCommandLineInterpreter::SymbolBinSearch(char const * name, int nsym, int * location) { SSymbolChange * List = (SSymbolChange *)SymbolList.Buf(); int lo = 0, hi = nsym - 1; while (lo <= hi) { int mid = (lo + hi) >> 1; int comp = strcmp(name, List[mid].Name1); if (!comp) { *location = mid; return 1; } else if (comp < 0) { hi = mid - 1; } else { lo = mid + 1; } } *location = lo; return 0; } int CCommandLineInterpreter::SymbolChange(char const * oldname, char const ** newname, int symtype) { // Check if symbol has to be changed int action = 0, i, isym; int nsym = SymbolList.GetNumEntries(); int n_prefix_suffix = PrefixSuffixList.GetNumEntries(); if (oldname == 0) return SYMA_NOCHANGE; if (newname) *newname = 0; // Convert standard names if type conversion if (cmd.InputType != cmd.OutputType && uint32(cmd.InputType) <= MaxType && uint32(cmd.OutputType) <= MaxType) { if (DesiredWordSize == 32) { // Look for standard names to translate, 32-bit for (i = 0; i < NumStandardNames; i++) { if (strcmp(oldname, StandardNames32[i][cmd.InputType]) == 0) { // Match found *newname = StandardNames32[i][cmd.OutputType]; CountSymbolNameChanges++; return SYMA_CHANGE_NAME; // Change name of symbol } } } else { // Look for standard names to translate, 64-bit for (i = 0; i < NumStandardNames; i++) { if (strcmp(oldname, StandardNames64[i][cmd.InputType]) == 0) { // Match found *newname = StandardNames64[i][cmd.OutputType]; CountSymbolNameChanges++; return SYMA_CHANGE_NAME; // Change name of symbol } } } } // See if there are other conversions to do if (Underscore == 0 && SegmentDot == 0 && nsym == 0 && n_prefix_suffix == 0) return SYMA_NOCHANGE; // Nothing to do if (oldname == 0 || *oldname == 0) return SYMA_NOCHANGE; // No name static char NameBuffer[MAXSYMBOLLENGTH]; // search for name in list of names specified by user on command line SSymbolChange * psym; int found = SymbolBinSearch(oldname, nsym, &isym); if (found) { psym = (SSymbolChange *)SymbolList.Buf() + isym; } else { // Search prefix/suffix match psym = (SSymbolChange *)PrefixSuffixList.Buf(); for (isym = 0; isym < n_prefix_suffix; isym++, psym++) { int n1len = (int)strlen(psym->Name1); // Length of string to search for int onlen = (int)strlen(oldname); // Length of string to match if ((psym->Action&~SYMA_ALIAS) == SYMA_CHANGE_PREFIX && strncmp(oldname, psym->Name1, n1len)==0) break; // matching prefix found if ((psym->Action&~SYMA_ALIAS) == SYMA_CHANGE_SUFFIX && strcmp(oldname+onlen-n1len, psym->Name1)==0) break; // matching suffix found } found = isym < n_prefix_suffix; } if (found) { // A matching name was found. action = psym->Action; // Whatever action is specified here is overriding any general option // Statistics counting switch (action & ~SYMA_ALIAS) { case SYMA_MAKE_WEAK: // Make public symbol weak if (symtype == SYMT_PUBLIC) { CountSymbolsWeakened++; psym->Done++; } else { // only public symbols can be weakened err.submit(1020, oldname); // cannot make weak action = SYMA_NOCHANGE; } break; case SYMA_MAKE_LOCAL: // Hide public or external symbol if (symtype == SYMT_PUBLIC || symtype == SYMT_EXTERNAL) { CountSymbolsMadeLocal++; psym->Done++; if (symtype == SYMT_EXTERNAL) err.submit(1023, oldname); } else { // only public and external symbols can be made local err.submit(1021, oldname); // cannot make local action = SYMA_NOCHANGE; } break; case SYMA_CHANGE_NAME: // Change name of symbol or segment or library member CountSymbolNameChanges++; psym->Done++; *newname = psym->Name2; break; case SYMA_CHANGE_PREFIX: // Change beginning of symbol name if (symtype == SYMT_PUBLIC || symtype == SYMT_EXTERNAL || symtype == SYMT_LOCAL || symtype == SYMT_SECTION) { if (strlen(oldname) - strlen(psym->Name1) + strlen(psym->Name2) >= MAXSYMBOLLENGTH) { err.submit(2202, oldname); // Name too long action = SYMA_NOCHANGE; break; } strcpy(NameBuffer, psym->Name2); strcpy(NameBuffer + strlen(psym->Name2), oldname + strlen(psym->Name1)); action = SYMA_CHANGE_NAME; *newname = NameBuffer; CountSymbolNameChanges++; psym->Done++; } else { // only symbols and segments can change prefix err.submit(1024, oldname); action = SYMA_NOCHANGE; } break; case SYMA_CHANGE_SUFFIX: // Change end of symbol name if (symtype == SYMT_PUBLIC || symtype == SYMT_EXTERNAL || symtype == SYMT_LOCAL || symtype == SYMT_SECTION) { if (strlen(oldname) - strlen(psym->Name1) + strlen(psym->Name2) >= MAXSYMBOLLENGTH) { err.submit(2202, oldname); // Name too long action = SYMA_NOCHANGE; break; } strcpy(NameBuffer, oldname); strcpy(NameBuffer + strlen(oldname) - strlen(psym->Name1), psym->Name2); action = SYMA_CHANGE_NAME; *newname = NameBuffer; CountSymbolNameChanges++; psym->Done++; } else { // only symbols and segments can change prefix err.submit(1024, oldname); action = SYMA_NOCHANGE; } break; case SYMA_EXTRACT_MEMBER: *newname = psym->Name2; // continue in next case case SYMA_DELETE_MEMBER: case SYMA_ADD_MEMBER: if (symtype == SYMT_LIBRARYMEMBER) { // Change to library member psym->Done++; } else { // Ignore action for symbols that have the same name as a library member action = SYMA_NOCHANGE; } } if (action && (psym->Action & SYMA_ALIAS)) { // Keep old name as alias if (symtype == SYMT_PUBLIC) { CountSymbolNameAliases++; psym->Done++; action = SYMA_ALIAS; } else { // only public symbols can have aliases CountSymbolNameChanges--; err.submit(1022, oldname); // cannot make alias action = SYMA_NOCHANGE; } } // Action to take return action; } // Not found in list. Check for section options if (symtype == SYMT_SECTION) { if (!strncmp(oldname, ".rela", 5)) { // ELF relocation section must have same name change as mother section const char * name2; int action2 = SymbolChange(oldname+5, &name2, symtype); if (action2 == SYMA_CHANGE_NAME && strlen(name2) + 6 < MAXSYMBOLLENGTH) { sprintf(NameBuffer, ".rela%s", name2); *newname = NameBuffer; return action2; } } if (!strncmp(oldname, ".rel", 4)) { // ELF relocation section must have same name change as mother section const char * name2; int action2 = SymbolChange(oldname+4, &name2, symtype); if (action2 == SYMA_CHANGE_NAME && strlen(name2) + 5 < MAXSYMBOLLENGTH) { sprintf(NameBuffer, ".rel%s", name2); *newname = NameBuffer; return action2; } } if (SegmentDot) { // Change section name if (SegmentDot == CMDL_SECTIONDOT_U2DOT && oldname[0] == '_') { // replace '_' by '.' strncpy(NameBuffer, oldname, MAXSYMBOLLENGTH-1); NameBuffer[MAXSYMBOLLENGTH-1] = 0; // Terminate string NameBuffer[0] = '.'; *newname = NameBuffer; CountSectionDotConversions++; return SYMA_CHANGE_NAME; } if (SegmentDot == CMDL_SECTIONDOT_DOT2U && oldname[0] == '.') { // replace '.' by '_' // Note: Microsoft and Intel compilers have . on standard names // and _ on nonstandard names in COFF files // Borland requires _ on all segment names in OMF files /* // Standard section names that should not be changed static char const * StandardSectionNames[] = { ".text", ".data", ".bss", ".comment", ".lib" }; for (uint32 i = 0; i < sizeof(StandardSectionNames)/sizeof(StandardSectionNames[0]); i++) { if (stricmp(oldname,StandardSectionNames[i]) == 0) { // Standard name. Don't change return SYMA_NOCHANGE; } }*/ strncpy(NameBuffer, oldname, MAXSYMBOLLENGTH-1); NameBuffer[MAXSYMBOLLENGTH-1] = 0; // Terminate string NameBuffer[0] = '_'; *newname = NameBuffer; CountSectionDotConversions++; return SYMA_CHANGE_NAME; } } } // Check for underscore options if ((Underscore & 0x0F) == CMDL_UNDERSCORE_REMOVE && oldname[0] == '_') { // Remove underscore if ((Underscore & CMDL_KEEP_ALIAS) && symtype == SYMT_PUBLIC) { // Alias only applicable to public symbols // Make alias without underscore *newname = oldname + 1; CountUnderscoreConversions++; CountSymbolNameAliases++; return SYMA_ALIAS; } // Change name applicable to public and external symbols if (symtype == SYMT_PUBLIC || symtype == SYMT_EXTERNAL) { // Make new name without underscore *newname = oldname + 1; CountUnderscoreConversions++; return SYMA_CHANGE_NAME; } } if ((Underscore & 0x0F) == CMDL_UNDERSCORE_ADD) { // Add underscore even if it already has one if ((Underscore & CMDL_KEEP_ALIAS) && symtype == SYMT_PUBLIC) { // Alias only applicable to public symbols // Make alias with underscore strncpy(NameBuffer+1, oldname, MAXSYMBOLLENGTH-2); NameBuffer[MAXSYMBOLLENGTH-1] = 0; // Terminate string NameBuffer[0] = '_'; *newname = NameBuffer; CountUnderscoreConversions++; CountSymbolNameAliases++; return SYMA_ALIAS; } // Change name applicable to public and external symbols if (symtype == SYMT_PUBLIC || symtype == SYMT_EXTERNAL) { // Make new name with underscore strncpy(NameBuffer+1, oldname, MAXSYMBOLLENGTH-2); NameBuffer[MAXSYMBOLLENGTH-1] = 0; // Terminate string NameBuffer[0] = '_'; *newname = NameBuffer; CountUnderscoreConversions++; return SYMA_CHANGE_NAME; } } return SYMA_NOCHANGE; } int CCommandLineInterpreter::SymbolChangesRequested() { // Any kind of symbol change requested on command line return (Underscore != 0) | (SegmentDot != 0) << 1 | (SymbolChangeEntries != 0) << 2; } void CCommandLineInterpreter::CountDebugRemoved() { // Count debug sections removed CountDebugSectionsRemoved++; } void CCommandLineInterpreter::CountExceptionRemoved() { // Count exception handler sections removed CountExceptionSectionsRemoved++; } void CCommandLineInterpreter::CountSymbolsHidden() { // Count unused external references hidden CountUnusedSymbolsHidden++; } void CCommandLineInterpreter::ReportStatistics() { // Report statistics about name changes etc. if (DebugInfo == CMDL_DEBUG_STRIP || ExeptionInfo == CMDL_EXCEPTION_STRIP || Underscore || SegmentDot || SymbolList.GetNumEntries()) { printf ("\n"); } if (DebugInfo == CMDL_DEBUG_STRIP) { printf ("\n%3i Debug sections removed", CountDebugSectionsRemoved); } if (ExeptionInfo == CMDL_EXCEPTION_STRIP) { printf ("\n%3i Exception sections removed", CountExceptionSectionsRemoved); } if ((DebugInfo == CMDL_DEBUG_STRIP || ExeptionInfo == CMDL_EXCEPTION_STRIP) && CountUnusedSymbolsHidden) { printf ("\n%3i Unused external symbol references hidden", CountUnusedSymbolsHidden); } if (Underscore || SegmentDot || SymbolList.GetNumEntries()) { if (CountUnderscoreConversions || Underscore) { printf ("\n%3i Changes in leading underscores on symbol names", CountUnderscoreConversions); } if (CountSectionDotConversions || SegmentDot) { printf ("\n%3i Changes in leading characters on section names", CountSectionDotConversions); } if (CountSymbolNameChanges) { printf ("\n%3i Symbol names changed", CountSymbolNameChanges); } if (CountSymbolNameAliases) { printf ("\n%3i Public symbol names aliased", CountSymbolNameAliases); } if (CountSymbolsWeakened) { printf ("\n%3i Public symbol names made weak", CountSymbolsWeakened); } if (CountSymbolsMadeLocal) { printf ("\n%3i Public or external symbol names made local", CountSymbolsMadeLocal); } if (SymbolChangeEntries && !CountSymbolNameChanges && !CountSymbolNameAliases && !CountSymbolsWeakened && !CountSymbolsMadeLocal) { printf ("\n No symbols to change were found"); } } } void CCommandLineInterpreter::Help() { // Print help message printf("\nObject file converter version %.2f for x86 and x86-64 platforms.", OBJCONV_VERSION); printf("\nCopyright (c) 2018 by Agner Fog. Gnu General Public License."); printf("\n\nUsage: objconv options inputfile [outputfile]"); printf("\n\nOptions:"); printf("\n-fXXX[SS] Output file format XXX, word size SS. Supported formats:"); printf("\n PE, COFF, ELF, OMF, MACHO\n"); printf("\n-fasm Disassemble file (-fmasm, -fnasm, -fyasm, -fgasm)\n"); printf("\n-dXXX Dump file contents to console."); printf("\n Values of XXX (can be combined):"); printf("\n f: File header, h: section Headers, s: Symbol table,"); printf("\n r: Relocation table, n: string table.\n"); printf("\n-nu change symbol Name Underscores to the default for the target format."); printf("\n-nu- remove Underscores from symbol Names."); printf("\n-nu+ add Underscores to symbol Names."); printf("\n-nd replace Dot/underscore in section names."); printf("\n-nr:N1:N2 Replace symbol Name N1 with N2."); printf("\n-np:N1:N2 Replace symbol Prefix N1 with N2."); printf("\n-ns:N1:N2 Replace symbol Suffix N1 with N2."); printf("\n-ar:N1:N2 make Alias N2 for existing public name N1."); printf("\n-ap:N1:N2 Replace symbol Prefix and keep old name as alias."); printf("\n-as:N1:N2 Replace symbol Suffix and keep old name as alias."); printf("\n-nw:N1 make public symbol Name N1 Weak (ELF and MAC64 only)."); printf("\n-nl:N1 make public symbol Name N1 Local (invisible).\n"); //printf("\n-ds Strip Debug info."); // default if input and output are different formats //printf("\n-dp Preserve Debug info, even if it is incompatible."); printf("\n-xs Strip exception handling info and other incompatible info."); // default if input and output are different formats. Hides unused symbols printf("\n-xp Preserve exception handling info and other incompatible info.\n"); printf("\n-lx eXtract all members from Library."); printf("\n-lx:N1:N2 eXtract member N1 from Library to file N2."); printf("\n-ld:N1 Delete member N1 from Library."); printf("\n-la:N1:N2 Add object file N1 to Library as member N2."); printf("\n Alternative: -lib LIBRARYNAME OBJECTFILENAMES.\n"); printf("\n-vN Verbose options. Values of N:"); printf("\n 0: Silent, 1: Print file names and types, 2: Tell about conversions."); printf("\n-wdNNN Disable Warning NNN."); printf("\n-weNNN treat Warning NNN as Error. -wex: treat all warnings as errors."); printf("\n-edNNN Disable Error number NNN."); printf("\n-ewNNN treat Error number NNN as Warning.\n"); printf("\n-h Print this help screen.\n"); printf("\n@RFILE Read additional options from response file RFILE.\n"); printf("\n\nExample:"); printf("\nobjconv -felf32 -nu filename.obj filename.o\n\n"); }