1 /* GNU PIC view object
2    Copyright (C) 2001, 2002, 2003, 2004, 2005
3    Craig Franklin
4 
5     Copyright (C) 2016 Molnar Karoly
6 
7 This file is part of gputils.
8 
9 gputils is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
12 any later version.
13 
14 gputils is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with gputils; see the file COPYING.  If not, write to
21 the Free Software Foundation, 59 Temple Place - Suite 330,
22 Boston, MA 02111-1307, USA.  */
23 
24 #include "stdhdr.h"
25 
26 #include "libgputils.h"
27 #include "gpvo.h"
28 
29 struct gpvo_state state;
30 
31 enum {
32   OPT_STRICT_OPTIONS = 0x100
33 };
34 
35 #define GET_OPTIONS "bcfhnstvx:y"
36 
37 /* Used: himpsv */
38 static struct option longopts[] =
39 {
40   { "binary",         no_argument,       NULL, 'b' },
41   { "mnemonics",      no_argument,       NULL, 'c' },
42   { "file",           no_argument,       NULL, 'f' },
43   { "help",           no_argument,       NULL, 'h' },
44   { "no-names",       no_argument,       NULL, 'n' },
45   { "section",        no_argument,       NULL, 's' },
46   { "strict-options", no_argument,       NULL, OPT_STRICT_OPTIONS },
47   { "symbol",         no_argument,       NULL, 't' },
48   { "version",        no_argument,       NULL, 'v' },
49   { "export",         required_argument, NULL, 'x' },
50   { "extended",       no_argument,       NULL, 'y' },
51   { NULL,             no_argument,       NULL, '\0'}
52 };
53 
54 /*------------------------------------------------------------------------------------------------*/
55 
56 static void
_show_usage(void)57 _show_usage(void)
58 {
59   printf("Usage: gpvo [options] file\n");
60   printf("Options: [defaults in brackets after descriptions]\n");
61   printf("  -b, --binary            Print binary data.\n");
62   printf("  -c, --mnemonics         Decode special mnemonics.\n");
63   printf("  -f, --file              File header.\n");
64   printf("  -h, --help              Show this usage message.\n");
65   printf("  -n, --no-names          Suppress filenames.\n");
66   printf("  -s, --section           Section data.\n");
67   printf("      --strict-options    If this is set, then an option may not be parameter\n"
68          "                            of an another option. For example: -x --symbol\n");
69   printf("  -t  --symbol            Symbol table.\n");
70   printf("  -v, --version           Show version.\n");
71   printf("  -x FILE, --export FILE  Export symbols to include file.\n");
72   printf("  -y, --extended          Enable 18xx extended mode.\n\n");
73   printf("Report bugs to:\n");
74   printf("%s\n", PACKAGE_BUGREPORT);
75   exit(0);
76 }
77 
78 /*------------------------------------------------------------------------------------------------*/
79 
80 static void
_print_header(const gp_object_t * Object)81 _print_header(const gp_object_t *Object)
82 {
83   static struct magic_s {
84     uint16_t    magic_num;
85     const char *magic_name;
86   } magic[] = {
87     { 0x1234, "MICROCHIP_MAGIC_v1" },
88     { 0x1240, "MICROCHIP_MAGIC_v2" }
89   };
90 
91   time_t      time;
92   const char *processor_name;
93   int         i;
94 #ifdef HAVE_LOCALE_H
95   char        time_str[256];
96 #else
97   char       *time_str;
98 #endif
99 
100   time = (time_t)Object->time;
101 
102 #ifdef HAVE_LOCALE_H
103   setlocale(LC_ALL, "");
104   strftime(time_str, sizeof(time_str), "%c", localtime(&time));
105   setlocale(LC_ALL, "C");
106 #else
107   time_str = ctime(&time);
108   /* strip the newline from time */
109   time_str[strlen(time_str) - 1] = '\0';
110 #endif
111 
112   processor_name = gp_processor_name(Object->processor, 2);
113 
114   printf("COFF File and Optional Headers\n");
115 
116   for (i = 0; i < ARRAY_SIZE(magic); ++i) {
117     if (magic[i].magic_num == Object->version) {
118       break;
119     }
120   }
121 
122   printf("COFF version         %#x: %s\n", Object->version,
123          (i < ARRAY_SIZE(magic)) ? magic[i].magic_name : "unknown");
124   printf("Processor Type       %s\n",  processor_name);
125   printf("Time Stamp           %s\n",  time_str);
126   printf("Number of Sections   %zu\n", Object->section_list.num_nodes);
127   printf("Number of Symbols    %u\n",  Object->num_symbols);
128   printf("Characteristics      %#x\n", Object->flags);
129 
130   if (Object->flags & F_RELFLG) {
131     printf("  Relocation info has been stripped.\n");
132   }
133 
134   if (Object->flags & F_EXEC) {
135     printf("  File is executable.\n");
136   }
137 
138   if (Object->flags & F_LINENO) {
139     printf("  Line numbers have been stripped.\n");
140   }
141 
142   if (Object->flags & F_ABSOLUTE) {
143     printf("  The MPASM assembler object file is from absolute assembly code.\n");
144   }
145 
146   if (Object->flags & L_SYMS) {
147     printf("  Local symbols have been stripped.\n");
148   }
149 
150   if (Object->flags & F_EXTENDED18) {
151     printf("  The COFF file produced utilizing the Extended mode.\n");
152   }
153 
154   if (Object->flags & F_GENERIC) {
155     printf("  Processor independent file for a core.\n");
156   }
157 
158   printf("\n");
159 }
160 
161 /*------------------------------------------------------------------------------------------------*/
162 
163 static const char *
_format_reloc_type(uint16_t Type,char * Buffer,size_t Sizeof_buffer)164 _format_reloc_type(uint16_t Type, char *Buffer, size_t Sizeof_buffer)
165 {
166   snprintf(Buffer, Sizeof_buffer, "0x%04x %-20s", Type, gp_coffgen_reloc_type_to_str(Type));
167   return Buffer;
168 }
169 
170 /*------------------------------------------------------------------------------------------------*/
171 
172 static void
_print_relocation_list(proc_class_t Class,const gp_reloc_t * Relocation,const char * Column_gap)173 _print_relocation_list(proc_class_t Class, const gp_reloc_t *Relocation, const char *Column_gap)
174 {
175   char buffer[32];
176   int  addr_digits;
177 
178   addr_digits = Class->addr_digits;
179 
180   printf("Relocations Table\n"
181          "Address     Offset      Offset       Type                        Symbol\n"
182          "            (hex)       (dec)\n");
183 
184   while (Relocation != NULL) {
185     printf("0x%0*x%s    0x%08x  %11d  %-25s %-s\n",
186            addr_digits, gp_processor_insn_from_byte_c(Class, Relocation->address), Column_gap,
187            Relocation->offset, Relocation->offset,
188            _format_reloc_type(Relocation->type, buffer, sizeof(buffer)),
189            Relocation->symbol->name);
190 
191     Relocation = Relocation->next;
192   }
193 
194   printf("\n");
195 }
196 
197 /*------------------------------------------------------------------------------------------------*/
198 
199 static void
_print_linenum_list(proc_class_t Class,const gp_linenum_t * Linenumber,const char * Column_gap)200 _print_linenum_list(proc_class_t Class, const gp_linenum_t *Linenumber, const char *Column_gap)
201 {
202   const char *filename;
203   int         addr_digits;
204 
205   addr_digits = Class->addr_digits;
206 
207   printf("Line Number Table\n"
208          "Line      Address     Symbol\n");
209 
210   while (Linenumber != NULL) {
211     if (state.suppress_names) {
212       filename = Linenumber->symbol->name;
213     }
214     else {
215       filename = Linenumber->symbol->aux_list.first->_aux_symbol._aux_file.filename;
216     }
217 
218     printf("%-8i  0x%0*x%s    %s\n",
219            Linenumber->line_number,
220            addr_digits, gp_processor_insn_from_byte_c(Class, Linenumber->address), Column_gap,
221            filename);
222 
223     Linenumber = Linenumber->next;
224   }
225 
226   printf("\n");
227 }
228 
229 /*------------------------------------------------------------------------------------------------*/
230 
231 static void
_print_data(pic_processor_t Processor,const gp_section_t * Section)232 _print_data(pic_processor_t Processor, const gp_section_t *Section)
233 {
234   proc_class_t class;
235   char         buffer[BUFSIZ];
236   unsigned int address;
237   int          num_words;
238   int          addr_digits;
239   unsigned int bsr_boundary;
240   uint16_t     word;
241   uint8_t      byte;
242 
243   class        = Processor->class;
244   address      = Section->address;
245   bsr_boundary = gp_processor_bsr_boundary(Processor);
246   addr_digits  = class->addr_digits;
247 
248   buffer[0] = '\0';
249 
250   printf("Data\n");
251   while (true) {
252     if ((Section->flags & STYP_TEXT) && (class->find_insn != NULL)) {
253       if (class->i_memory_get(Section->data, address, &word, NULL, NULL) != W_USED_ALL) {
254         break;
255       }
256 
257       num_words = gp_disassemble(Section->data, address, class, bsr_boundary, 0,
258                                  GPDIS_SHOW_ALL_BRANCH, buffer, sizeof(buffer), 0);
259       printf("%0*x:  %04x  %s\n",
260              addr_digits, gp_processor_insn_from_byte_c(class, address), word, buffer);
261 
262       if (num_words != 1) {
263         if (class->i_memory_get(Section->data, address + WORD_SIZE, &word, NULL, NULL) != W_USED_ALL) {
264           break;
265         }
266 
267         printf("%0*x:  %04x\n",
268                addr_digits, gp_processor_insn_from_byte_c(class, address + WORD_SIZE), word);
269       }
270 
271       address += num_words * WORD_SIZE;
272     }
273     else if ((Section->flags & STYP_DATA_ROM) || (class == PROC_CLASS_EEPROM16)) {
274       if (class->i_memory_get(Section->data, address, &word, NULL, NULL) != 0) {
275         printf("%0*x:  %04x\n", addr_digits, gp_processor_insn_from_byte_c(class, address), word);
276         address += WORD_SIZE;
277       }
278       else {
279         if (gp_mem_b_get(Section->data, address, &byte, NULL, NULL)) {
280           printf("%0*x:  %02x\n", addr_digits, gp_processor_insn_from_byte_c(class, address), byte);
281         }
282 
283         break;
284       }
285     }
286     else {
287       /* STYP_DATA or STYP_ACTREC, or EEPROM8 */
288       if (!gp_mem_b_get(Section->data, address, &byte, NULL, NULL)) {
289         break;
290       }
291 
292       printf("%0*x:  %02x\n", addr_digits, address, byte);
293       ++address;
294     }
295   }
296 
297   printf("\n");
298 }
299 
300 /*------------------------------------------------------------------------------------------------*/
301 
302 static void
_print_section_header(proc_class_t Class,const gp_section_t * Section)303 _print_section_header(proc_class_t Class, const gp_section_t *Section)
304 {
305   unsigned int org_to_byte_shift;
306 
307   org_to_byte_shift = (Section->flags & STYP_ROM_AREA) ? Class->org_to_byte_shift : 0;
308 
309   printf("Section Header\n");
310   printf("Name                    %s\n",  Section->name);
311   printf("Physical address        %#x\n", gp_insn_from_byte(org_to_byte_shift, Section->address));
312   printf("Virtual address         %#x\n", gp_insn_from_byte(org_to_byte_shift, Section->virtual_address));
313   printf("Size of Section         %u\n",  Section->size);
314   printf("Number of Relocations   %zu\n", Section->relocation_list.num_nodes);
315   printf("Number of Line Numbers  %zu\n", Section->line_number_list.num_nodes);
316   printf("Flags                   %#x\n", Section->flags);
317 
318   if (Section->flags & STYP_TEXT) {
319     printf("  Executable code.\n");
320   }
321 
322   if (Section->flags & STYP_DATA) {
323     printf("  Initialized data.\n");
324   }
325 
326   if (Section->flags & STYP_BSS) {
327     printf("  Uninitialized data.\n");
328   }
329 
330   if (Section->flags & STYP_DATA_ROM) {
331     printf("  Initialized data for ROM.\n");
332   }
333 
334   if (Section->flags & STYP_ABS) {
335     printf("  Absolute.\n");
336   }
337 
338   if (Section->flags & STYP_SHARED) {
339     printf("  Shared across banks.\n");
340   }
341 
342   if (Section->flags & STYP_OVERLAY) {
343     printf("  Overlaid with other sections from different objects modules.\n");
344   }
345 
346   if (Section->flags & STYP_ACCESS) {
347     printf("  Available using access bit.\n");
348   }
349 
350   if (Section->flags & STYP_ACTREC) {
351     printf("  Contains the activation record for a function.\n");
352   }
353 
354   printf("\n");
355 }
356 
357 /*------------------------------------------------------------------------------------------------*/
358 
359 static void
_print_section_list(const gp_object_t * Object)360 _print_section_list(const gp_object_t *Object)
361 {
362   proc_class_t        class;
363   const gp_section_t *section;
364   char                column_gap[16];
365   unsigned int        i;
366 
367   class = Object->class;
368   i     = class->addr_digits;
369 
370   if (i > 6) {
371     i = 6;
372   }
373 
374   i = 6 - i;
375 
376   if (i >= (sizeof(column_gap) - 1)) {
377     i = (sizeof(column_gap) - 1);
378   }
379 
380   if (i > 0) {
381     memset(column_gap, ' ', i);
382   }
383 
384   column_gap[i] = '\0';
385 
386   section = Object->section_list.first;
387   while (section != NULL) {
388     _print_section_header(class, section);
389 
390     if ((section->size > 0) && (section->data_ptr > 0)) {
391       _print_data(Object->processor, section);
392     }
393 
394     if (section->relocation_list.num_nodes > 0) {
395       _print_relocation_list(class, section->relocation_list.first, column_gap);
396     }
397 
398     if (section->line_number_list.num_nodes > 0) {
399       _print_linenum_list(class, section->line_number_list.first, column_gap);
400     }
401 
402     section = section->next;
403   }
404 }
405 
406 /*------------------------------------------------------------------------------------------------*/
407 
408 static void
_coff_type(unsigned int Type,char * Buffer,size_t Sizeof_buffer)409 _coff_type(unsigned int Type, char *Buffer, size_t Sizeof_buffer)
410 {
411   const char *str;
412 
413   switch (Type) {
414     case T_NULL:   str = "NULL";                break; /* null */
415     case T_VOID:   str = "void";                break; /* void */
416     case T_CHAR:   str = "char";                break; /* character */
417     case T_SHORT:  str = "short";               break; /* short integer */
418     case T_INT:    str = "int";                 break; /* integer */
419     case T_LONG:   str = "long int";            break; /* long integer */
420     case T_FLOAT:  str = "float";               break; /* floating point */
421     case T_DOUBLE: str = "double";              break; /* double length floating point */
422     case T_STRUCT: str = "struct";              break; /* structure */
423     case T_UNION:  str = "union";               break; /* union */
424     case T_ENUM:   str = "enum";                break; /* enumeration */
425     case T_MOE:    str = "enum member";         break; /* member of enumeration */
426     case T_UCHAR:  str = "unsigned char";       break; /* unsigned character */
427     case T_USHORT: str = "unsigned short";      break; /* unsigned short */
428     case T_UINT:   str = "unsigned int";        break; /* unsigned integer */
429     case T_ULONG:  str = "unsigned long";       break; /* unsigned long */
430     case T_LNGDBL: str = "long double";         break; /* long double floating point */
431     case T_SLONG:  str = "short long";          break; /* short long */
432     case T_USLONG: str = "unsigned short long"; break; /* unsigned short long */
433     default:       str = NULL;                  break;
434   }
435 
436   if (str != NULL) {
437     snprintf(Buffer, Sizeof_buffer, "%s", str);
438   }
439   else {
440     snprintf(Buffer, Sizeof_buffer, "unknown(%u)", Type);
441   }
442 }
443 
444 /*------------------------------------------------------------------------------------------------*/
445 
446 static const char *
_format_sym_type(unsigned int Type,char * Buffer,size_t Sizeof_buffer)447 _format_sym_type(unsigned int Type, char *Buffer, size_t Sizeof_buffer)
448 {
449   const char *str;
450 
451   str = gp_coffgen_symbol_type_to_str(Type);
452   if (str != NULL) {
453     return str;
454   }
455 
456   snprintf(Buffer, Sizeof_buffer, "%u", Type);
457   return Buffer;
458 }
459 
460 /*------------------------------------------------------------------------------------------------*/
461 
462 static const char *
_format_sym_derived_type(unsigned int Type,char * Buffer,size_t Sizeof_buffer)463 _format_sym_derived_type(unsigned int Type, char *Buffer, size_t Sizeof_buffer)
464 {
465   const char *str;
466 
467   str = gp_coffgen_symbol_derived_type_to_str(Type);
468   if (str != NULL) {
469     return str;
470   }
471 
472   snprintf(Buffer, Sizeof_buffer, "%u", Type);
473   return Buffer;
474 }
475 
476 /*------------------------------------------------------------------------------------------------*/
477 
478 static const char *
_format_sym_class(unsigned int Class,char * Buffer,size_t Sizeof_buffer)479 _format_sym_class(unsigned int Class, char *Buffer, size_t Sizeof_buffer)
480 {
481   const char *str;
482 
483   str = gp_coffgen_symbol_class_to_str(Class);
484   if (str != NULL) {
485     return str;
486   }
487 
488   snprintf(Buffer, Sizeof_buffer, "%u", Class);
489   return Buffer;
490 }
491 
492 /*------------------------------------------------------------------------------------------------*/
493 
494 #define AUX_INDENT              "       "
495 
496 static void
_print_symbol_table(const gp_object_t * Object)497 _print_symbol_table(const gp_object_t *Object)
498 {
499   static char   buf[64];
500 
501   gp_symbol_t  *symbol;
502   gp_symbol_t  *callee;
503   gp_aux_t     *aux;
504   const char   *section;
505   int           c;
506   size_t        i;
507   unsigned int  idx;
508   char          buffer_type[8];
509   char          buffer_derived_type[8];
510   char          buffer_class[8];
511 
512   printf("Symbol Table\n");
513   printf("Idx  Name                     Section          Value      Type     DT           Class     NumAux\n");
514 
515   idx = 1;
516   symbol = Object->symbol_list.first;
517   while (symbol != NULL) {
518     if (symbol->section_number == N_DEBUG) {
519       section = "DEBUG";
520     }
521     else if (symbol->section_number == N_ABS) {
522       section = "ABSOLUTE";
523     }
524     else if (symbol->section_number == N_UNDEF) {
525       section = "UNDEFINED";
526     }
527     else {
528       if (symbol->section != NULL) {
529         section = symbol->section->name;
530       }
531       else {
532         snprintf(buf, sizeof(buf), "Bad num.: %u", symbol->section_number);
533         section = buf;
534       }
535     }
536 
537     printf("%04u %-24s %-16s 0x%08lx %-8s %-12s %-9s %zu\n",
538            idx,
539            symbol->name,
540            section,
541            symbol->value,
542            _format_sym_type(symbol->type, buffer_type, sizeof(buffer_type)),
543            _format_sym_derived_type(symbol->derived_type, buffer_derived_type, sizeof(buffer_derived_type)),
544            _format_sym_class(symbol->class, buffer_class, sizeof(buffer_class)),
545            symbol->aux_list.num_nodes);
546 
547     aux = symbol->aux_list.first;
548     while (aux != NULL) {
549       switch (aux->type) {
550         case AUX_DIRECT:
551           printf(AUX_INDENT "command = '%c'\n", aux->_aux_symbol._aux_direct.command);
552           printf(AUX_INDENT "string  = \"%s\"\n", aux->_aux_symbol._aux_direct.string);
553           break;
554 
555         case AUX_FILE: {
556           if (!state.suppress_names) {
557             printf(AUX_INDENT "file          = %s\n", aux->_aux_symbol._aux_file.filename);
558           }
559           printf(AUX_INDENT "line included = %u\n", aux->_aux_symbol._aux_file.line_number);
560           printf(AUX_INDENT "flags         = 0x%08x\n", aux->_aux_symbol._aux_file.flags);
561           break;
562         }
563 
564         case AUX_IDENT:
565           printf(AUX_INDENT "string = \"%s\"\n", aux->_aux_symbol._aux_ident.string);
566           break;
567 
568         case AUX_SECTION:
569           printf(AUX_INDENT "length                 = %u\n", aux->_aux_symbol._aux_scn.length);
570           printf(AUX_INDENT "number of relocations  = %u\n", aux->_aux_symbol._aux_scn.nreloc);
571           printf(AUX_INDENT "number of line numbers = %u\n", aux->_aux_symbol._aux_scn.nlineno);
572           break;
573 
574         case AUX_FCN_CALLS: {
575           callee = aux->_aux_symbol._aux_fcn_calls.callee;
576           printf(AUX_INDENT "callee       = %s\n", (callee != NULL) ? callee->name : "higher order");
577           printf(AUX_INDENT "is_interrupt = %u\n", aux->_aux_symbol._aux_fcn_calls.is_interrupt);
578           break;
579         }
580 
581         default: {
582           printf("%u  ", aux->type);
583           for (i = 0; i < Object->symbol_size; i++) {
584             printf("%02x", aux->_aux_symbol.data[i] & 0xFF);
585 
586             if (i & 1) {
587               putchar(' ');
588             }
589           }
590           for (i = 0; i < Object->symbol_size; i++) {
591             c = aux->_aux_symbol.data[i] & 0xFF;
592             putchar((isprint(c)) ? c : '.');
593           }
594           putchar('\n');
595         }
596       } /* switch (aux->type) */
597 
598       aux = aux->next;
599       ++idx;
600     }
601 
602     symbol = symbol->next;
603     ++idx;
604   }
605 
606   putchar('\n');
607 }
608 
609 /*------------------------------------------------------------------------------------------------*/
610 
611 static void
_export_symbol_table(gp_object_t * Object)612 _export_symbol_table(gp_object_t *Object)
613 {
614   gp_symbol_t *symbol;
615   char         buffer[BUFSIZ];
616 
617   symbol = Object->symbol_list.first;
618   while (symbol != NULL) {
619     if ((state.export.enabled) && (symbol->class == C_EXT) && (symbol->section_number > N_UNDEF)) {
620       _coff_type(symbol->type, buffer, sizeof(buffer));
621       fprintf(state.export.f, "  extern %s ; %s\n", symbol->name, buffer);
622     }
623 
624     symbol = symbol->next;
625   }
626 }
627 
628 /*------------------------------------------------------------------------------------------------*/
629 
630 #define BIN_DATA_LINE_SIZE    16
631 
632 static void
_print_binary(const uint8_t * Data,long File_size)633 _print_binary(const uint8_t *Data, long File_size)
634 {
635   long         i;
636   long         j;
637   unsigned int memory;
638   int          c;
639 
640   printf("\nObject file size = %li bytes\n", File_size);
641 
642   printf("\nBinary object file contents:");
643   for (i = 0; i < File_size; i += BIN_DATA_LINE_SIZE) {
644     printf("\n%06lx", i);
645 
646     for (j = 0; j < BIN_DATA_LINE_SIZE; j += 2) {
647       memory = (Data[i + j] << 8) | Data[i + j + 1];
648 
649       if ((i + j) >= File_size) {
650         printf("     ");
651       }
652       else {
653         printf(" %04x", memory);
654       }
655     }
656 
657     putchar(' ');
658 
659     for (j = 0; j < BIN_DATA_LINE_SIZE; j += 2) {
660       if ((i + j) < File_size) {
661         c = Data[i + j];
662         putchar((isprint(c)) ? c : '.');
663 
664         c = Data[i + j + 1];
665         putchar((isprint(c)) ? c : '.');
666       }
667     }
668   }
669 
670   printf("\n\n");
671 }
672 
673 /*------------------------------------------------------------------------------------------------*/
674 
main(int argc,char * argv[])675 int main(int argc, char *argv[])
676 {
677   int         option_index;
678   int         c;
679   const char *command;
680   gp_boolean  strict_options = false;
681   gp_boolean  usage          = false;
682   char        buffer[BUFSIZ];
683 
684   gp_init();
685 
686   /* initalize */
687   state.dump_flags     = 0;
688   state.suppress_names = false;
689   state.export.enabled = false;
690 
691   /* Scan through the options for the --strict-options flag. */
692   while ((c = getopt_long(argc, argv, GET_OPTIONS, longopts, NULL)) != EOF) {
693     if (c == OPT_STRICT_OPTIONS) {
694       strict_options = true;
695       break;
696     }
697   }
698 
699   /* Restores the getopt_long index. */
700   optind = 1;
701   while (true) {
702     /* This is necessary for the gp_exit_is_excluded_arg() function. */
703     option_index = -1;
704     command = argv[optind];
705     if ((c = getopt_long(argc, argv, GET_OPTIONS, longopts, &option_index)) == EOF) {
706       break;
707     }
708 
709     if (strict_options) {
710       gp_exit_if_arg_an_option(longopts, ARRAY_SIZE(longopts), option_index, optarg, c, command);
711     }
712 
713     switch (c) {
714       case '?':
715       case 'h':
716         usage = true;
717         break;
718 
719       case 'b':
720         state.dump_flags |= PRINT_BINARY;
721         break;
722 
723       case 'c':
724         gp_decode_mnemonics = true;
725         break;
726 
727       case 'f':
728         state.dump_flags |= PRINT_HEADER;
729         break;
730 
731       case 'n':
732         state.suppress_names = true;
733         break;
734 
735       case 's':
736         state.dump_flags |= PRINT_SECTIONS;
737         break;
738 
739       case 't':
740         state.dump_flags |= PRINT_SYMTBL;
741         break;
742 
743       case 'y':
744         gp_decode_extended = true;
745         break;
746 
747       case 'x':
748         state.export.enabled = true;
749         state.export.filename = optarg;
750         break;
751 
752       case 'v':
753         fprintf(stderr, "%s\n", GPVO_VERSION_STRING);
754         exit(0);
755 
756       case OPT_STRICT_OPTIONS:
757         /* do nothing */
758         break;
759     } /* switch (c) */
760 
761     if (usage)
762       break;
763   }
764 
765   if ((optind + 1) == argc) {
766     state.filename = argv[optind];
767   }
768   else {
769     usage = true;
770   }
771 
772   if (usage) {
773     _show_usage();
774   }
775 
776   if (!state.dump_flags) {
777     /* no command line flags were set so print everything */
778     state.dump_flags = PRINT_HEADER | PRINT_SECTIONS | PRINT_SYMTBL;
779   }
780 
781   if ((gp_identify_coff_file(state.filename) != GP_COFF_OBJECT_V2) &&
782       (gp_identify_coff_file(state.filename) != GP_COFF_OBJECT)) {
783     gp_error("The \"%s\" is not a valid object file.", state.filename);
784     exit(1);
785   }
786 
787   state.object = gp_read_coff(state.filename);
788   state.file   = gp_read_file(state.filename);
789 
790   if (state.export.enabled) {
791     state.export.f = fopen(state.export.filename, "w");
792 
793     if (state.export.f == NULL) {
794       perror(state.export.filename);
795       exit(1);
796     }
797 
798     gp_date_string(buffer, sizeof(buffer));
799 
800     fprintf(state.export.f, "; %s\n", state.export.filename);
801     fprintf(state.export.f, "; generated by %s on %s\n", GPVO_VERSION_STRING, buffer);
802     fprintf(state.export.f, "; from %s\n\n", state.filename);
803 
804     _export_symbol_table(state.object);
805 
806     fclose(state.export.f);
807 
808     /* suppress normal output */
809     state.dump_flags = 0;
810   }
811 
812   if (state.dump_flags & PRINT_HEADER) {
813     _print_header(state.object);
814   }
815 
816   if (state.dump_flags & PRINT_SECTIONS) {
817     _print_section_list(state.object);
818   }
819 
820   if (state.dump_flags & PRINT_SYMTBL) {
821     _print_symbol_table(state.object);
822   }
823 
824   if (state.dump_flags & PRINT_BINARY) {
825     _print_binary(state.file->file, state.file->size);
826   }
827 
828   return EXIT_SUCCESS;
829 }
830