1 /****************************  cmdline.cpp  **********************************
2 * Author:        Agner Fog
3 * Date created:  2006-07-25
4 * Last modified: 2020-02-26
5 * Project:       objconv
6 * Module:        cmdline.cpp
7 * Description:
8 * This module is for interpretation of command line options
9 * Also contains symbol change function
10 *
11 * Copyright 2006-2020 GNU General Public License http://www.gnu.org/licenses
12 *****************************************************************************/
13 
14 #include "stdafx.h"
15 
16 // List of recognized output file type options
17 static SIntTxt TypeOptionNames[] = {
18     {CMDL_OUTPUT_ELF,   "elf"},
19     {CMDL_OUTPUT_PE,    "pe"},
20     {CMDL_OUTPUT_PE,    "coff"},
21     {CMDL_OUTPUT_PE,    "cof"},
22     {CMDL_OUTPUT_PE,    "win"},
23     {CMDL_OUTPUT_OMF,   "omf"},
24     {CMDL_OUTPUT_MACHO, "mac"},
25     {CMDL_OUTPUT_MACHO, "macho"},
26     {CMDL_OUTPUT_MACHO, "mach-o"},
27     {CMDL_OUTPUT_MACHO, "mach"},
28     {CMDL_OUTPUT_MASM,  "asm"},
29     {CMDL_OUTPUT_MASM,  "masm"},
30     {CMDL_OUTPUT_MASM,  "tasm"},
31     {CMDL_OUTPUT_MASM,  "nasm"},
32     {CMDL_OUTPUT_MASM,  "yasm"},
33     {CMDL_OUTPUT_MASM,  "gasm"},
34     {CMDL_OUTPUT_MASM,  "gas"}
35 };
36 
37 // List of subtype names
38 static SIntTxt SubtypeNames[] = {
39     {SUBTYPE_MASM,  "asm"},
40     {SUBTYPE_MASM,  "masm"},
41     {SUBTYPE_MASM,  "tasm"},
42     {SUBTYPE_NASM,  "nasm"},
43     {SUBTYPE_NASM,  "yasm"},
44     {SUBTYPE_GASM,  "gasm"},
45     {SUBTYPE_GASM,  "gas"}
46 };
47 
48 // List of standard names that are always translated
49 const uint32_t MaxType = FILETYPE_MACHO_LE;
50 
51 // Standard names in 32-bit mode
52 const char * StandardNames32[][MaxType+1] = {
53     //  0,    COFF,          OMF,           ELF,                MACHO
54     {0,"___ImageBase","___ImageBase","__executable_start","__mh_execute_header"}
55 };
56 
57 // Standard names in 64-bit mode
58 // COFF removes an underscore in 32-bit. There is no 64-bit OMF
59 const char * StandardNames64[][MaxType+1] = {
60     //  0,    COFF,       OMF,         ELF,                MACHO
61     {0,"__ImageBase",  "",    "__executable_start","__mh_execute_header"}
62 };
63 
64 const int NumStandardNames = sizeof(StandardNames32) / sizeof(StandardNames32[0]);
65 
66 
67 // Command line interpreter
68 CCommandLineInterpreter cmd;                  // Instantiate command line interpreter
69 
CCommandLineInterpreter()70 CCommandLineInterpreter::CCommandLineInterpreter() {
71     // Default constructor
72     memset(this, 0, sizeof(*this));            // Set all to zero
73     Verbose        = CMDL_VERBOSE_YES;         // How much diagnostics to print on screen
74     DumpOptions    = DUMP_NONE;                // Dump options
75     DebugInfo      = CMDL_DEBUG_DEFAULT;       // Strip or convert debug info
76     ExeptionInfo   = CMDL_EXCEPTION_DEFAULT;   // Strip or preserve exception handling info
77     SegmentDot     = CMDL_SECTIONDOT_NOCHANGE; // Change underscore/dot in beginning of segment names
78     Underscore     = CMDL_UNDERSCORE_NOCHANGE; // Add/remove underscores in symbol names
79     LibraryOptions = CMDL_LIBRARY_DEFAULT;     // Library options
80 }
81 
82 
~CCommandLineInterpreter()83 CCommandLineInterpreter::~CCommandLineInterpreter() { // Destructor
84 }
85 
86 
ReadCommandLine(int argc,char * argv[])87 void CCommandLineInterpreter::ReadCommandLine(int argc, char * argv[]) {
88 
89     // Read command line
90     for (int i = 1; i < argc; i++) {
91         ReadCommandItem(argv[i]);
92     }
93     if (ShowHelp || (InputFile == 0 && OutputFile == 0) /* || !OutputType */) {
94         // No useful command found. Print help
95         Help();  ShowHelp = 1;
96         return;
97     }
98     // Check file options
99     FileOptions = CMDL_FILE_INPUT;
100     if (LibraryOptions == CMDL_LIBRARY_ADDMEMBER) {
101         // Adding object files to library. Library may not exist
102         FileOptions = CMDL_FILE_IN_IF_EXISTS;
103     }
104     if (DumpOptions || ((LibraryOptions & CMDL_LIBRARY_EXTRACTMEM) && !(LibraryOptions & CMDL_LIBRARY_ADDMEMBER))) {
105         // Dumping or extracting. Output file not used
106         if (OutputFile) err.submit(1103); // Output file name ignored
107         OutputFile = 0;
108     }
109     else {
110         // Output file required
111         FileOptions |= CMDL_FILE_OUTPUT;
112     }
113     if ((LibraryOptions & CMDL_LIBRARY_ADDMEMBER) && !(LibraryOptions & CMDL_LIBRARY_CONVERT)) {
114         // Adding library members only. Output file may have same name as input file
115         FileOptions |= CMDL_FILE_IN_OUT_SAME;
116     }
117     // Check output type
118     if (!OutputType) {
119         // Output type not defined yet
120         if (LibraryOptions & (CMDL_LIBRARY_CONVERT | CMDL_LIBRARY_ADDMEMBER)) {
121             OutputType = FILETYPE_LIBRARY;
122         }
123     }
124 }
125 
126 
ReadCommandItem(char * string)127 void CCommandLineInterpreter::ReadCommandItem(char * string) {
128     // Read one option from command line
129     // Skip leading whitespace
130     while (*string != 0 && *string <= ' ') string++;
131     if (*string == 0) return;  // Empty string
132 
133     // Look for option prefix and response file prefix
134     const char OptionPrefix1 = '-';  // Option must begin with '-'
135 #if defined (_WIN32) || defined (__WINDOWS__)
136     const char OptionPrefix2 = '/';  // '/' allowed instead of '-' in Windows only
137 #else
138     const char OptionPrefix2 = '-';
139 #endif
140     const char ResponseFilePrefix = '@';  // Response file name prefixed by '@'
141     if (*string == OptionPrefix1 || *string == OptionPrefix2) {
142         // Option prefix found. This is a command line option
143         InterpretCommandOption(string+1);
144     }
145     else if (*string == ResponseFilePrefix) {
146         // Response file prefix found. Read more options from response file
147         ReadCommandFile(string+1);
148     }
149     else {
150         // No prefix found. This is an input or output file name
151         InterpretFileName(string);
152     }
153 }
154 
155 
ReadCommandFile(char * filename)156 void CCommandLineInterpreter::ReadCommandFile(char * filename) {
157     // Read commands from file
158     if (*filename <= ' ') {
159         err.submit(1001); return;    // Warning: empty filename
160     }
161 
162     // Check if too many response file buffers (possibly because file includes itself)
163     if (++NumBuffers > MAX_COMMAND_FILES) {err.submit(2107); return;}
164 
165     // Allocate buffer for response files.
166     if (ResponseFiles.GetNumEntries() == 0) {
167         ResponseFiles.SetNum(MAX_COMMAND_FILES);
168         ResponseFiles.SetZero();
169     }
170 
171     // Read response file into new buffer
172     ResponseFiles[NumBuffers-1].FileName = filename;
173     ResponseFiles[NumBuffers-1].Read();
174 
175     // Get buffer with file contents
176     char * buffer = (char*)ResponseFiles[NumBuffers-1].Buf();
177     char * ItemBegin, * ItemEnd;  // Mark begin and end of token in buffer
178 
179     // Check if buffer is allocated
180     if (buffer) {
181 
182         // Parse contents of response file for tokens
183         while (*buffer) {
184 
185             // Skip whitespace
186             while (*buffer != 0 && uint8_t(*buffer) <= uint8_t(' ')) buffer++;
187             if (*buffer == 0) break; // End of buffer found
188             ItemBegin = buffer;
189 
190             // Find end of token
191             ItemEnd = buffer+1;
192             while (uint8_t(*ItemEnd) > uint8_t(' ')) ItemEnd++;
193             if (*ItemEnd == 0) {
194                 buffer = ItemEnd;
195             }
196             else {
197                 buffer = ItemEnd + 1;
198                 *ItemEnd = 0;    // Mark end of token
199             }
200             // Found token.
201             // Check if it is a comment beginning with '#' or '//'
202             if (ItemBegin[0] == '#' || (ItemBegin[0] == '/' && ItemBegin[1] == '/' )) {
203                 // This is a comment. Skip to end of line
204                 ItemEnd = buffer;
205                 while (*ItemEnd != 0 && *ItemEnd != '\n') {
206                     ItemEnd++;
207                 }
208                 if (*ItemEnd == 0) {
209                     buffer = ItemEnd;
210                 }
211                 else {
212                     buffer = ItemEnd + 1;
213                 }
214                 continue;
215             }
216             // Not a comment. Interpret token
217             ReadCommandItem(ItemBegin);
218         }
219     }
220 }
221 
222 
InterpretFileName(char * string)223 void CCommandLineInterpreter::InterpretFileName(char * string) {
224     // Interpret input or output filename from command line
225 
226     switch (libmode) {
227     case 1:            // First filename after -lib = inputfile and outputfile
228         InputFile = string;
229         libmode = 2;
230         return;
231 
232     case 2:            // Second or later filename after -lib = object file to add to library
233         AddObjectToLibrary(string, string);
234         return;
235     }
236     // libmode = 0: Ordinary input or output file
237 
238     if (!InputFile) {
239         // Input file not specified yet
240         InputFile = string;
241     }
242     else if (!OutputFile) {
243         // Output file not specified yet
244         OutputFile = string;
245     }
246     else {
247         // Both input and output files already specified
248         err.submit(2001);
249     }
250 }
251 
252 
InterpretCommandOption(char * string)253 void CCommandLineInterpreter::InterpretCommandOption(char * string) {
254     // Interpret one option from command line
255     if (*string <= ' ') {
256         err.submit(1001); return;    // Warning: empty option
257     }
258 
259     // Detect option type
260     switch(string[0]) {
261     case 'f': case 'F':   // output file format
262         if (string[1] == 'd') {
263             // -fd == deprecated dump option
264             InterpretDumpOption(string+2);  break;
265         }
266         InterpretOutputTypeOption(string+1);  break;
267 
268     case 'v': case 'V':   // verbose/silent
269         InterpretVerboseOption(string+1);  break;
270 
271     case 'd': case 'D':   // dump option
272         InterpretDumpOption(string+1);  break;
273         // Debug info option
274         //InterpretDebugInfoOption(string+1);  break;
275 
276     case 'x': case 'X':   // Exception handler info option
277         InterpretExceptionInfoOption(string+1);  break;
278 
279     case 'h': case 'H': case '?':  // Help
280         ShowHelp = 1;  break;
281 
282     case 'e': case 'E':   // Error option
283     case 'w': case 'W':   // Warning option
284         InterpretErrorOption(string);  break;
285 
286     case 'n': case 'N':   // Symbol name change option
287     case 'a': case 'A':   // Symbol name alias option
288         InterpretSymbolNameChangeOption(string);  break;
289 
290     case 'i': case 'I':   // Imagebase
291         if ((string[1] | 0x20) == 'm') {
292             InterpretImagebaseOption(string);
293         }
294         break;
295 
296     case 'l': case 'L':   // Library option
297         InterpretLibraryOption(string);  break;
298 
299     case 'c':  // Count instruction codes supported
300         // This is an easter egg: You can only get it if you know it's there
301         if (strncmp(string,"countinstructions", 17) == 0) {
302             CDisassembler::CountInstructions();
303             exit(0);
304         }
305 
306     default:    // Unknown option
307         err.submit(1002, string);
308     }
309 }
310 
311 
InterpretLibraryOption(char * string)312 void CCommandLineInterpreter::InterpretLibraryOption(char * string) {
313     // Interpret options for manipulating library/archive files
314 
315     // Check for -lib command
316     if (stricmp(string, "lib") == 0) {  // Found -lib command
317         if (InputFile) {
318             libmode = 2;                  // Input file already specified. Remaining file names are object files to add
319         }
320         else {
321             libmode = 1;                  // The rest of the command line must be interpreted as library name and object file names
322         }
323         return;
324     }
325 
326     SSymbolChange sym = {0,0,0,0};      // Symbol change record
327     int i;                              // Loop counter
328 
329     // Check for member name and optional new name in this command
330     char * name1 = 0, * name2 = 0, separator;
331     if ((string[2] == ':' || string[2] == '|') && string[3]) {
332         // name1 found
333         separator = string[2];
334         name1 = string+3;
335         // Search for second separator or end
336         name2 = name1 + 1;
337         while (name2[0] != 0) {
338             if (name2[0] == separator) {
339                 *name2 = 0;  // Mark end of name1
340                 if (name2[1]) {
341                     // name2 found
342                     name2++;     // Name2 starts here
343                     break;
344                 }
345             }
346             name2++;
347         }
348         if (name2 == 0 || name2[0] == 0 || name2[0] == separator) {
349             // name 2 is blank
350             //name2 = name1;
351             name2 = 0;
352         }
353         else {
354             // Check if name2 ends with separator
355             for (i = 0; i < (int)strlen(name2); i++) {
356                 if (name2[i] == separator) name2[i] = 0;
357             }
358         }
359     }
360     // Check for duplicate name
361     if (SymbolIsInList(name1)) {
362         // This symbol is already in list
363         err.submit(2017, name1);
364         return;
365     }
366 
367     sym.Name1 = name1;     // Store names in symbol change record
368     sym.Name2 = name2;
369 
370     switch (string[1]) {
371     case 'a': case 'A':      // Add input file to library
372         if (name1) {
373             AddObjectToLibrary(name1, name2);
374         }
375         else err.submit(2004, string);
376         break;
377 
378     case 'x': case 'X':      // Extract member(s) from library
379         if (name1) {
380             // Extract specified member
381             cmd.LibraryOptions = CMDL_LIBRARY_EXTRACTMEM;
382             sym.Action  = SYMA_EXTRACT_MEMBER;
383             SymbolList.Push(&sym, sizeof(sym));
384         }
385         else {
386             // Extract all members
387             cmd.LibraryOptions = CMDL_LIBRARY_EXTRACTALL;
388         }
389         break;
390 
391     case 'd': case 'D':  // Delete member from library
392         if (name1) {
393             // Delete specified member
394             cmd.LibraryOptions = CMDL_LIBRARY_CONVERT;
395             sym.Action  = SYMA_DELETE_MEMBER;
396             SymbolList.Push(&sym, sizeof(sym));
397         }
398         else err.submit(2004, string);
399         break;
400 
401     case 's': case 'S':  // Use short member names for compatibility
402         cmd.LibrarySubtype = LIBTYPE_SHORTNAMES;
403         break;
404 
405     default:
406         err.submit(2004, string);  // Unknown option
407     }
408 }
409 
410 
AddObjectToLibrary(char * filename,char * membername)411 void CCommandLineInterpreter::AddObjectToLibrary(char * filename, char * membername) {
412     // Add object file to library
413     if (!filename || !*filename) {
414         err.submit(2004, filename-1);  return;     // Empty string
415     }
416 
417     if (!membername || !*membername) membername = filename;
418 
419     SSymbolChange Sym = {0,0,0,0};                // Symbol change record
420 
421     Sym.Name2 = filename;                         // Object file name
422 
423     if (!MemberNamesAllocated) {
424         // Allocate space for truncated member names
425         const int SafetySpace = 1024;
426 
427         // Get size of response files
428         if (ResponseFiles.GetNumEntries()) {
429             MemberNamesAllocated = ResponseFiles[0].GetDataSize() + ResponseFiles[1].GetDataSize();
430         }
431         // Allocate this size + SafetySpace
432         MemberNames.SetSize(MemberNamesAllocated + SafetySpace);
433 
434         // Remember allocated buffer size
435         MemberNamesAllocated = MemberNames.GetBufferSize();
436     }
437 
438     // Truncate name and store it in MemberNames
439     //uint32_t Name1Offset = MemberNames.PushString(CLibrary::TruncateMemberName(membername));
440     uint32_t Name1Offset = MemberNames.PushString(membername);
441     Sym.Name1 = (char*)(MemberNames.Buf() + Name1Offset);
442     CLibrary::StripMemberName(Sym.Name1);
443 
444     // Note: Sym.Name1 points to allocated memory in violation of good programming practice.
445     // Check that it is not reallocated:
446     if (MemberNames.GetBufferSize() != MemberNamesAllocated) {
447         err.submit(2506); // Cannot reallocate MemberNames because we have pointers to in in SymbolList
448         return;
449     }
450 
451     // Check for duplicate name
452     if (SymbolIsInList(Sym.Name1)) {
453         // This symbol is already in list
454         err.submit(2017, Sym.Name1);
455         return;
456     }
457 
458     // Store options
459     cmd.LibraryOptions |= CMDL_LIBRARY_ADDMEMBER;
460     Sym.Action  = SYMA_ADD_MEMBER;
461 
462     // Store SYMA_ADD_MEMBER record in symbol list
463     SymbolList.Push(&Sym, sizeof(Sym));
464 }
465 
466 
InterpretOutputTypeOption(char * string)467 void CCommandLineInterpreter::InterpretOutputTypeOption(char * string) {
468     // Interpret output file format option from command line
469 
470     int opt;
471     for (opt = 0; opt < TableSize(TypeOptionNames); opt++) {
472         int len = (int)strlen(TypeOptionNames[opt].b);
473         if (strncmp(string, TypeOptionNames[opt].b, len) == 0) {
474             // Match found
475             if (OutputType)  err.submit(2003, string);  // More than one output type specified
476             if (DumpOptions) err.submit(2007);          // Both dump and convert specified
477 
478             // Save desired output type
479             OutputType = TypeOptionNames[opt].a;
480 
481             // Check if name is followed by a word size
482             int wordsize = 0;
483             if (string[len]) wordsize = atoi(string+len);
484             switch (wordsize) {
485             case 0:  // No word size specified
486                 break;
487 
488             case 32: case 64:  // Valid word size
489                 DesiredWordSize = wordsize;
490                 break;
491 
492             default:  // Illegal word size
493                 err.submit(2002, wordsize);
494             }
495             break;   // Finished searching
496         }
497     }
498 
499     // Check if found
500     if (opt >= TableSize(TypeOptionNames)) err.submit(2004, string-1);
501 
502     if (OutputType == CMDL_OUTPUT_MASM) {
503         // Get subtype
504         for (opt = 0; opt < TableSize(SubtypeNames); opt++) {
505             int len = (int)strlen(SubtypeNames[opt].b);
506             if (strncmp(string, SubtypeNames[opt].b, len) == 0) {
507                 // Match found
508                 SubType = SubtypeNames[opt].a;  break;
509             }
510         }
511     }
512 }
513 
514 
InterpretVerboseOption(char * string)515 void CCommandLineInterpreter::InterpretVerboseOption(char * string) {
516     // Interpret silent/verbose option from command line
517     Verbose = atoi(string);
518 }
519 
520 
InterpretDumpOption(char * string)521 void CCommandLineInterpreter::InterpretDumpOption(char * string) {
522     // Interpret dump option from command line
523     if (OutputType || DumpOptions) err.submit(2007);          // Both dump and convert specified
524 
525     char * s1 = string;
526     while (*s1) {
527         switch (*(s1++)) {
528         case 'f': case 'F':  // dump file header
529             DumpOptions |= DUMP_FILEHDR;  break;
530         case 'h': case 'H':  // dump section headers
531             DumpOptions |= DUMP_SECTHDR;  break;
532         case 's': case 'S':  // dump symbol table
533             DumpOptions |= DUMP_SYMTAB;  break;
534         case 'r': case 'R':  // dump relocations
535             DumpOptions |= DUMP_RELTAB;  break;
536         case 'n': case 'N':  // dump string table
537             DumpOptions |= DUMP_STRINGTB;  break;
538         case 'c': case 'C':  // dump comment records (currently only for OMF)
539             DumpOptions |= DUMP_COMMENT;  break;
540         default:
541             err.submit(2004, string-1);  // Unknown option
542         }
543     }
544     if (DumpOptions == 0) DumpOptions = DUMP_FILEHDR;
545     OutputType = CMDL_OUTPUT_DUMP;
546     if (OutputType && OutputType != CMDL_OUTPUT_DUMP) err.submit(2007); // Both dump and convert specified
547     OutputType = CMDL_OUTPUT_DUMP;
548 }
549 
550 
InterpretDebugInfoOption(char * string)551 void CCommandLineInterpreter::InterpretDebugInfoOption(char * string) {
552     // Interpret debug info option from command line
553     if (strlen(string) > 1) err.submit(2004, string-1);  // Unknown option
554     switch (*string) {
555     case 's': case 'S': case 'r': case 'R':  // Strip (remove)
556         DebugInfo = CMDL_DEBUG_STRIP;  break;
557     case 'p': case 'P':                      // Preserve
558         DebugInfo = CMDL_DEBUG_PRESERVE;  break;
559     case 'l': case 'L':                      // (Not supported)
560         DebugInfo = CMDL_DEBUG_LINNUM;  break;
561     case 'c': case 'C':                      // (Not supported)
562         DebugInfo = CMDL_DEBUG_SYMBOLS;  break;
563     default:
564         err.submit(2004, string-1);  // Unknown option
565     }
566 }
567 
568 
InterpretExceptionInfoOption(char * string)569 void CCommandLineInterpreter::InterpretExceptionInfoOption(char * string) {
570     // Interpret exception handler info option from command line
571     if (strlen(string) > 1) err.submit(2004, string-1);  // Unknown option
572     switch (*string) {
573     case 's': case 'S': case 'r': case 'R':  // Strip (remove)
574         ExeptionInfo = CMDL_EXCEPTION_STRIP;  break;
575     case 'p': case 'P':                      // Preserve
576         ExeptionInfo = CMDL_EXCEPTION_PRESERVE;  break;
577     default:
578         err.submit(2004, string-1);  // Unknown option
579     }
580 }
581 
582 
InterpretErrorOption(char * string)583 void CCommandLineInterpreter::InterpretErrorOption(char * string) {
584     // Interpret warning/error option from command line
585     if (strlen(string) < 3) {
586         err.submit(2004, string); return; // Unknown option
587     }
588     int newstatus;   // New status for this error number
589 
590     switch (string[1]) {
591     case 'd': case 'D':  // Disable
592         newstatus = 0;  break;
593 
594     case 'w': case 'W':  // Treat as warning
595         newstatus = 1;  break;
596 
597     case 'e': case 'E':  // Treat as error
598         newstatus = 2;  break;
599 
600     default:
601         err.submit(2004, string);  // Unknown option
602         return;
603     }
604     if (string[2] == 'x' || string[2] == 'X') {
605         // Apply new status to all non-fatal messages
606         for (SErrorText * ep = ErrorTexts; ep->Status < 9; ep++) {
607             ep->Status = newstatus;  // Change status of all errors
608         }
609     }
610     else {
611         int ErrNum = atoi(string+2);
612         if (ErrNum == 0 && string[2] != '0') {
613             err.submit(2004, string);  return; // Unknown option
614         }
615         // Search for this error number
616         SErrorText * ep = err.FindError(ErrNum);
617         if (ep->Status & 0x100) {
618             // Error number not found
619             err.submit(1003, ErrNum);  return; // Unknown error number
620         }
621         // Change status of this error
622         ep->Status = newstatus;
623     }
624 }
625 
InterpretSymbolNameChangeOption(char * string)626 void CCommandLineInterpreter::InterpretSymbolNameChangeOption(char * string) {
627     // Interpret various options for changing symbol names
628     SSymbolChange sym = {0,0,0,0};   // Symbol change record
629     string[0] |= 0x20;  // change first letter to lower case
630 
631     // Check for symbol names in this command
632     char * name1 = 0, * name2 = 0;
633     if (string[2] == ':' && string[3]) {
634         // name1 found
635         name1 = string+3;
636         // Search for second ':' or end
637         name2 = name1 + 1;
638         while (name2[0] != 0) {
639             if (name2[0] == ':') {
640                 *name2 = 0;  // Mark end of name1
641                 if (name2[1]) {
642                     // name2 found
643                     name2++;     // Name2 starts here
644                     break;
645                 }
646             }
647             name2++;
648         }
649         if (name2 && name2[0]) {
650             // name2 found. check if it ends with ':'
651             for (uint32_t i = 0; i < (uint32_t)strlen(name2); i++) {
652                 if (name2[i] == ':') name2[i] = 0;
653             }
654         }
655         if (name2[0] == 0) name2 = 0;
656     }
657     // Check for duplicate name
658     if (name1 && SymbolIsInList(name1)) {
659         // This symbol is already in list
660         err.submit(2015, name1);
661         return;
662     }
663 
664     switch (string[1]) {
665     case 'u': case 'U':  // underscore option
666         switch (string[2]) {
667         case 0:
668             Underscore = CMDL_UNDERSCORE_CHANGE;
669             if (string[0] == 'a') Underscore |= CMDL_KEEP_ALIAS;
670             break;
671         case '+': case 'a': case 'A':
672             Underscore = CMDL_UNDERSCORE_ADD;
673             if (string[0] == 'a') Underscore |= CMDL_KEEP_ALIAS;
674             break;
675         case '-': case 'r': case 'R':
676             Underscore = CMDL_UNDERSCORE_REMOVE;
677             if (string[0] == 'a') Underscore |= CMDL_KEEP_ALIAS;
678             break;
679         default:
680             err.submit(2004, string);  // Unknown option
681         }
682         break;
683 
684     case 'd': case 'D':  // section name dot option
685         SegmentDot = CMDL_SECTIONDOT_CHANGE;
686         break;
687 
688     case 'r': case 'R':  // name replace option
689         if (name1 == 0 || name2 == 0 || *name1 == 0 || *name2 == 0) {
690             err.submit(2008, string); return;
691         }
692         sym.Name1 = name1;
693         sym.Name2 = name2;
694         sym.Action  = SYMA_CHANGE_NAME;
695         if (string[0] == 'a') sym.Action |= SYMA_ALIAS;
696         SymbolList.Push(&sym, sizeof(sym));  SymbolChangeEntries++;
697         break;
698 
699     case 'p': case 'P':  // prefix replace option
700         if (name1 == 0 || *name1 == 0) {
701             err.submit(2008, string); return;
702         }
703         if (name2 == 0) name2 = (char*)"";
704         sym.Name1 = name1;
705         sym.Name2 = name2;
706         sym.Action  = SYMA_CHANGE_PREFIX;
707         if (string[0] == 'a') sym.Action |= SYMA_ALIAS;
708         SymbolList.Push(&sym, sizeof(sym));  SymbolChangeEntries++;
709         break;
710 
711     case 's': case 'S':  // suffix replace option
712         if (name1 == 0 || *name1 == 0) {
713             err.submit(2008, string); return;
714         }
715         if (name2 == 0) name2 = (char*)"";
716         sym.Name1 = name1;
717         sym.Name2 = name2;
718         sym.Action  = SYMA_CHANGE_SUFFIX;
719         if (string[0] == 'a') sym.Action |= SYMA_ALIAS;
720         SymbolList.Push(&sym, sizeof(sym));  SymbolChangeEntries++;
721         break;
722 
723     case 'w': case 'W':  // Weaken symbol
724         if (name1 == 0 || *name1 == 0 || name2) {
725             err.submit(2009, string); return;
726         }
727         sym.Name1 = name1;
728         sym.Action  = SYMA_MAKE_WEAK;
729         SymbolList.Push(&sym, sizeof(sym));  SymbolChangeEntries++;
730         break;
731 
732     case 'l': case 'L':  // Make symbol local or hidden
733         if (name1 == 0 || *name1 == 0 || name2) {
734             err.submit(2009, string); return;
735         }
736         sym.Name1 = name1;
737         sym.Action  = SYMA_MAKE_LOCAL;
738         SymbolList.Push(&sym, sizeof(sym));  SymbolChangeEntries++;
739         break;
740 
741     default:
742         err.submit(2004, string);  // Unknown option
743     }
744 }
745 
InterpretImagebaseOption(char * string)746 void CCommandLineInterpreter::InterpretImagebaseOption(char * string) {
747     // Interpret image base option
748     char * p = strchr(string, '=');
749     if ((strnicmp(string, "imagebase", 9) && strnicmp(string, "image_base", 10)) || !p) {
750         // Unknown option
751         err.submit(1002, string);
752         return;
753     }
754     if (ImageBase) err.submit(2330); // Imagebase specified more than once
755 
756     p++;  // point to number following '='
757     // Loop through string to interpret hexadecimal number
758     while (*p) {
759         char letter = *p | 0x20; // lower case letter
760         if (*p >= '0' && *p <= '9') {
761             // 0 - 9 hexadecimal digit
762             ImageBase = (ImageBase << 4) + *p - '0';
763         }
764         else if (letter >= 'a' && letter <= 'f') {
765             // A - F hexadecimal digit
766             ImageBase = (ImageBase << 4) + letter - 'a' + 10;
767         }
768         else if (letter == 'h') {
769             // Hexadecimal number may end with 'H'
770             break;
771         }
772         else if (letter == 'x' || letter == ' ') {
773             // Hexadecimal number may begin with 0x
774             if (ImageBase) {
775                 // 'x' preceded by number other than 0
776                 err.submit(1002, string); break;
777             }
778         }
779         else {
780             // Any other character not allowed
781             err.submit(1002, string); break;
782         }
783         // next character
784         p++;
785     }
786     if (ImageBase & 0xFFF) {
787         // Must be divisible by page size
788         err.submit(2331, string);
789     }
790     if ((int32_t)ImageBase <= 0) {
791         // Cannot be zero or > 2^31
792         err.submit(2332, string);
793     }
794 }
795 
796 
GetMemberToAdd()797 SSymbolChange const * CCommandLineInterpreter::GetMemberToAdd() {
798     // Get names of object files to add to library
799     // replaced will be set to 1 if a member with the same name is replaced
800 
801     // Search through SymbolList, continuing from last CurrentSymbol
802     while (CurrentSymbol < SymbolList.GetDataSize()) {
803         // Get pointer to current symbol record
804         SSymbolChange * Sym = (SSymbolChange *)(SymbolList.Buf() + CurrentSymbol);
805         // Increment pointer
806         CurrentSymbol += sizeof(SSymbolChange);
807         // Check record type
808         if (Sym->Action == SYMA_ADD_MEMBER) {
809             // Name found
810             return Sym;
811         }
812     }
813     // No more names found
814     return 0;
815 }
816 
817 
CheckExtractSuccess()818 void CCommandLineInterpreter::CheckExtractSuccess() {
819     // Check if library members to extract were found
820 
821     // Search through SymbolList for extract records
822     for (uint32_t i = 0; i < SymbolList.GetDataSize(); i += sizeof(SSymbolChange)) {
823         SSymbolChange * Sym = (SSymbolChange *)(SymbolList.Buf() + i);
824         if (Sym->Action == SYMA_EXTRACT_MEMBER && Sym->Done == 0) {
825             // Member has not been extracted
826             err.submit(1104, Sym->Name1);
827         }
828     }
829 }
830 
831 
CheckSymbolModifySuccess()832 void CCommandLineInterpreter::CheckSymbolModifySuccess() {
833     // Check if symbols to modify were found
834 
835     // Search through SymbolList for symbol change records
836     for (uint32_t i = 0; i < SymbolList.GetDataSize(); i += sizeof(SSymbolChange)) {
837         SSymbolChange * Sym = (SSymbolChange *)(SymbolList.Buf() + i);
838         if (Sym->Action >= SYMA_MAKE_WEAK && Sym->Action < SYMA_ADD_MEMBER && Sym->Done == 0) {
839             // Member has not been extracted
840             err.submit(1106, Sym->Name1);
841         }
842         if (Sym->Action == SYMA_DELETE_MEMBER && Sym->Done == 0) {
843             // Member has not been extracted
844             err.submit(1105, Sym->Name1);
845         }
846     }
847 }
848 
849 
SymbolIsInList(char const * name)850 int CCommandLineInterpreter::SymbolIsInList(char const * name) {
851     // Check if name is already in symbol list
852     int isym, nsym = SymbolList.GetNumEntries();
853     SSymbolChange * List = (SSymbolChange *)SymbolList.Buf(), * psym;
854 
855     // Search for name in list of names specified by user on command line
856     for (isym = 0, psym = List; isym < nsym; isym++, psym++) {
857         if (strcmp(name, psym->Name1) == 0) return 1;  // Matching name found
858     }
859     return 0;
860 }
861 
862 
SymbolChange(char const * oldname,char const ** newname,int symtype)863 int CCommandLineInterpreter::SymbolChange(char const * oldname, char const ** newname, int symtype) {
864     // Check if symbol has to be changed
865     int action = 0, i, isym;
866     int nsym = SymbolList.GetNumEntries();
867     if (oldname == 0) return SYMA_NOCHANGE;
868     if (newname) *newname = 0;
869 
870     // Convert standard names if type conversion
871     if (cmd.InputType != cmd.OutputType
872         && uint32_t(cmd.InputType) <= MaxType && uint32_t(cmd.OutputType) <= MaxType) {
873             if (DesiredWordSize == 32) {
874                 // Look for standard names to translate, 32-bit
875                 for (i = 0; i < NumStandardNames; i++) {
876                     if (strcmp(oldname, StandardNames32[i][cmd.InputType]) == 0) {
877                         // Match found
878                         *newname = StandardNames32[i][cmd.OutputType];
879                         CountSymbolNameChanges++;
880                         return SYMA_CHANGE_NAME; // Change name of symbol
881                     }
882                 }
883             }
884             else {
885                 // Look for standard names to translate, 64-bit
886                 for (i = 0; i < NumStandardNames; i++) {
887                     if (strcmp(oldname, StandardNames64[i][cmd.InputType]) == 0) {
888                         // Match found
889                         *newname = StandardNames64[i][cmd.OutputType];
890                         CountSymbolNameChanges++;
891                         return SYMA_CHANGE_NAME; // Change name of symbol
892                     }
893                 }
894             }
895     }
896 
897     // See if there are other conversions to do
898     if (Underscore == 0 && SegmentDot == 0 && nsym == 0) return SYMA_NOCHANGE;  // Nothing to do
899     if (oldname == 0 || *oldname == 0) return SYMA_NOCHANGE;                    // No name
900 
901     static char NameBuffer[MAXSYMBOLLENGTH];
902 
903     SSymbolChange * List = (SSymbolChange *)SymbolList.Buf(), * psym;
904     // search for name in list of names specified by user on command line
905     for (isym = 0, psym = List; isym < nsym; isym++, psym++) {
906         if (strcmp(oldname, psym->Name1) == 0) break;  // Matching name found
907         int n1len = (int)strlen(psym->Name1); // Length of string to search for
908         int onlen = (int)strlen(oldname);     // Length of string to match
909         if ((psym->Action&~SYMA_ALIAS) == SYMA_CHANGE_PREFIX && strncmp(oldname, psym->Name1, n1len)==0) break; // matching prefix found
910         if ((psym->Action&~SYMA_ALIAS) == SYMA_CHANGE_SUFFIX && strcmp(oldname+onlen-n1len, psym->Name1)==0) break; // matching suffix found
911     }
912     if (isym < nsym) {
913         // A matching name was found.
914         action = psym->Action;
915         // Whatever action is specified here is overriding any general option
916         // Statistics counting
917         switch (action & ~SYMA_ALIAS) {
918 
919         case SYMA_MAKE_WEAK: // Make public symbol weak
920             if (symtype == SYMT_PUBLIC) {
921                 CountSymbolsWeakened++;  psym->Done++;
922             }
923             else { // only public symbols can be weakened
924                 err.submit(1020, oldname); // cannot make weak
925                 action = SYMA_NOCHANGE;
926             }
927             break;
928 
929         case SYMA_MAKE_LOCAL: // Hide public or external symbol
930             if (symtype == SYMT_PUBLIC || symtype == SYMT_EXTERNAL) {
931                 CountSymbolsMadeLocal++;  psym->Done++;
932                 if (symtype == SYMT_EXTERNAL) err.submit(1023, oldname);
933             }
934             else { // only public and external symbols can be made local
935                 err.submit(1021, oldname); // cannot make local
936                 action = SYMA_NOCHANGE;
937             }
938             break;
939 
940         case SYMA_CHANGE_NAME: // Change name of symbol or segment or library member
941             CountSymbolNameChanges++;  psym->Done++;
942             *newname = psym->Name2;
943             break;
944 
945         case SYMA_CHANGE_PREFIX: // Change beginning of symbol name
946             if (symtype == SYMT_PUBLIC || symtype == SYMT_EXTERNAL || symtype == SYMT_LOCAL || symtype == SYMT_SECTION) {
947                 if (strlen(oldname) - strlen(psym->Name1) + strlen(psym->Name2) >= MAXSYMBOLLENGTH) {
948                     err.submit(2202, oldname);  // Name too long
949                     action = SYMA_NOCHANGE;  break;
950                 }
951                 strcpy(NameBuffer, psym->Name2);
952                 strcpy(NameBuffer + strlen(psym->Name2), oldname + strlen(psym->Name1));
953                 action = SYMA_CHANGE_NAME;
954                 *newname = NameBuffer;
955                 CountSymbolNameChanges++;  psym->Done++;
956             }
957             else { // only symbols and segments can change prefix
958                 err.submit(1024, oldname);
959                 action = SYMA_NOCHANGE;
960             }
961             break;
962 
963         case SYMA_CHANGE_SUFFIX: // Change end of symbol name
964             if (symtype == SYMT_PUBLIC || symtype == SYMT_EXTERNAL || symtype == SYMT_LOCAL || symtype == SYMT_SECTION) {
965                 if (strlen(oldname) - strlen(psym->Name1) + strlen(psym->Name2) >= MAXSYMBOLLENGTH) {
966                     err.submit(2202, oldname);  // Name too long
967                     action = SYMA_NOCHANGE;  break;
968                 }
969                 strcpy(NameBuffer, oldname);
970                 strcpy(NameBuffer + strlen(oldname) - strlen(psym->Name1), psym->Name2);
971                 action = SYMA_CHANGE_NAME;
972                 *newname = NameBuffer;
973                 CountSymbolNameChanges++;  psym->Done++;
974             }
975             else { // only symbols and segments can change prefix
976                 err.submit(1024, oldname);
977                 action = SYMA_NOCHANGE;
978             }
979             break;
980 
981         case SYMA_EXTRACT_MEMBER:
982             *newname = psym->Name2;
983             // continue in next case
984         case SYMA_DELETE_MEMBER: case SYMA_ADD_MEMBER:
985             if (symtype == SYMT_LIBRARYMEMBER) {
986                 // Change to library member
987                 psym->Done++;
988             }
989             else {
990                 // Ignore action for symbols that have the same name as a library member
991                 action = SYMA_NOCHANGE;
992             }
993         }
994 
995         if (action && (psym->Action & SYMA_ALIAS)) {
996             // Keep old name as alias
997             if (symtype == SYMT_PUBLIC) {
998                 CountSymbolNameAliases++;  psym->Done++;
999                 action = SYMA_ALIAS;
1000             }
1001             else { // only public symbols can have aliases
1002                 CountSymbolNameChanges--;
1003                 err.submit(1022, oldname); // cannot make alias
1004                 action = SYMA_NOCHANGE;
1005             }
1006         }
1007 
1008         // Action to take
1009         return action;
1010     }
1011 
1012     // Not found in list. Check for section options
1013     if (symtype == SYMT_SECTION) {
1014         if (!strncmp(oldname, ".rela", 5)) {
1015             // ELF relocation section must have same name change as mother section
1016             const char * name2;
1017             int action2 = SymbolChange(oldname+5, &name2, symtype);
1018             if (action2 == SYMA_CHANGE_NAME && strlen(name2) + 6 < MAXSYMBOLLENGTH) {
1019                 sprintf(NameBuffer, ".rela%s", name2);
1020                 *newname = NameBuffer;
1021                 return action2;
1022             }
1023         }
1024         if (!strncmp(oldname, ".rel", 4)) {
1025             // ELF relocation section must have same name change as mother section
1026             const char * name2;
1027             int action2 = SymbolChange(oldname+4, &name2, symtype);
1028             if (action2 == SYMA_CHANGE_NAME && strlen(name2) + 5 < MAXSYMBOLLENGTH) {
1029                 sprintf(NameBuffer, ".rel%s", name2);
1030                 *newname = NameBuffer;
1031                 return action2;
1032             }
1033         }
1034         if (SegmentDot) {
1035             // Change section name
1036 
1037             if (SegmentDot == CMDL_SECTIONDOT_U2DOT && oldname[0] == '_') {
1038                 // replace '_' by '.'
1039                 strncpy(NameBuffer, oldname, MAXSYMBOLLENGTH-1);
1040                 NameBuffer[MAXSYMBOLLENGTH-1] = 0;  // Terminate string
1041                 NameBuffer[0] = '.';
1042                 *newname = NameBuffer;
1043                 CountSectionDotConversions++;
1044                 return SYMA_CHANGE_NAME;
1045             }
1046             if (SegmentDot == CMDL_SECTIONDOT_DOT2U && oldname[0] == '.') {
1047                 // replace '.' by '_'
1048                 // Note: Microsoft and Intel compilers have . on standard names
1049                 // and _ on nonstandard names in COFF files
1050                 // Borland requires _ on all segment names in OMF files
1051                 /*
1052                 // Standard section names that should not be changed
1053                 static char const * StandardSectionNames[] = {
1054                 ".text", ".data", ".bss", ".comment", ".lib"
1055                 };
1056                 for (uint32_t i = 0; i < sizeof(StandardSectionNames)/sizeof(StandardSectionNames[0]); i++) {
1057                 if (stricmp(oldname,StandardSectionNames[i]) == 0) {
1058                 // Standard name. Don't change
1059                 return SYMA_NOCHANGE;
1060                 }
1061                 }*/
1062                 strncpy(NameBuffer, oldname, MAXSYMBOLLENGTH-1);
1063                 NameBuffer[MAXSYMBOLLENGTH-1] = 0;  // Terminate string
1064                 NameBuffer[0] = '_';
1065                 *newname = NameBuffer;
1066                 CountSectionDotConversions++;
1067                 return SYMA_CHANGE_NAME;
1068             }
1069         }
1070     }
1071 
1072     // Check for underscore options
1073     if ((Underscore & 0x0F) == CMDL_UNDERSCORE_REMOVE && oldname[0] == '_') {
1074         // Remove underscore
1075         if ((Underscore & CMDL_KEEP_ALIAS) && symtype == SYMT_PUBLIC) {
1076             // Alias only applicable to public symbols
1077             // Make alias without underscore
1078             *newname = oldname + 1;
1079             CountUnderscoreConversions++;  CountSymbolNameAliases++;
1080             return SYMA_ALIAS;
1081         }
1082         // Change name applicable to public and external symbols
1083         if (symtype == SYMT_PUBLIC || symtype == SYMT_EXTERNAL) {
1084             // Make new name without underscore
1085             *newname = oldname + 1;
1086             CountUnderscoreConversions++;
1087             return SYMA_CHANGE_NAME;
1088         }
1089     }
1090     if ((Underscore & 0x0F) == CMDL_UNDERSCORE_ADD) {
1091         // Add underscore even if it already has one
1092         if ((Underscore & CMDL_KEEP_ALIAS) && symtype == SYMT_PUBLIC) {
1093             // Alias only applicable to public symbols
1094             // Make alias with underscore
1095             strncpy(NameBuffer+1, oldname, MAXSYMBOLLENGTH-2);
1096             NameBuffer[MAXSYMBOLLENGTH-1] = 0;  // Terminate string
1097             NameBuffer[0] = '_';
1098             *newname = NameBuffer;
1099             CountUnderscoreConversions++;  CountSymbolNameAliases++;
1100             return SYMA_ALIAS;
1101         }
1102         // Change name applicable to public and external symbols
1103         if (symtype == SYMT_PUBLIC || symtype == SYMT_EXTERNAL) {
1104             // Make new name with underscore
1105             strncpy(NameBuffer+1, oldname, MAXSYMBOLLENGTH-2);
1106             NameBuffer[MAXSYMBOLLENGTH-1] = 0;  // Terminate string
1107             NameBuffer[0] = '_';
1108             *newname = NameBuffer;
1109             CountUnderscoreConversions++;
1110             return SYMA_CHANGE_NAME;
1111         }
1112     }
1113     return SYMA_NOCHANGE;
1114 }
1115 
1116 
SymbolChangesRequested()1117 int CCommandLineInterpreter::SymbolChangesRequested() {
1118     // Any kind of symbol change requested on command line
1119     return (Underscore != 0)
1120         | (SegmentDot != 0) << 1
1121         | (SymbolChangeEntries != 0) << 2;
1122 }
1123 
1124 
CountDebugRemoved()1125 void CCommandLineInterpreter::CountDebugRemoved() {
1126     // Count debug sections removed
1127     CountDebugSectionsRemoved++;
1128 }
1129 
1130 
CountExceptionRemoved()1131 void CCommandLineInterpreter::CountExceptionRemoved() {
1132     // Count exception handler sections removed
1133     CountExceptionSectionsRemoved++;
1134 }
1135 
1136 
CountSymbolsHidden()1137 void CCommandLineInterpreter::CountSymbolsHidden() {
1138     // Count unused external references hidden
1139     CountUnusedSymbolsHidden++;
1140 }
1141 
1142 
ReportStatistics()1143 void CCommandLineInterpreter::ReportStatistics() {
1144     // Report statistics about name changes etc.
1145     if (DebugInfo == CMDL_DEBUG_STRIP || ExeptionInfo == CMDL_EXCEPTION_STRIP
1146         || Underscore || SegmentDot || SymbolList.GetNumEntries()) {
1147             printf ("\n");
1148     }
1149     if (DebugInfo == CMDL_DEBUG_STRIP) {
1150         printf ("\n%3i Debug sections removed", CountDebugSectionsRemoved);
1151     }
1152     if (ExeptionInfo == CMDL_EXCEPTION_STRIP) {
1153         printf ("\n%3i Exception sections removed", CountExceptionSectionsRemoved);
1154     }
1155     if ((DebugInfo == CMDL_DEBUG_STRIP || ExeptionInfo == CMDL_EXCEPTION_STRIP)
1156         && CountUnusedSymbolsHidden) {
1157             printf ("\n%3i Unused external symbol references hidden", CountUnusedSymbolsHidden);
1158     }
1159 
1160     if (Underscore || SegmentDot || SymbolList.GetNumEntries()) {
1161         if (CountUnderscoreConversions || Underscore) {
1162             printf ("\n%3i Changes in leading underscores on symbol names", CountUnderscoreConversions);
1163         }
1164         if (CountSectionDotConversions || SegmentDot) {
1165             printf ("\n%3i Changes in leading characters on section names", CountSectionDotConversions);
1166         }
1167         if (CountSymbolNameChanges) {
1168             printf ("\n%3i Symbol names changed", CountSymbolNameChanges);
1169         }
1170         if (CountSymbolNameAliases) {
1171             printf ("\n%3i Public symbol names aliased", CountSymbolNameAliases);
1172         }
1173         if (CountSymbolsWeakened) {
1174             printf ("\n%3i Public symbol names made weak", CountSymbolsWeakened);
1175         }
1176         if (CountSymbolsMadeLocal) {
1177             printf ("\n%3i Public or external symbol names made local", CountSymbolsMadeLocal);
1178         }
1179         if (SymbolChangeEntries && !CountSymbolNameChanges && !CountSymbolNameAliases && !CountSymbolsWeakened && !CountSymbolsMadeLocal) {
1180             printf ("\n    No symbols to change were found");
1181         }
1182     }
1183 }
1184 
1185 
Help()1186 void CCommandLineInterpreter::Help() {
1187     // Print help message
1188     printf("\nObject file converter version %.2f for x86 and x86-64 platforms.", OBJCONV_VERSION);
1189     printf("\nCopyright (c) 2020 by Agner Fog. Gnu General Public License.");
1190     printf("\n\nUsage: objconv options inputfile [outputfile]");
1191     printf("\n\nOptions:");
1192     printf("\n-fXXX[SS]  Output file format XXX, word size SS. Supported formats:");
1193     printf("\n           PE, COFF, ELF, OMF, MACHO\n");
1194     printf("\n-fasm      Disassemble file (-fmasm, -fnasm, -fyasm, -fgasm)\n");
1195     printf("\n-dXXX      Dump file contents to console.");
1196     printf("\n           Values of XXX (can be combined):");
1197     printf("\n           f: File header, h: section Headers, s: Symbol table,");
1198     printf("\n           r: Relocation table, n: string table.\n");
1199 
1200     printf("\n-nu        change symbol Name Underscores to the default for the target format.");
1201     printf("\n-nu-       remove Underscores from symbol Names.");
1202     printf("\n-nu+       add Underscores to symbol Names.");
1203     printf("\n-nd        replace Dot/underscore in section names.");
1204     printf("\n-nr:N1:N2  Replace symbol Name N1 with N2.");
1205     printf("\n-np:N1:N2  Replace symbol Prefix N1 with N2.");
1206     printf("\n-ns:N1:N2  Replace symbol Suffix N1 with N2.");
1207     printf("\n-ar:N1:N2  make Alias N2 for existing public name N1.");
1208     printf("\n-ap:N1:N2  Replace symbol Prefix and keep old name as alias.");
1209     printf("\n-as:N1:N2  Replace symbol Suffix and keep old name as alias.");
1210     printf("\n-nw:N1     make public symbol Name N1 Weak (ELF and MAC64 only).");
1211     printf("\n-nl:N1     make public symbol Name N1 Local (invisible).\n");
1212     //printf("\n-ds        Strip Debug info.");    // default if input and output are different formats
1213     //printf("\n-dp        Preserve Debug info, even if it is incompatible.");
1214     printf("\n-xs        Strip exception handling info and other incompatible info.");  // default if input and output are different formats. Hides unused symbols
1215     printf("\n-xp        Preserve exception handling info and other incompatible info.\n");
1216 
1217     printf("\n-lx        eXtract all members from Library.");
1218     printf("\n-lx:N1:N2  eXtract member N1 from Library to file N2.");
1219     printf("\n-ld:N1     Delete member N1 from Library.");
1220     printf("\n-la:N1:N2  Add object file N1 to Library as member N2.");
1221     printf("\n           Alternative: -lib LIBRARYNAME OBJECTFILENAMES.\n");
1222 
1223     printf("\n-vN        Verbose options. Values of N:");
1224     printf("\n           0: Silent, 1: Print file names and types, 2: Tell about conversions.");
1225 
1226     printf("\n-wdNNN     Disable Warning NNN.");
1227     printf("\n-weNNN     treat Warning NNN as Error. -wex: treat all warnings as errors.");
1228     printf("\n-edNNN     Disable Error number NNN.");
1229     printf("\n-ewNNN     treat Error number NNN as Warning.\n");
1230 
1231     printf("\n-h         Print this help screen.\n");
1232 
1233     printf("\n@RFILE     Read additional options from response file RFILE.\n");
1234     printf("\n\nExample:");
1235     printf("\nobjconv -felf32 -nu filename.obj filename.o\n\n");
1236 }
1237