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