/* GNU PIC view object
Copyright 2001-2005 Craig Franklin
Copyright 2016 Molnár Károly
*/
#include "gpvo.h"
gpvo_state state;
static const char longopts[]=
"bbinary\0" // first character is short option, following long option (if any)
"cmnemonics\0"
"ffile\0"
"hhelp\0"
"nno-names\0"
"ssection\0"
"\1strict-options\0" // Non-printable first character = no short option but return value
"tsymbol\0"
"vversion\0"
"xfexport\1FILE\0" // \1 = required argument, name id for usage formatting
"yextended\0"; // Entire string is double-zero terminated
static const char longdesc[]=
"Print binary data.\0"
"Decode special mnemonics.\0"
"File header.\0"
"Show this usage message.\0"
"Suppress filenames.\0"
"Section data.\0"
"If this is set, then an option may not be parameter"
" of an another option. For example: -x --symbol\0"
"Symbol table.\0"
"Show version.\0"
"Export symbols to include file.\0"
"Enable 18xx extended mode.\0";
/*------------------------------------------------------------------------------------------------*/
__attribute__((noreturn)) static void _show_usage(const char*argv0) {
gp_usage(argv0,longopts,longdesc);
exit(0);
}
/*------------------------------------------------------------------------------------------------*/
static void _print_header(const gp_object_t*Object) {
static struct magic_s {
uint16_t magic_num;
const char *magic_name;
} magic[] = {
{ 0x1234, "MICROCHIP_MAGIC_v1" },
{ 0x1240, "MICROCHIP_MAGIC_v2" }
};
time_t time;
const char *processor_name;
int i;
#ifdef HAVE_LOCALE_H
char time_str[256];
#else
char *time_str;
#endif
time = (time_t)Object->time;
#ifdef HAVE_LOCALE_H
setlocale(LC_ALL, "");
strftime(time_str, sizeof(time_str), "%c", localtime(&time));
setlocale(LC_ALL, "C");
#else
time_str = ctime(&time);
/* strip the newline from time */
time_str[strlen(time_str) - 1] = '\0';
#endif
processor_name = gp_processor_name(Object->processor, 2);
printf("COFF File and Optional Headers\n");
for (i = 0; i < (int)ARRAY_SIZE(magic); ++i) {
if (magic[i].magic_num == Object->version) {
break;
}
}
printf("COFF version %#x: %s\n", Object->version,
(i < (int)ARRAY_SIZE(magic)) ? magic[i].magic_name : "unknown");
printf("Processor Type %s\n", processor_name);
printf("Time Stamp %s\n", time_str);
printf("Number of Sections %" SIZE_FMTu "\n", Object->section_list.num_nodes);
printf("Number of Symbols %u\n", Object->num_symbols);
printf("Characteristics %#x\n", Object->flags);
if (Object->flags & F_RELFLG) {
printf(" Relocation info has been stripped.\n");
}
if (Object->flags & F_EXEC) {
printf(" File is executable.\n");
}
if (Object->flags & F_LINENO) {
printf(" Line numbers have been stripped.\n");
}
if (Object->flags & F_ABSOLUTE) {
printf(" The MPASM assembler object file is from absolute assembly code.\n");
}
if (Object->flags & L_SYMS) {
printf(" Local symbols have been stripped.\n");
}
if (Object->flags & F_EXTENDED18) {
printf(" The COFF file produced utilizing the Extended mode.\n");
}
if (Object->flags & F_GENERIC) {
printf(" Processor independent file for a core.\n");
}
printf("\n");
}
/*------------------------------------------------------------------------------------------------*/
static const char* _format_reloc_type(uint16_t Type, char *Buffer, size_t Sizeof_buffer) {
snprintf(Buffer, Sizeof_buffer, "0x%04x %-20s", Type, gp_coffgen_reloc_type_to_str(Type));
return Buffer;
}
/*------------------------------------------------------------------------------------------------*/
static void _print_relocation_list(proc_class_t Class, const gp_reloc_t *Relocation, const char *Column_gap) {
char buffer[32];
int addr_digits;
addr_digits = Class->addr_digits;
printf("Relocations Table\n"
"Address Offset Offset Type Symbol\n"
" (hex) (dec)\n");
while (Relocation) {
printf("0x%0*x%s 0x%08x %11d %-25s %-s\n",
addr_digits, gp_processor_insn_from_byte_c(Class, (int)Relocation->address), Column_gap,
Relocation->offset, Relocation->offset,
_format_reloc_type(Relocation->type, buffer, sizeof(buffer)),
Relocation->symbol->name);
Relocation = Relocation->next;
}
printf("\n");
}
/*------------------------------------------------------------------------------------------------*/
static void _print_linenum_list(proc_class_t Class, const gp_linenum_t *Linenumber, const char *Column_gap) {
const char *filename;
int addr_digits;
addr_digits = Class->addr_digits;
printf("Line Number Table\n"
"Line Address Symbol\n");
while (Linenumber) {
if (state.suppress_names) {
filename = Linenumber->symbol->name;
}
else {
filename = Linenumber->symbol->aux_list.first->_aux_symbol._aux_file.filename;
}
printf("%-8i 0x%0*x%s %s\n",
Linenumber->line_number,
addr_digits, gp_processor_insn_from_byte_c(Class, (int)Linenumber->address), Column_gap,
filename);
Linenumber = Linenumber->next;
}
printf("\n");
}
/*------------------------------------------------------------------------------------------------*/
static void _print_data(pic_processor_t Processor, const gp_section_t *Section) {
proc_class_t pclass;
char buffer[BUFSIZ];
unsigned address;
int num_words;
int addr_digits;
unsigned bsr_boundary;
uint16_t word;
uint8_t byte;
pclass = Processor->pclass;
address = Section->address;
bsr_boundary = gp_processor_bsr_boundary(Processor);
addr_digits = pclass->addr_digits;
buffer[0] = '\0';
printf("Data\n");
while (true) {
if ((Section->flags & STYP_TEXT) && (pclass->find_insn)) {
if (pclass->i_memory_get(Section->data, address, &word, NULL, NULL) != W_USED_ALL) {
break;
}
num_words = (int)gp_disassemble(Section->data, address, pclass, bsr_boundary, 0,
GPDIS_SHOW_ALL_BRANCH, buffer, sizeof(buffer), 0);
printf("%0*x: %04x %s\n",
addr_digits, gp_processor_insn_from_byte_c(pclass, (int)address), word, buffer);
if (num_words != 1) {
if (pclass->i_memory_get(Section->data, address + WORD_SIZE, &word, NULL, NULL) != W_USED_ALL) {
break;
}
printf("%0*x: %04x\n",
addr_digits, gp_processor_insn_from_byte_c(pclass, (int)(address + WORD_SIZE)), word);
}
address += (size_t)num_words * WORD_SIZE;
}
else if ((Section->flags & STYP_DATA_ROM) || (pclass == PROC_CLASS_EEPROM16)) {
if (pclass->i_memory_get(Section->data, address, &word, NULL, NULL) != 0) {
printf("%0*x: %04x\n", addr_digits, gp_processor_insn_from_byte_c(pclass, (int)address), word);
address += WORD_SIZE;
}
else {
if (gp_mem_b_get(Section->data, address, &byte, NULL, NULL)) {
printf("%0*x: %02x\n", addr_digits, gp_processor_insn_from_byte_c(pclass, (int)address), byte);
}
break;
}
}
else {
/* STYP_DATA or STYP_ACTREC, or EEPROM8 */
if (!gp_mem_b_get(Section->data, address, &byte, NULL, NULL)) {
break;
}
printf("%0*x: %02x\n", addr_digits, address, byte);
++address;
}
}
printf("\n");
}
/*------------------------------------------------------------------------------------------------*/
static void _print_section_header(proc_class_t Class, const gp_section_t *Section) {
unsigned org_to_byte_shift;
org_to_byte_shift = (Section->flags & STYP_ROM_AREA) ? Class->org_to_byte_shift : 0;
printf("Section Header\n");
printf("Name %s\n", Section->name);
printf("Physical address %#x\n", gp_insn_from_byte(org_to_byte_shift, (int)Section->address));
printf("Virtual address %#x\n", gp_insn_from_byte(org_to_byte_shift, (int)Section->virtual_address));
printf("Size of Section %u\n", Section->size);
printf("Number of Relocations %" SIZE_FMTu "\n", Section->relocation_list.num_nodes);
printf("Number of Line Numbers %" SIZE_FMTu "\n", Section->line_number_list.num_nodes);
printf("Flags %#x\n", Section->flags);
if (Section->flags & STYP_TEXT) {
printf(" Executable code.\n");
}
if (Section->flags & STYP_DATA) {
printf(" Initialized data.\n");
}
if (Section->flags & STYP_BSS) {
printf(" Uninitialized data.\n");
}
if (Section->flags & STYP_DATA_ROM) {
printf(" Initialized data for ROM.\n");
}
if (Section->flags & STYP_ABS) {
printf(" Absolute.\n");
}
if (Section->flags & STYP_SHARED) {
printf(" Shared across banks.\n");
}
if (Section->flags & STYP_OVERLAY) {
printf(" Overlaid with other sections from different objects modules.\n");
}
if (Section->flags & STYP_ACCESS) {
printf(" Available using access bit.\n");
}
if (Section->flags & STYP_ACTREC) {
printf(" Contains the activation record for a function.\n");
}
printf("\n");
}
/*------------------------------------------------------------------------------------------------*/
static void _print_section_list(const gp_object_t *Object) {
proc_class_t pclass;
const gp_section_t *section;
char column_gap[16];
unsigned i;
pclass = Object->pclass;
i = (unsigned)pclass->addr_digits;
if (i > 6) {
i = 6;
}
i = 6 - i;
if (i >= (sizeof(column_gap) - 1)) {
i = (sizeof(column_gap) - 1);
}
if (i > 0) {
memset(column_gap, ' ', i);
}
column_gap[i] = '\0';
section = Object->section_list.first;
while (section) {
_print_section_header(pclass, section);
if ((section->size > 0) && (section->data_ptr > 0)) {
_print_data(Object->processor, section);
}
if (section->relocation_list.num_nodes > 0) {
_print_relocation_list(pclass, section->relocation_list.first, column_gap);
}
if (section->line_number_list.num_nodes > 0) {
_print_linenum_list(pclass, section->line_number_list.first, column_gap);
}
section = section->next;
}
}
/*------------------------------------------------------------------------------------------------*/
static void _coff_type(unsigned Type, char *Buffer, size_t Sizeof_buffer) {
const char *str;
switch (Type) {
case T_NULL: str = "NULL"; break; /* null */
case T_VOID: str = "void"; break; /* void */
case T_CHAR: str = "char"; break; /* character */
case T_SHORT: str = "short"; break; /* short integer */
case T_INT: str = "int"; break; /* integer */
case T_LONG: str = "long int"; break; /* long integer */
case T_FLOAT: str = "float"; break; /* floating point */
case T_DOUBLE: str = "double"; break; /* double length floating point */
case T_STRUCT: str = "struct"; break; /* structure */
case T_UNION: str = "union"; break; /* union */
case T_ENUM: str = "enum"; break; /* enumeration */
case T_MOE: str = "enum member"; break; /* member of enumeration */
case T_UCHAR: str = "unsigned char"; break; /* unsigned character */
case T_USHORT: str = "unsigned short"; break; /* unsigned short */
case T_UINT: str = "unsigned int"; break; /* unsigned integer */
case T_ULONG: str = "unsigned long"; break; /* unsigned long */
case T_LNGDBL: str = "long double"; break; /* long double floating point */
case T_SLONG: str = "short long"; break; /* short long */
case T_USLONG: str = "unsigned short long"; break; /* unsigned short long */
default: str = NULL; break;
}
if (str) {
snprintf(Buffer, Sizeof_buffer, "%s", str);
}
else {
snprintf(Buffer, Sizeof_buffer, "unknown(%u)", Type);
}
}
/*------------------------------------------------------------------------------------------------*/
static const char* _format_sym_type(unsigned Type, char *Buffer, size_t Sizeof_buffer) {
const char *str = gp_coffgen_symbol_type_to_str((uint8_t)Type);
if (str) return str;
snprintf(Buffer, Sizeof_buffer, "%u", Type);
return Buffer;
}
/*------------------------------------------------------------------------------------------------*/
static const char* _format_sym_derived_type(unsigned Type, char *Buffer, size_t Sizeof_buffer) {
const char *str = gp_coffgen_symbol_derived_type_to_str(Type);
if (str) return str;
snprintf(Buffer, Sizeof_buffer, "%u", Type);
return Buffer;
}
/*------------------------------------------------------------------------------------------------*/
static const char* _format_sym_class(unsigned Class, char *Buffer, size_t Sizeof_buffer) {
const char *str = gp_coffgen_symbol_class_to_str((uint8_t)Class);
if (str) return str;
snprintf(Buffer, Sizeof_buffer, "%u", Class);
return Buffer;
}
/*------------------------------------------------------------------------------------------------*/
#define AUX_INDENT " "
static void _print_symbol_table(const gp_object_t *Object) {
static char buf[64]; // igitt!!
printf("Symbol Table\n");
printf("Idx Name Section Value Type DT Class NumAux\n");
unsigned idx = 1;
FORC(gp_symbol_list_t,symbol,Object->symbol_list) {
const char*section;
if (symbol->section_number == N_DEBUG) {
section = "DEBUG";
}else if (symbol->section_number == N_ABS) {
section = "ABSOLUTE";
}else if (symbol->section_number == N_UNDEF) {
section = "UNDEFINED";
}else{
if (symbol->section) {
section = symbol->section->name;
}else{
snprintf(buf, sizeof(buf), "Bad num.: %u", symbol->section_number);
section = buf;
}
}
char buffer_type[8];
char buffer_derived_type[8];
char buffer_class[8];
printf("%04u %-24s %-16s 0x%08lx %-8s %-12s %-9s %" SIZE_FMTu "\n",
idx,
symbol->name,
section,
symbol->value,
_format_sym_type(symbol->type, buffer_type, sizeof(buffer_type)),
_format_sym_derived_type(symbol->derived_type, buffer_derived_type, sizeof(buffer_derived_type)),
_format_sym_class(symbol->pclass, buffer_class, sizeof(buffer_class)),
symbol->aux_list.num_nodes);
gp_aux_t*aux = symbol->aux_list.first;
while (aux) {
switch (aux->type) {
case AUX_DIRECT:
printf(AUX_INDENT "command = '%c'\n", aux->_aux_symbol._aux_direct.command);
printf(AUX_INDENT "string = \"%s\"\n", aux->_aux_symbol._aux_direct.string);
break;
case AUX_FILE: {
if (!state.suppress_names) {
printf(AUX_INDENT "file = %s\n", aux->_aux_symbol._aux_file.filename);
}
printf(AUX_INDENT "line included = %u\n", aux->_aux_symbol._aux_file.line_number);
printf(AUX_INDENT "flags = 0x%08x\n", aux->_aux_symbol._aux_file.flags);
break;
}
case AUX_IDENT:
printf(AUX_INDENT "string = \"%s\"\n", aux->_aux_symbol._aux_ident.string);
break;
case AUX_SECTION:
printf(AUX_INDENT "length = %u\n", aux->_aux_symbol._aux_scn.length);
printf(AUX_INDENT "number of relocations = %u\n", aux->_aux_symbol._aux_scn.nreloc);
printf(AUX_INDENT "number of line numbers = %u\n", aux->_aux_symbol._aux_scn.nlineno);
break;
case AUX_FCN_CALLS: {
gp_symbol_t*callee = aux->_aux_symbol._aux_fcn_calls.callee;
printf(AUX_INDENT "callee = %s\n", (callee) ? callee->name : "higher order");
printf(AUX_INDENT "is_interrupt = %u\n", aux->_aux_symbol._aux_fcn_calls.is_interrupt);
break;
}
default: {
printf("%u ", aux->type);
size_t i;
for (i = 0; i < Object->symbol_size; i++) {
printf("%02x", aux->_aux_symbol.data[i] & 0xFF);
if (i & 1) {
putchar(' ');
}
}
for (i = 0; i < Object->symbol_size; i++) {
char c = aux->_aux_symbol.data[i] & 0xFF;
putchar(isprint(c) ? c : '.');
}
putchar('\n');
}
} /* switch (aux->type) */
aux = aux->next;
++idx;
}
++idx;
}
putchar('\n');
}
/*------------------------------------------------------------------------------------------------*/
static void _export_symbol_table(const gp_object_t *Object) {
FORC(gp_symbol_list_t,symbol,Object->symbol_list)
if (state.fexport.enabled && symbol->pclass == C_EXT && symbol->section_number > N_UNDEF) {
char buffer[BUFSIZ];
_coff_type(symbol->type, buffer, sizeof(buffer));
fprintf(state.fexport.f, " extern %s ; %s\n", symbol->name, buffer);
}
}
/*------------------------------------------------------------------------------------------------*/
#define BIN_DATA_LINE_SIZE 16
static void _print_binary(const uint8_t *Data, long File_size) {
long i;
long j;
unsigned memory;
int c;
printf("\nObject file size = %li bytes\n", File_size);
printf("\nBinary object file contents:");
for (i = 0; i < File_size; i += BIN_DATA_LINE_SIZE) {
printf("\n%06lx", i);
for (j = 0; j < BIN_DATA_LINE_SIZE; j += 2) {
memory = (Data[i + j] << 8) | Data[i + j + 1];
if ((i + j) >= File_size) {
printf(" ");
}
else {
printf(" %04x", memory);
}
}
putchar(' ');
for (j = 0; j < BIN_DATA_LINE_SIZE; j += 2) {
if ((i + j) < File_size) {
c = Data[i + j];
putchar((isprint(c)) ? c : '.');
c = Data[i + j + 1];
putchar((isprint(c)) ? c : '.');
}
}
}
printf("\n\n");
}
static bool strict_options;
static bool usage;
static void _stdcall onOption(void*, char c, const char*arg) {
switch (c) {
case 1: gp_checkarg(state.fexport.filename,longopts);
strict_options = true; break;
case '?':
case 'h': usage = true; break;
case 'b': state.dump_flags |= PRINT_BINARY; break;
case 'c': gp_decode_mnemonics = true; break;
case 'f': state.dump_flags |= PRINT_HEADER; break;
case 'n': state.suppress_names = true; break;
case 's': state.dump_flags |= PRINT_SECTIONS; break;
case 't': state.dump_flags |= PRINT_SYMTBL; break;
case 'y': gp_decode_extended = true; break;
case 'x': state.fexport.enabled = true;
if (strict_options) gp_checkarg(arg,longopts);
state.fexport.filename = arg; break;
case 'v': fprintf(stderr, "%s\n", GPVO_VERSION_STRING);
exit(0);
case 0: state.filename = arg; break;
}
}
/*------------------------------------------------------------------------------------------------*/
int main(int argc, char *argv[]) {
gp_init();
/* Scan through the options — but only once! */
gp_getopt(argv,longopts,onOption,0);
if (usage || !state.filename) _show_usage(*argv);
if (!state.dump_flags) {
/* no command line flags were set so print everything */
state.dump_flags = PRINT_HEADER | PRINT_SECTIONS | PRINT_SYMTBL;
}
if ((gp_identify_coff_file(state.filename) != GP_COFF_OBJECT_V2) &&
(gp_identify_coff_file(state.filename) != GP_COFF_OBJECT)) {
gp_error("The \"%s\" is not a valid object file.", state.filename);
exit(1);
}
state.object = gp_read_coff(state.filename);
state.file = gp_read_file(state.filename);
if (state.fexport.enabled) {
state.fexport.f = fopen(state.fexport.filename, "w");
if (!state.fexport.f) {
perror(state.fexport.filename);
exit(1);
}
char buffer[BUFSIZ];
gp_date_string(buffer, sizeof(buffer));
fprintf(state.fexport.f, "; %s\n", state.fexport.filename);
fprintf(state.fexport.f, "; generated by %s on %s\n", GPVO_VERSION_STRING, buffer);
fprintf(state.fexport.f, "; from %s\n\n", state.filename);
_export_symbol_table(state.object);
fclose(state.fexport.f);
/* suppress normal output */
state.dump_flags = 0;
}
if (state.dump_flags & PRINT_HEADER) {
_print_header(state.object);
}
if (state.dump_flags & PRINT_SECTIONS) {
_print_section_list(state.object);
}
if (state.dump_flags & PRINT_SYMTBL) {
_print_symbol_table(state.object);
}
if (state.dump_flags & PRINT_BINARY) {
_print_binary(state.file->file, state.file->size);
}
return 0;
}
Detected encoding: UTF-8 | 0
|