#ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include "mcpu-archive.h" #include "mcpu-melf.h" #include "mcpu-version.h" typedef struct mcpu_objdump_shdr { __mpu_uint32_t name; __mpu_uint32_t type; __mpu_uint64_t flags; __mpu_uint64_t addr; __mpu_uint64_t offset; __mpu_uint64_t size; __mpu_uint32_t link; __mpu_uint32_t info; __mpu_uint64_t align; __mpu_uint64_t entsize; } mcpu_objdump_shdr; typedef struct mcpu_objdump_file { const char *name; const unsigned char *data; size_t size; int encoding; __mpu_uint16_t type; __mpu_uint16_t shnum; __mpu_uint16_t shstrndx; mcpu_objdump_shdr *shdr; const unsigned char *shstr; size_t shstr_size; } mcpu_objdump_file; typedef struct mcpu_objdump_options { int disassemble; int disassemble_all; int relocations; int full_contents; int symbols; int dynamic_symbols; int section_headers; char **sections; size_t section_count; } mcpu_objdump_options; static void usage( FILE *fp, const char *name ) { fprintf( fp, "usage: %s [options] MELF-file(s)\n\n" "Display and disassemble LibMCPU MELF objects, executables, shared objects and archives.\n\n" "Options:\n" " -d, --disassemble - disassemble executable sections\n" " -D, --disassemble-all - disassemble all PROGBITS sections\n" " -r, --reloc - display relocation entries\n" " -s, --full-contents - display full contents of sections\n" " -t, --syms - display .symtab symbols\n" " -T, --dynamic-syms - display .dynsym symbols\n" " -h, --section-headers - display section headers\n" " -j, --section=NAME - restrict output to section NAME (repeatable)\n" " --help - show this help\n" " --version - show program version\n" " --dump-version - show version number only\n", name ); } /* End of usage() */ static void objdump_file_free( mcpu_objdump_file *file ) { if( !file ) return; free( file->shdr ); memset( file, 0, sizeof( *file ) ); } /* End of objdump_file_free() */ static void get_shdr( const unsigned char *p, int encoding, mcpu_objdump_shdr *sh ) { memset( sh, 0, sizeof( *sh ) ); sh->name = cpu_melf_get32( p + MELF64_SHDR_SH_NAME_OFF, encoding ); sh->type = cpu_melf_get32( p + MELF64_SHDR_SH_TYPE_OFF, encoding ); sh->flags = cpu_melf_get64( p + MELF64_SHDR_SH_FLAGS_OFF, encoding ); sh->addr = cpu_melf_get64( p + MELF64_SHDR_SH_ADDR_OFF, encoding ); sh->offset = cpu_melf_get64( p + MELF64_SHDR_SH_OFFSET_OFF, encoding ); sh->size = cpu_melf_get64( p + MELF64_SHDR_SH_SIZE_OFF, encoding ); sh->link = cpu_melf_get32( p + MELF64_SHDR_SH_LINK_OFF, encoding ); sh->info = cpu_melf_get32( p + MELF64_SHDR_SH_INFO_OFF, encoding ); sh->align = cpu_melf_get64( p + MELF64_SHDR_SH_ADDRALIGN_OFF, encoding ); sh->entsize = cpu_melf_get64( p + MELF64_SHDR_SH_ENTSIZE_OFF, encoding ); } /* End of get_shdr() */ static const char * string_at( const unsigned char *table, size_t size, __mpu_uint64_t offset ) { const unsigned char *p; if( !table || offset >= size ) return( NULL ); p = table + (size_t)offset; if( !memchr( p, '\0', size - (size_t)offset ) ) return( NULL ); return( (const char *)p ); } /* End of string_at() */ static int objdump_file_open_data( const char *name, const unsigned char *data, size_t size, mcpu_objdump_file *file, char *err, size_t errsz ) { __mpu_uint64_t shoff; __mpu_uint16_t shentsize; size_t i; memset( file, 0, sizeof( *file ) ); file->name = name; file->data = data; file->size = size; if( size < MELF64_EHDR_SIZE ) { snprintf( err, errsz, "file is too small for a MELF64 header" ); return( -1 ); } if( data[MELF_EI_MAG0] != MELFMAG0 || data[MELF_EI_MAG1] != MELFMAG1 || data[MELF_EI_MAG2] != MELFMAG2 || data[MELF_EI_MAG3] != MELFMAG3 ) { snprintf( err, errsz, "not a MELF file" ); return( -1 ); } if( data[MELF_EI_CLASS] != MELF_CLASS_64 ) { snprintf( err, errsz, "unsupported MELF class %u", (unsigned int)data[MELF_EI_CLASS] ); return( -1 ); } file->encoding = data[MELF_EI_DATA]; if( file->encoding != MELF_DATA_LSB && file->encoding != MELF_DATA_MSB ) { snprintf( err, errsz, "invalid MELF data encoding %d", file->encoding ); return( -1 ); } if( cpu_melf_get16( data + MELF64_EHDR_E_EHSIZE_OFF, file->encoding ) != MELF64_EHDR_SIZE ) { snprintf( err, errsz, "unsupported MELF64 header size" ); return( -1 ); } file->type = cpu_melf_get16( data + MELF64_EHDR_E_TYPE_OFF, file->encoding ); shoff = cpu_melf_get64( data + MELF64_EHDR_E_SHOFF_OFF, file->encoding ); shentsize = cpu_melf_get16( data + MELF64_EHDR_E_SHENTSIZE_OFF, file->encoding ); file->shnum = cpu_melf_get16( data + MELF64_EHDR_E_SHNUM_OFF, file->encoding ); file->shstrndx = cpu_melf_get16( data + MELF64_EHDR_E_SHSTRNDX_OFF, file->encoding ); if( !file->shnum ) return( 0 ); if( shentsize != MELF64_SHDR_SIZE || !cpu_melf_span( size, shoff, (__mpu_uint64_t)file->shnum * MELF64_SHDR_SIZE ) ) { snprintf( err, errsz, "malformed section-header table" ); return( -1 ); } file->shdr = (mcpu_objdump_shdr *)calloc( file->shnum, sizeof( *file->shdr ) ); if( !file->shdr ) { snprintf( err, errsz, "out of memory" ); return( -1 ); } for( i = 0; i < file->shnum; ++i ) get_shdr( data + (size_t)shoff + i * MELF64_SHDR_SIZE, file->encoding, &file->shdr[i] ); if( file->shstrndx >= file->shnum ) { snprintf( err, errsz, "invalid section-name string table index" ); goto fail; } if( file->shdr[file->shstrndx].type != MELF_SHT_STRTAB || file->shdr[file->shstrndx].size > SIZE_MAX || !cpu_melf_span( size, file->shdr[file->shstrndx].offset, file->shdr[file->shstrndx].size ) ) { snprintf( err, errsz, "malformed section-name string table" ); goto fail; } file->shstr = data + (size_t)file->shdr[file->shstrndx].offset; file->shstr_size = (size_t)file->shdr[file->shstrndx].size; for( i = 0; i < file->shnum; ++i ) { if( !string_at( file->shstr, file->shstr_size, file->shdr[i].name ) ) { snprintf( err, errsz, "section has invalid name offset" ); goto fail; } if( file->shdr[i].type != MELF_SHT_NOBITS && file->shdr[i].size && !cpu_melf_span( size, file->shdr[i].offset, file->shdr[i].size ) ) { snprintf( err, errsz, "section payload is outside file" ); goto fail; } } return( 0 ); fail: objdump_file_free( file ); return( -1 ); } /* End of objdump_file_open_data() */ static const char * section_name( const mcpu_objdump_file *file, size_t index ) { if( !file || index >= file->shnum ) return( "" ); return( string_at( file->shstr, file->shstr_size, file->shdr[index].name ) ); } /* End of section_name() */ static int section_selected( const mcpu_objdump_file *file, size_t index, const mcpu_objdump_options *options ) { const char *name; size_t i; if( !options->section_count ) return( 1 ); name = section_name( file, index ); for( i = 0; i < options->section_count; ++i ) if( !strcmp( name, options->sections[i] ) ) return( 1 ); return( 0 ); } /* End of section_selected() */ static const char * section_type_name( __mpu_uint32_t type ) { switch( type ) { case MELF_SHT_NULL: return( "NULL" ); case MELF_SHT_PROGBITS: return( "PROGBITS" ); case MELF_SHT_SYMTAB: return( "SYMTAB" ); case MELF_SHT_STRTAB: return( "STRTAB" ); case MELF_SHT_RELA: return( "RELA" ); case MELF_SHT_DYNAMIC: return( "DYNAMIC" ); case MELF_SHT_NOBITS: return( "NOBITS" ); case MELF_SHT_DYNSYM: return( "DYNSYM" ); case MELF_SHT_MCPU_TARGET: return( "MCPU_TARGET" ); case MELF_SHT_MCPU_LIFECYCLE: return( "MCPU_LIFECYCLE" ); default: return( "" ); } } /* End of section_type_name() */ static const char * symbol_bind_name( unsigned int bind ) { switch( bind ) { case MELF_STB_LOCAL: return( "LOCAL" ); case MELF_STB_GLOBAL: return( "GLOBAL" ); case MELF_STB_WEAK: return( "WEAK" ); default: return( "" ); } } /* End of symbol_bind_name() */ static const char * symbol_type_name( unsigned int type ) { switch( type ) { case MELF_STT_NOTYPE: return( "NOTYPE" ); case MELF_STT_OBJECT: return( "OBJECT" ); case MELF_STT_FUNC: return( "FUNC" ); case MELF_STT_SECTION: return( "SECTION" ); case MELF_STT_FILE: return( "FILE" ); default: return( "" ); } } /* End of symbol_type_name() */ static const char * relocation_type_name( unsigned int type ) { switch( type ) { case MELF_R_MCPU_NONE: return( "R_MCPU_NONE" ); case MELF_R_MCPU_CODE64: return( "R_MCPU_CODE64" ); case MELF_R_MCPU_DATA64: return( "R_MCPU_DATA64" ); case MELF_R_MCPU_PTR64_CODE: return( "R_MCPU_PTR64_CODE" ); case MELF_R_MCPU_PTR64_DATA: return( "R_MCPU_PTR64_DATA" ); case MELF_R_MCPU_CPTR64_DATA: return( "R_MCPU_CPTR64_DATA" ); default: return( "" ); } } /* End of relocation_type_name() */ static const char * opcode_name( cpu_opcode opcode ) { switch( opcode ) { case CPU_OP_NOP: return( "NOP" ); case CPU_OP_HALT: return( "HALT" ); case CPU_OP_END: return( "END" ); case CPU_OP_EXIT: return( "EXIT" ); case CPU_OP_SYSCALL: return( "SYSCALL" ); case CPU_OP_SETF: return( "SETF" ); case CPU_OP_CLRF: return( "CLRF" ); case CPU_OP_CLRCPUF: return( "CLRCPUF" ); case CPU_OP_CLRMPUF: return( "CLRMPUF" ); case CPU_OP_MOV: return( "MOV" ); case CPU_OP_CLR: return( "CLR" ); case CPU_OP_SWAP: return( "SWAP" ); case CPU_OP_BSWAP: return( "BSWAP" ); case CPU_OP_LDI: return( "LDI" ); case CPU_OP_LDR: return( "LDR" ); case CPU_OP_LDC: return( "LDC" ); case CPU_OP_LOAD: return( "LOAD" ); case CPU_OP_STORE: return( "STORE" ); case CPU_OP_LEA: return( "LEA" ); case CPU_OP_LEASP: return( "LEASP" ); case CPU_OP_LOADP: return( "LOADP" ); case CPU_OP_STOREP: return( "STOREP" ); case CPU_OP_PADD: return( "PADD" ); case CPU_OP_PADDI: return( "PADDI" ); case CPU_OP_PSUB: return( "PSUB" ); case CPU_OP_PSUBI: return( "PSUBI" ); case CPU_OP_PDIFF: return( "PDIFF" ); case CPU_OP_BMOV: return( "BMOV" ); case CPU_OP_BMOVI: return( "BMOVI" ); case CPU_OP_IADD: return( "IADD" ); case CPU_OP_IADC: return( "IADC" ); case CPU_OP_ISUB: return( "ISUB" ); case CPU_OP_ISBB: return( "ISBB" ); case CPU_OP_IAND: return( "IAND" ); case CPU_OP_IOR: return( "IOR" ); case CPU_OP_IXOR: return( "IXOR" ); case CPU_OP_INOT: return( "INOT" ); case CPU_OP_INEG: return( "INEG" ); case CPU_OP_ICMP: return( "ICMP" ); case CPU_OP_ITEST: return( "ITEST" ); case CPU_OP_ISHL: return( "ISHL" ); case CPU_OP_ISHR: return( "ISHR" ); case CPU_OP_ISAL: return( "ISAL" ); case CPU_OP_ISAR: return( "ISAR" ); case CPU_OP_IROL: return( "IROL" ); case CPU_OP_IROR: return( "IROR" ); case CPU_OP_IRCL: return( "IRCL" ); case CPU_OP_IRCR: return( "IRCR" ); case CPU_OP_ISHLN: return( "ISHLN" ); case CPU_OP_ISHRN: return( "ISHRN" ); case CPU_OP_ISALN: return( "ISALN" ); case CPU_OP_ISARN: return( "ISARN" ); case CPU_OP_IROLN: return( "IROLN" ); case CPU_OP_IRORN: return( "IRORN" ); case CPU_OP_IRCLN: return( "IRCLN" ); case CPU_OP_IRCRN: return( "IRCRN" ); case CPU_OP_IINC: return( "IINC" ); case CPU_OP_IDEC: return( "IDEC" ); case CPU_OP_IXCHG: return( "IXCHG" ); case CPU_OP_ICPY: return( "ICPY" ); case CPU_OP_ICVT: return( "ICVT" ); case CPU_OP_IMUL: return( "IMUL" ); case CPU_OP_ISMUL: return( "ISMUL" ); case CPU_OP_IDIV: return( "IDIV" ); case CPU_OP_ISDIV: return( "ISDIV" ); case CPU_OP_IBITSLEN: return( "IBITSLEN" ); case CPU_OP_RADD: return( "RADD" ); case CPU_OP_RSUB: return( "RSUB" ); case CPU_OP_RMUL: return( "RMUL" ); case CPU_OP_RDIV: return( "RDIV" ); case CPU_OP_RSQRT: return( "RSQRT" ); case CPU_OP_RSIN: return( "RSIN" ); case CPU_OP_RCOS: return( "RCOS" ); case CPU_OP_RTAN: return( "RTAN" ); case CPU_OP_RASIN: return( "RASIN" ); case CPU_OP_RACOS: return( "RACOS" ); case CPU_OP_RATAN: return( "RATAN" ); case CPU_OP_RLOG: return( "RLOG" ); case CPU_OP_RLOG10: return( "RLOG10" ); case CPU_OP_RLOG2: return( "RLOG2" ); case CPU_OP_REXP: return( "REXP" ); case CPU_OP_RSINH: return( "RSINH" ); case CPU_OP_RCOSH: return( "RCOSH" ); case CPU_OP_RTANH: return( "RTANH" ); case CPU_OP_RPOW: return( "RPOW" ); case CPU_OP_RHYPOT: return( "RHYPOT" ); case CPU_OP_RATAN2: return( "RATAN2" ); case CPU_OP_RNEG: return( "RNEG" ); case CPU_OP_RABS: return( "RABS" ); case CPU_OP_RCMP: return( "RCMP" ); case CPU_OP_RCPY: return( "RCPY" ); case CPU_OP_RCVT: return( "RCVT" ); case CPU_OP_RFLOOR: return( "RFLOOR" ); case CPU_OP_RCEIL: return( "RCEIL" ); case CPU_OP_RROUND: return( "RROUND" ); case CPU_OP_RFREXP: return( "RFREXP" ); case CPU_OP_RLDEXP: return( "RLDEXP" ); case CPU_OP_RLOGB: return( "RLOGB" ); case CPU_OP_RMODF: return( "RMODF" ); case CPU_OP_RREMAIN: return( "RREMAIN" ); case CPU_OP_RLOG1P: return( "RLOG1P" ); case CPU_OP_REXPM1: return( "REXPM1" ); case CPU_OP_RASINH: return( "RASINH" ); case CPU_OP_RACOSH: return( "RACOSH" ); case CPU_OP_RATANH: return( "RATANH" ); case CPU_OP_ITOR: return( "ITOR" ); case CPU_OP_UITOR: return( "UITOR" ); case CPU_OP_RTOI: return( "RTOI" ); case CPU_OP_RTOUI: return( "RTOUI" ); case CPU_OP_RISNEG: return( "RISNEG" ); case CPU_OP_RSIGN: return( "RSIGN" ); case CPU_OP_RISIND: return( "RISIND" ); case CPU_OP_RISNAN: return( "RISNAN" ); case CPU_OP_RISNANMAX: return( "RISNANMAX" ); case CPU_OP_RISNANMIN: return( "RISNANMIN" ); case CPU_OP_RISINF: return( "RISINF" ); case CPU_OP_RISSIGNULL: return( "RISSIGNULL" ); case CPU_OP_RCONST: return( "RCONST" ); case CPU_OP_CADD: return( "CADD" ); case CPU_OP_CSUB: return( "CSUB" ); case CPU_OP_CMUL: return( "CMUL" ); case CPU_OP_CDIV: return( "CDIV" ); case CPU_OP_CEXP: return( "CEXP" ); case CPU_OP_CSIN: return( "CSIN" ); case CPU_OP_CCOS: return( "CCOS" ); case CPU_OP_CSINH: return( "CSINH" ); case CPU_OP_CCOSH: return( "CCOSH" ); case CPU_OP_CLOG: return( "CLOG" ); case CPU_OP_CSQRT: return( "CSQRT" ); case CPU_OP_CPOW: return( "CPOW" ); case CPU_OP_CREAL: return( "CREAL" ); case CPU_OP_CIMAG: return( "CIMAG" ); case CPU_OP_CRTOC: return( "CRTOC" ); case CPU_OP_CGEN: return( "CGEN" ); case CPU_OP_CPOLAR: return( "CPOLAR" ); case CPU_OP_CCONJ: return( "CCONJ" ); case CPU_OP_CABS: return( "CABS" ); case CPU_OP_CNORM: return( "CNORM" ); case CPU_OP_CARG: return( "CARG" ); case CPU_OP_CISEQ: return( "CISEQ" ); case CPU_OP_CISNE: return( "CISNE" ); case CPU_OP_CCPY: return( "CCPY" ); case CPU_OP_CCVT: return( "CCVT" ); case CPU_OP_JMP: return( "JMP" ); case CPU_OP_JSET: return( "JSET" ); case CPU_OP_JCLR: return( "JCLR" ); case CPU_OP_JMPP: return( "JMPP" ); case CPU_OP_CALL: return( "CALL" ); case CPU_OP_RET: return( "RET" ); case CPU_OP_CALLP: return( "CALLP" ); case CPU_OP_PUSH: return( "PUSH" ); case CPU_OP_POP: return( "POP" ); case CPU_OP_LDSP: return( "LDSP" ); case CPU_OP_STSP: return( "STSP" ); case CPU_OP_ADJSP: return( "ADJSP" ); case CPU_OP_PUSHFP: return( "PUSHFP" ); case CPU_OP_SETFP: return( "SETFP" ); case CPU_OP_RESTSP: return( "RESTSP" ); case CPU_OP_POPFP: return( "POPFP" ); case CPU_OP_LDFP: return( "LDFP" ); case CPU_OP_STFP: return( "STFP" ); case CPU_OP_LEAFP: return( "LEAFP" ); case CPU_OP_SETSP: return( "SETSP" ); default: return( NULL ); } } /* End of opcode_name() */ static const char * rconst_name( unsigned int value ) { switch( value ) { case CPU_RCONST_ZERO: return( "ZERO" ); case CPU_RCONST_HALF: return( "HALF" ); case CPU_RCONST_ONE: return( "ONE" ); case CPU_RCONST_TWO: return( "TWO" ); case CPU_RCONST_TEN: return( "TEN" ); case CPU_RCONST_MTEN: return( "MTEN" ); case CPU_RCONST_32: return( "32" ); case CPU_RCONST_PI: return( "PI" ); case CPU_RCONST_E: return( "E" ); case CPU_RCONST_1_LN2: return( "1_LN2" ); case CPU_RCONST_LN2: return( "LN2" ); case CPU_RCONST_1_LN10: return( "1_LN10" ); case CPU_RCONST_LN10: return( "LN10" ); case CPU_RCONST_1_LG2: return( "1_LG2" ); case CPU_RCONST_LG2: return( "LG2" ); case CPU_RCONST_PI_2: return( "PI_2" ); case CPU_RCONST_PI_3: return( "PI_3" ); case CPU_RCONST_PI_4: return( "PI_4" ); case CPU_RCONST_PI_5: return( "PI_5" ); case CPU_RCONST_PI_6: return( "PI_6" ); case CPU_RCONST_1_PI: return( "1_PI" ); case CPU_RCONST_2_PI: return( "2_PI" ); case CPU_RCONST_3_PI: return( "3_PI" ); case CPU_RCONST_4_PI: return( "4_PI" ); case CPU_RCONST_5_PI: return( "5_PI" ); case CPU_RCONST_2PI: return( "2PI" ); case CPU_RCONST_3PI: return( "3PI" ); case CPU_RCONST_1_2PI: return( "1_2PI" ); case CPU_RCONST_1_3PI: return( "1_3PI" ); case CPU_RCONST_1_4PI: return( "1_4PI" ); case CPU_RCONST_1_5PI: return( "1_5PI" ); case CPU_RCONST_1_6PI: return( "1_6PI" ); case CPU_RCONST_3PI_4: return( "3PI_4" ); case CPU_RCONST_SQRTPI: return( "SQRTPI" ); case CPU_RCONST_1_SQRTPI: return( "1_SQRTPI" ); case CPU_RCONST_2_SQRTPI: return( "2_SQRTPI" ); case CPU_RCONST_SQRT2: return( "SQRT2" ); case CPU_RCONST_1_SQRT2: return( "1_SQRT2" ); case CPU_RCONST_SQRT3: return( "SQRT3" ); case CPU_RCONST_1_SQRT3: return( "1_SQRT3" ); case CPU_RCONST_DEGREE: return( "DEGREE" ); case CPU_RCONST_1_DEGREE: return( "1_DEGREE" ); case CPU_RCONST_GOLDENRATIO: return( "GOLDENRATIO" ); case CPU_RCONST_EULERGAMMA: return( "EULERGAMMA" ); case CPU_RCONST_CATALAN: return( "CATALAN" ); case CPU_RCONST_EPSILON: return( "EPSILON" ); case CPU_RCONST_IND: return( "IND" ); case CPU_RCONST_PNAN: return( "PNAN" ); case CPU_RCONST_NNAN: return( "NNAN" ); case CPU_RCONST_PNANMAX: return( "PNANMAX" ); case CPU_RCONST_NNANMAX: return( "NNANMAX" ); case CPU_RCONST_PNANMIN: return( "PNANMIN" ); case CPU_RCONST_NNANMIN: return( "NNANMIN" ); case CPU_RCONST_PINF: return( "PINF" ); case CPU_RCONST_NINF: return( "NINF" ); case CPU_RCONST_PREALMIN: return( "PREALMIN" ); case CPU_RCONST_NREALMIN: return( "NREALMIN" ); case CPU_RCONST_PREALMAX: return( "PREALMAX" ); case CPU_RCONST_NREALMAX: return( "NREALMAX" ); case CPU_RCONST_PZERO: return( "PZERO" ); default: return( NULL ); } } /* End of rconst_name() */ static const char * pswbit_name( unsigned int value ) { switch( value ) { case CPU_PSWBIT_AF: return( "AF" ); case CPU_PSWBIT_CF: return( "CF" ); case CPU_PSWBIT_OF: return( "OF" ); case CPU_PSWBIT_SF: return( "SF" ); case CPU_PSWBIT_PF: return( "PF" ); case CPU_PSWBIT_ZF: return( "ZF" ); case CPU_PSWBIT_RF: return( "RF" ); case CPU_PSWBIT_VF: return( "VF" ); case CPU_PSWBIT_DOMF: return( "DOMF" ); case CPU_PSWBIT_SNGF: return( "SNGF" ); case CPU_PSWBIT_OVFF: return( "OVFF" ); case CPU_PSWBIT_UDFF: return( "UDFF" ); case CPU_PSWBIT_TLSF: return( "TLSF" ); case CPU_PSWBIT_PLSF: return( "PLSF" ); case CPU_PSWBIT_INDF: return( "INDF" ); case CPU_PSWBIT_INXF: return( "INXF" ); case CPU_PSWBIT_HF: return( "HF" ); case CPU_PSWBIT_TF: return( "TF" ); case CPU_PSWBIT_IF: return( "IF" ); default: return( NULL ); } } /* End of pswbit_name() */ static int opcode_complex_operand( cpu_opcode opcode, unsigned int operand ) { switch( opcode ) { case CPU_OP_LDC: return( operand == 0 ); case CPU_OP_CADD: case CPU_OP_CSUB: case CPU_OP_CMUL: case CPU_OP_CDIV: case CPU_OP_CPOW: return( operand < 3 ); case CPU_OP_CEXP: case CPU_OP_CSIN: case CPU_OP_CCOS: case CPU_OP_CSINH: case CPU_OP_CCOSH: case CPU_OP_CLOG: case CPU_OP_CSQRT: case CPU_OP_CCONJ: case CPU_OP_CCPY: case CPU_OP_CCVT: return( operand < 2 ); case CPU_OP_CREAL: case CPU_OP_CIMAG: case CPU_OP_CABS: case CPU_OP_CNORM: case CPU_OP_CARG: return( operand == 1 ); case CPU_OP_CRTOC: return( operand == 0 ); case CPU_OP_CGEN: case CPU_OP_CPOLAR: return( operand == 0 ); case CPU_OP_CISEQ: case CPU_OP_CISNE: return( operand == 1 || operand == 2 ); default: return( 0 ); } } /* End of opcode_complex_operand() */ static __mpu_uint64_t symbol_section_offset( const mcpu_objdump_file *file, const mcpu_objdump_shdr *section, __mpu_uint64_t value ) { if( file->type == MELF_ET_EXEC && MELF_SHF_MCPU_HAS_SEG( section->flags ) ) { __mpu_uint64_t base = MELF_MCPU_PTR64_OFFSET( section->addr ); __mpu_uint64_t pos = MELF_MCPU_PTR64_OFFSET( value ); if( pos >= base ) return( pos - base ); } return( value ); } /* End of symbol_section_offset() */ static const char * symbol_at( const mcpu_objdump_file *file, size_t section_index, __mpu_uint64_t offset ) { unsigned int pass; for( pass = 0; pass < 2; ++pass ) { __mpu_uint32_t wanted = pass == 0 ? MELF_SHT_SYMTAB : MELF_SHT_DYNSYM; size_t i; for( i = 0; i < file->shnum; ++i ) { const mcpu_objdump_shdr *sh = &file->shdr[i]; const mcpu_objdump_shdr *str; const unsigned char *strtab; size_t strsize; size_t count; size_t k; if( sh->type != wanted ) continue; if( sh->entsize != MELF64_SYM_SIZE || sh->size % MELF64_SYM_SIZE || sh->link >= file->shnum ) continue; str = &file->shdr[sh->link]; if( str->type != MELF_SHT_STRTAB || str->size > SIZE_MAX || !cpu_melf_span( file->size, str->offset, str->size ) ) continue; strtab = file->data + (size_t)str->offset; strsize = (size_t)str->size; count = (size_t)(sh->size / MELF64_SYM_SIZE); for( k = 1; k < count; ++k ) { const unsigned char *p = file->data + (size_t)sh->offset + k * MELF64_SYM_SIZE; __mpu_uint16_t shndx = cpu_melf_get16( p + MELF64_SYM_ST_SHNDX_OFF, file->encoding ); __mpu_uint64_t value = cpu_melf_get64( p + MELF64_SYM_ST_VALUE_OFF, file->encoding ); __mpu_uint32_t name_off = cpu_melf_get32( p + MELF64_SYM_ST_NAME_OFF, file->encoding ); const char *name; if( shndx != section_index ) continue; if( symbol_section_offset( file, &file->shdr[section_index], value ) != offset ) continue; name = string_at( strtab, strsize, name_off ); if( name && *name ) return( name ); } } } return( NULL ); } /* End of symbol_at() */ static const char * code_symbol_at( const mcpu_objdump_file *file, __mpu_uint64_t target ) { unsigned int pass; if( file->type == MELF_ET_REL ) return( NULL ); for( pass = 0; pass < 2; ++pass ) { __mpu_uint32_t wanted = pass == 0 ? MELF_SHT_SYMTAB : MELF_SHT_DYNSYM; size_t i; for( i = 0; i < file->shnum; ++i ) { const mcpu_objdump_shdr *sh = &file->shdr[i]; const mcpu_objdump_shdr *str; const unsigned char *strtab; size_t strsize; size_t count; size_t k; if( sh->type != wanted ) continue; if( sh->entsize != MELF64_SYM_SIZE || sh->size % MELF64_SYM_SIZE || sh->link >= file->shnum ) continue; str = &file->shdr[sh->link]; if( str->type != MELF_SHT_STRTAB || str->size > SIZE_MAX || !cpu_melf_span( file->size, str->offset, str->size ) ) continue; strtab = file->data + (size_t)str->offset; strsize = (size_t)str->size; count = (size_t)(sh->size / MELF64_SYM_SIZE); for( k = 1; k < count; ++k ) { const unsigned char *p = file->data + (size_t)sh->offset + k * MELF64_SYM_SIZE; __mpu_uint16_t shndx = cpu_melf_get16( p + MELF64_SYM_ST_SHNDX_OFF, file->encoding ); __mpu_uint64_t value = cpu_melf_get64( p + MELF64_SYM_ST_VALUE_OFF, file->encoding ); __mpu_uint32_t name_off = cpu_melf_get32( p + MELF64_SYM_ST_NAME_OFF, file->encoding ); __mpu_uint64_t code_value; const char *name; if( shndx >= file->shnum || !(file->shdr[shndx].flags & MELF_SHF_EXECINSTR) ) continue; if( file->type == MELF_ET_EXEC ) code_value = MELF_MCPU_PTR64_OFFSET( value ); else if( MELF_SHF_MCPU_HAS_SEG( file->shdr[shndx].flags ) ) code_value = MELF_MCPU_PTR64_OFFSET( file->shdr[shndx].addr ) + value; else code_value = value; if( code_value != target ) continue; name = string_at( strtab, strsize, name_off ); if( name && *name ) return( name ); } } } return( NULL ); } /* End of code_symbol_at() */ static void print_section_headers( const mcpu_objdump_file *file, const mcpu_objdump_options *options ) { size_t i; printf( "Sections:\n" ); printf( "Idx Name Size VMA " "File off Algn Type\n" ); for( i = 0; i < file->shnum; ++i ) { const mcpu_objdump_shdr *sh = &file->shdr[i]; if( !section_selected( file, i, options ) ) continue; printf( "%3zu %-20s %016" PRIx64 " %016" PRIx64 " %016" PRIx64 " %4" PRIu64 " %s", i, section_name( file, i ), (uint64_t)sh->size, (uint64_t)sh->addr, (uint64_t)sh->offset, (uint64_t)sh->align, section_type_name( sh->type ) ); if( sh->flags & MELF_SHF_ALLOC ) printf( " ALLOC" ); if( sh->flags & MELF_SHF_WRITE ) printf( " WRITE" ); if( sh->flags & MELF_SHF_EXECINSTR ) printf( " CODE" ); printf( "\n" ); } } /* End of print_section_headers() */ static int print_symbol_table( const mcpu_objdump_file *file, __mpu_uint32_t table_type, const mcpu_objdump_options *options, char *err, size_t errsz ) { size_t i; int printed = 0; for( i = 0; i < file->shnum; ++i ) { const mcpu_objdump_shdr *sh = &file->shdr[i]; const mcpu_objdump_shdr *str; const unsigned char *strtab; size_t strsize; size_t count; size_t k; if( sh->type != table_type ) continue; if( sh->entsize != MELF64_SYM_SIZE || sh->size % MELF64_SYM_SIZE || sh->size > SIZE_MAX || !cpu_melf_span( file->size, sh->offset, sh->size ) || sh->link >= file->shnum ) { snprintf( err, errsz, "malformed symbol table" ); return( -1 ); } str = &file->shdr[sh->link]; if( str->type != MELF_SHT_STRTAB || str->size > SIZE_MAX || !cpu_melf_span( file->size, str->offset, str->size ) ) { snprintf( err, errsz, "malformed symbol string table" ); return( -1 ); } strtab = file->data + (size_t)str->offset; strsize = (size_t)str->size; count = (size_t)(sh->size / MELF64_SYM_SIZE); if( !printed ) { printf( "%sSYMBOL TABLE:\n", table_type == MELF_SHT_DYNSYM ? "DYNAMIC " : "" ); printf( "Value Size Bind Type Ndx Name\n" ); printed = 1; } for( k = 1; k < count; ++k ) { const unsigned char *p = file->data + (size_t)sh->offset + k * MELF64_SYM_SIZE; __mpu_uint32_t name_off = cpu_melf_get32( p + MELF64_SYM_ST_NAME_OFF, file->encoding ); unsigned int bind = MELF64_ST_BIND( p[MELF64_SYM_ST_INFO_OFF] ); unsigned int type = MELF64_ST_TYPE( p[MELF64_SYM_ST_INFO_OFF] ); __mpu_uint16_t shndx = cpu_melf_get16( p + MELF64_SYM_ST_SHNDX_OFF, file->encoding ); __mpu_uint64_t value = cpu_melf_get64( p + MELF64_SYM_ST_VALUE_OFF, file->encoding ); __mpu_uint64_t ssize = cpu_melf_get64( p + MELF64_SYM_ST_SIZE_OFF, file->encoding ); const char *name = string_at( strtab, strsize, name_off ); char ndx[32]; if( !name ) { snprintf( err, errsz, "symbol has invalid name offset" ); return( -1 ); } if( shndx < file->shnum && !section_selected( file, shndx, options ) ) continue; if( shndx == MELF_SHN_UNDEF ) strcpy( ndx, "UND" ); else if( shndx == MELF_SHN_ABS ) strcpy( ndx, "ABS" ); else if( shndx == MELF_SHN_COMMON ) strcpy( ndx, "COM" ); else snprintf( ndx, sizeof( ndx ), "%u", (unsigned int)shndx ); printf( "%016" PRIx64 " %8" PRIu64 " %-6s %-8s %3s %s\n", (uint64_t)value, (uint64_t)ssize, symbol_bind_name( bind ), symbol_type_name( type ), ndx, name ); } } return( 0 ); } /* End of print_symbol_table() */ static int relocation_symbol( const mcpu_objdump_file *file, const mcpu_objdump_shdr *rela, __mpu_uint64_t symidx, const char **name, char *err, size_t errsz ) { const mcpu_objdump_shdr *symtab; const mcpu_objdump_shdr *str; const unsigned char *p; const unsigned char *strtab; size_t count; __mpu_uint32_t name_off; *name = ""; if( rela->link >= file->shnum ) { snprintf( err, errsz, "relocation section has invalid symbol-table link" ); return( -1 ); } symtab = &file->shdr[rela->link]; if( (symtab->type != MELF_SHT_SYMTAB && symtab->type != MELF_SHT_DYNSYM) || symtab->entsize != MELF64_SYM_SIZE || symtab->size % MELF64_SYM_SIZE || symtab->link >= file->shnum ) { snprintf( err, errsz, "relocation section has malformed symbol table" ); return( -1 ); } count = (size_t)(symtab->size / MELF64_SYM_SIZE); if( symidx >= count ) { snprintf( err, errsz, "relocation has invalid symbol index" ); return( -1 ); } str = &file->shdr[symtab->link]; if( str->type != MELF_SHT_STRTAB || str->size > SIZE_MAX || !cpu_melf_span( file->size, str->offset, str->size ) ) { snprintf( err, errsz, "relocation symbol table has malformed strings" ); return( -1 ); } p = file->data + (size_t)symtab->offset + (size_t)symidx * MELF64_SYM_SIZE; name_off = cpu_melf_get32( p + MELF64_SYM_ST_NAME_OFF, file->encoding ); strtab = file->data + (size_t)str->offset; *name = string_at( strtab, (size_t)str->size, name_off ); if( !*name ) { snprintf( err, errsz, "relocation symbol has invalid name offset" ); return( -1 ); } return( 0 ); } /* End of relocation_symbol() */ static int print_relocations( const mcpu_objdump_file *file, const mcpu_objdump_options *options, int skip_disassembled, char *err, size_t errsz ) { size_t i; for( i = 0; i < file->shnum; ++i ) { const mcpu_objdump_shdr *sh = &file->shdr[i]; size_t count; size_t k; if( sh->type != MELF_SHT_RELA ) continue; if( sh->info >= file->shnum ) { snprintf( err, errsz, "relocation section has invalid target section" ); return( -1 ); } if( !section_selected( file, sh->info, options ) ) continue; if( skip_disassembled ) { const mcpu_objdump_shdr *target = &file->shdr[sh->info]; int eligible; if( options->disassemble_all ) eligible = target->type == MELF_SHT_PROGBITS && target->size; else eligible = target->type == MELF_SHT_PROGBITS && target->size && (target->flags & MELF_SHF_EXECINSTR) != 0; if( eligible ) continue; } if( sh->entsize != MELF64_RELA_SIZE || sh->size % MELF64_RELA_SIZE || sh->size > SIZE_MAX || !cpu_melf_span( file->size, sh->offset, sh->size ) ) { snprintf( err, errsz, "malformed relocation section" ); return( -1 ); } count = (size_t)(sh->size / MELF64_RELA_SIZE); printf( "RELOCATION RECORDS FOR [%s]:\n", section_name( file, sh->info ) ); printf( "OFFSET TYPE VALUE\n" ); for( k = 0; k < count; ++k ) { const unsigned char *p = file->data + (size_t)sh->offset + k * MELF64_RELA_SIZE; __mpu_uint64_t offset = cpu_melf_get64( p + MELF64_RELA_R_OFFSET_OFF, file->encoding ); __mpu_uint64_t info = cpu_melf_get64( p + MELF64_RELA_R_INFO_OFF, file->encoding ); __mpu_uint64_t addbits = cpu_melf_get64( p + MELF64_RELA_R_ADDEND_OFF, file->encoding ); __mpu_int64_t addend; __mpu_uint64_t symidx = MELF64_R_SYM( info ); unsigned int rtype = MELF64_R_TYPE( info ); const char *name; memcpy( &addend, &addbits, sizeof( addend ) ); if( relocation_symbol( file, sh, symidx, &name, err, errsz ) ) return( -1 ); printf( "%016" PRIx64 " %-23s %s", (uint64_t)offset, relocation_type_name( rtype ), name ); if( addend > 0 ) printf( "+0x%" PRIx64, (uint64_t)addend ); else if( addend < 0 ) { __mpu_uint64_t mag = (__mpu_uint64_t)(-(addend + 1)) + __mpu_UINT64_C( 1 ); printf( "-0x%" PRIx64, (uint64_t)mag ); } printf( "\n" ); } printf( "\n" ); } return( 0 ); } /* End of print_relocations() */ static void print_contents( const mcpu_objdump_file *file, const mcpu_objdump_options *options ) { size_t i; for( i = 0; i < file->shnum; ++i ) { const mcpu_objdump_shdr *sh = &file->shdr[i]; size_t off; if( !section_selected( file, i, options ) || sh->type == MELF_SHT_NULL || sh->type == MELF_SHT_NOBITS || !sh->size ) continue; printf( "Contents of section %s:\n", section_name( file, i ) ); for( off = 0; off < (size_t)sh->size; off += 16 ) { size_t k; size_t n = (size_t)sh->size - off; __mpu_uint64_t addr = sh->addr + off; if( n > 16 ) n = 16; printf( " %016" PRIx64 " ", (uint64_t)addr ); for( k = 0; k < 16; ++k ) { if( k < n ) printf( "%02x", file->data[(size_t)sh->offset + off + k] ); else printf( " " ); if( (k & 3U) == 3U ) putchar( ' ' ); } putchar( ' ' ); for( k = 0; k < n; ++k ) { unsigned char ch = file->data[(size_t)sh->offset + off + k]; putchar( isprint( ch ) ? ch : '.' ); } putchar( '\n' ); } printf( "\n" ); } } /* End of print_contents() */ static const char * instruction_relocation_symbol( const mcpu_objdump_file *file, size_t section_index, __mpu_uint64_t first, __mpu_uint64_t last ) { size_t i; for( i = 0; i < file->shnum; ++i ) { const mcpu_objdump_shdr *sh = &file->shdr[i]; size_t count; size_t k; if( sh->type != MELF_SHT_RELA || sh->info != section_index || sh->entsize != MELF64_RELA_SIZE || sh->size % MELF64_RELA_SIZE ) continue; count = (size_t)(sh->size / MELF64_RELA_SIZE); for( k = 0; k < count; ++k ) { const unsigned char *p = file->data + (size_t)sh->offset + k * MELF64_RELA_SIZE; __mpu_uint64_t off = cpu_melf_get64( p + MELF64_RELA_R_OFFSET_OFF, file->encoding ); __mpu_uint64_t info = cpu_melf_get64( p + MELF64_RELA_R_INFO_OFF, file->encoding ); const char *name; char err[128]; if( off < first || off >= last ) continue; if( !relocation_symbol( file, sh, MELF64_R_SYM( info ), &name, err, sizeof( err ) ) && name && *name ) return( name ); } } return( NULL ); } /* End of instruction_relocation_symbol() */ static void format_operand( const mcpu_objdump_file *file, size_t section_index, __mpu_uint64_t instruction_offset, __mpu_uint64_t instruction_length, cpu_opcode opcode, unsigned int index, const cpu_operand *operand, char *buf, size_t bufsz ) { const char *name; const char *relname = instruction_relocation_symbol( file, section_index, instruction_offset, instruction_offset + instruction_length ); switch( operand->type ) { case CPU_OPERAND_REGISTER: snprintf( buf, bufsz, "%c%u@0x%05" PRIx32, opcode_complex_operand( opcode, index ) ? 'c' : 'r', operand->value.reg.bits, (uint32_t)operand->value.reg.address ); break; case CPU_OPERAND_IMM64: if( opcode == CPU_OP_LDI ) { __mpu_int64_t v; memcpy( &v, &operand->value.imm64, sizeof( v ) ); snprintf( buf, bufsz, "%" PRId64, (int64_t)v ); } else if( opcode == CPU_OP_BMOVI ) snprintf( buf, bufsz, "%" PRIu64, (uint64_t)operand->value.imm64 ); else snprintf( buf, bufsz, "0x%016" PRIx64, (uint64_t)operand->value.imm64 ); break; case CPU_OPERAND_ADDR64: if( relname ) snprintf( buf, bufsz, "0x%016" PRIx64 " <%s>", (uint64_t)operand->value.addr64, relname ); else snprintf( buf, bufsz, "0x%016" PRIx64, (uint64_t)operand->value.addr64 ); break; case CPU_OPERAND_CODE64: name = relname ? relname : code_symbol_at( file, operand->value.code64 ); if( name ) snprintf( buf, bufsz, "0x%016" PRIx64 " <%s>", (uint64_t)operand->value.code64, name ); else snprintf( buf, bufsz, "0x%016" PRIx64, (uint64_t)operand->value.code64 ); break; case CPU_OPERAND_OFF64: snprintf( buf, bufsz, "%" PRId64, (int64_t)operand->value.off64 ); break; case CPU_OPERAND_CONST8: name = rconst_name( operand->value.const8 ); if( name ) snprintf( buf, bufsz, "%s", name ); else snprintf( buf, bufsz, "0x%02x", (unsigned int)operand->value.const8 ); break; case CPU_OPERAND_PSWBIT8: name = pswbit_name( operand->value.pswbit8 ); if( name ) snprintf( buf, bufsz, "%s", name ); else snprintf( buf, bufsz, "%u", (unsigned int)operand->value.pswbit8 ); break; case CPU_OPERAND_PTR64: if( relname ) snprintf( buf, bufsz, "0x%016" PRIx64 " <%s>", (uint64_t)operand->value.ptr64, relname ); else snprintf( buf, bufsz, "0x%016" PRIx64, (uint64_t)operand->value.ptr64 ); break; default: *buf = 0; break; } } /* End of format_operand() */ static int print_instruction_relocations( const mcpu_objdump_file *file, size_t section_index, __mpu_uint64_t first, __mpu_uint64_t last, char *err, size_t errsz ) { size_t i; for( i = 0; i < file->shnum; ++i ) { const mcpu_objdump_shdr *sh = &file->shdr[i]; size_t count; size_t k; if( sh->type != MELF_SHT_RELA || sh->info != section_index ) continue; if( sh->entsize != MELF64_RELA_SIZE || sh->size % MELF64_RELA_SIZE ) continue; count = (size_t)(sh->size / MELF64_RELA_SIZE); for( k = 0; k < count; ++k ) { const unsigned char *p = file->data + (size_t)sh->offset + k * MELF64_RELA_SIZE; __mpu_uint64_t off = cpu_melf_get64( p + MELF64_RELA_R_OFFSET_OFF, file->encoding ); __mpu_uint64_t info = cpu_melf_get64( p + MELF64_RELA_R_INFO_OFF, file->encoding ); __mpu_uint64_t addbits = cpu_melf_get64( p + MELF64_RELA_R_ADDEND_OFF, file->encoding ); __mpu_int64_t addend; const char *name; if( off < first || off >= last ) continue; memcpy( &addend, &addbits, sizeof( addend ) ); if( relocation_symbol( file, sh, MELF64_R_SYM( info ), &name, err, errsz ) ) return( -1 ); printf( "\t\t%016" PRIx64 ": %-23s %s", (uint64_t)off, relocation_type_name( MELF64_R_TYPE( info ) ), name ); if( addend > 0 ) printf( "+0x%" PRIx64, (uint64_t)addend ); else if( addend < 0 ) { __mpu_uint64_t mag = (__mpu_uint64_t)(-(addend + 1)) + __mpu_UINT64_C( 1 ); printf( "-0x%" PRIx64, (uint64_t)mag ); } putchar( '\n' ); } } return( 0 ); } /* End of print_instruction_relocations() */ static int disassemble_section( const mcpu_objdump_file *file, size_t section_index, const mcpu_objdump_options *options, char *err, size_t errsz ) { const mcpu_objdump_shdr *sh = &file->shdr[section_index]; const unsigned char *program = file->data + (size_t)sh->offset; __mpu_uint64_t off = 0; printf( "Disassembly of section %s:\n\n", section_name( file, section_index ) ); while( off < sh->size ) { cpu_instruction insn; const char *label = symbol_at( file, section_index, off ); int rc; if( label ) printf( "%016" PRIx64 " <%s>:\n", (uint64_t)(sh->addr + off), label ); rc = __cpu_decode_at( program, sh->size, off, &insn ); if( rc == CPU_EXEC_SUCCESS ) { const char *mn = opcode_name( insn.opcode ); unsigned int k; char operand[CPU_INSTRUCTION_MAX_OPERANDS][128]; size_t b; if( !mn || !insn.length || insn.length > sh->size - off ) rc = CPU_EXEC_DECODE_FAULT; else { printf( " %016" PRIx64 ": ", (uint64_t)(sh->addr + off) ); for( b = 0; b < 16; ++b ) { if( b < insn.length ) printf( "%02x ", program[(size_t)off + b] ); else printf( " " ); } printf( " %-10s", mn ); for( k = 0; k < CPU_INSTRUCTION_MAX_OPERANDS; ++k ) { if( insn.operand[k].type == CPU_OPERAND_NONE ) break; format_operand( file, section_index, off, insn.length, insn.opcode, k, &insn.operand[k], operand[k], sizeof( operand[k] ) ); printf( "%s%s", k ? ", " : " ", operand[k] ); } putchar( '\n' ); if( options->relocations && print_instruction_relocations( file, section_index, off, off + insn.length, err, errsz ) ) return( -1 ); off += insn.length; continue; } } printf( " %016" PRIx64 ": %02x", (uint64_t)(sh->addr + off), program[(size_t)off] ); printf( "%*s.byte 0x%02x\n", 47, "", program[(size_t)off] ); ++off; } printf( "\n" ); return( 0 ); } /* End of disassemble_section() */ static int print_disassembly( const mcpu_objdump_file *file, const mcpu_objdump_options *options, char *err, size_t errsz ) { size_t i; for( i = 0; i < file->shnum; ++i ) { const mcpu_objdump_shdr *sh = &file->shdr[i]; int eligible; if( !section_selected( file, i, options ) || !sh->size || sh->type == MELF_SHT_NOBITS ) continue; if( options->disassemble_all ) eligible = sh->type == MELF_SHT_PROGBITS; else eligible = sh->type == MELF_SHT_PROGBITS && (sh->flags & MELF_SHF_EXECINSTR) != 0; if( !eligible ) continue; if( disassemble_section( file, i, options, err, errsz ) ) return( -1 ); } return( 0 ); } /* End of print_disassembly() */ static int process_melf_data( const char *name, const unsigned char *data, size_t size, const mcpu_objdump_options *options ) { mcpu_objdump_file file; char err[256]; if( objdump_file_open_data( name, data, size, &file, err, sizeof( err ) ) ) { fprintf( stderr, "mcpu-objdump: %s: %s\n", name, err ); return( 1 ); } printf( "\n%s: file format melf64-mcpu\n\n", name ); if( options->section_headers ) print_section_headers( &file, options ); if( options->symbols ) { if( options->section_headers ) putchar( '\n' ); if( print_symbol_table( &file, MELF_SHT_SYMTAB, options, err, sizeof( err ) ) ) goto fail; } if( options->dynamic_symbols ) { if( options->section_headers || options->symbols ) putchar( '\n' ); if( print_symbol_table( &file, MELF_SHT_DYNSYM, options, err, sizeof( err ) ) ) goto fail; } if( options->full_contents ) print_contents( &file, options ); if( options->relocations ) { if( print_relocations( &file, options, options->disassemble || options->disassemble_all, err, sizeof( err ) ) ) goto fail; } if( options->disassemble || options->disassemble_all ) { if( print_disassembly( &file, options, err, sizeof( err ) ) ) goto fail; } objdump_file_free( &file ); return( 0 ); fail: fprintf( stderr, "mcpu-objdump: %s: %s\n", name, err ); objdump_file_free( &file ); return( 1 ); } /* End of process_melf_data() */ static int process_archive( const char *path, const mcpu_objdump_options *options ) { mcpu_archive archive; char err[256]; size_t i; int status = 0; mcpu_archive_init( &archive ); if( mcpu_archive_read( path, &archive, err, sizeof( err ) ) ) { fprintf( stderr, "mcpu-objdump: %s: %s\n", path, err ); return( 1 ); } for( i = 0; i < archive.member_count; ++i ) { char *display; size_t n = strlen( path ) + strlen( archive.members[i].name ) + 3; display = (char *)malloc( n ); if( !display ) { fprintf( stderr, "mcpu-objdump: %s: out of memory\n", path ); status = 1; break; } snprintf( display, n, "%s(%s)", path, archive.members[i].name ); if( process_melf_data( display, archive.members[i].data, archive.members[i].size, options ) ) status = 1; free( display ); } mcpu_archive_free( &archive ); return( status ); } /* End of process_archive() */ static int process_path( const char *path, const mcpu_objdump_options *options ) { unsigned char *data = NULL; size_t size = 0; char err[256]; int status; if( cpu_melf_readall( path, &data, &size, err, sizeof( err ) ) ) { fprintf( stderr, "mcpu-objdump: %s\n", err ); return( 1 ); } if( size >= 8 && !memcmp( data, "!\n", 8 ) ) { free( data ); return( process_archive( path, options ) ); } status = process_melf_data( path, data, size, options ); free( data ); return( status ); } /* End of process_path() */ static int add_section( mcpu_objdump_options *options, const char *name ) { char **q; char *copy; if( !name || !*name ) return( -1 ); q = (char **)realloc( options->sections, (options->section_count + 1) * sizeof( *q ) ); if( !q ) return( -1 ); copy = strdup( name ); if( !copy ) return( -1 ); options->sections = q; options->sections[options->section_count++] = copy; return( 0 ); } /* End of add_section() */ static void free_options( mcpu_objdump_options *options ) { size_t i; for( i = 0; i < options->section_count; ++i ) free( options->sections[i] ); free( options->sections ); } /* End of free_options() */ static int parse_short_options( const char *arg, mcpu_objdump_options *options, const char **section_arg ) { const char *p = arg + 1; while( *p ) { switch( *p ) { case 'd': options->disassemble = 1; ++p; break; case 'D': options->disassemble_all = 1; ++p; break; case 'r': options->relocations = 1; ++p; break; case 's': options->full_contents = 1; ++p; break; case 't': options->symbols = 1; ++p; break; case 'T': options->dynamic_symbols = 1; ++p; break; case 'h': options->section_headers = 1; ++p; break; case 'j': ++p; if( *p ) { *section_arg = p; return( 1 ); } return( 2 ); default: return( -1 ); } } return( 0 ); } /* End of parse_short_options() */ int main( int argc, char **argv ) { mcpu_objdump_options options; int first_file = -1; int status = 0; int i; memset( &options, 0, sizeof( options ) ); for( i = 1; i < argc; ++i ) { const char *arg = argv[i]; if( !strcmp( arg, "--" ) ) { first_file = i + 1; break; } if( arg[0] != '-' || !arg[1] ) { first_file = i; break; } if( !strcmp( arg, "--help" ) ) { usage( stdout, argv[0] ); free_options( &options ); return( 0 ); } if( !strcmp( arg, "--version" ) || !strcmp( arg, "--dump-version" ) ) { mcpu_version_action version_action; int rc; mcpu_version_parse_option( arg, &version_action ); rc = mcpu_version_print( argv[0], version_action ); free_options( &options ); return( rc == 0 ? 0 : 1 ); } if( !strcmp( arg, "--disassemble" ) ) options.disassemble = 1; else if( !strcmp( arg, "--disassemble-all" ) ) options.disassemble_all = 1; else if( !strcmp( arg, "--reloc" ) ) options.relocations = 1; else if( !strcmp( arg, "--full-contents" ) ) options.full_contents = 1; else if( !strcmp( arg, "--syms" ) ) options.symbols = 1; else if( !strcmp( arg, "--dynamic-syms" ) ) options.dynamic_symbols = 1; else if( !strcmp( arg, "--section-headers" ) ) options.section_headers = 1; else if( !strncmp( arg, "--section=", 10 ) ) { if( add_section( &options, arg + 10 ) ) { fprintf( stderr, "mcpu-objdump: out of memory or empty section name\n" ); free_options( &options ); return( 1 ); } } else if( !strcmp( arg, "-j" ) || !strcmp( arg, "--section" ) ) { if( i + 1 >= argc || add_section( &options, argv[++i] ) ) { fprintf( stderr, "mcpu-objdump: option %s requires a section name\n", arg ); free_options( &options ); return( 1 ); } } else if( arg[0] == '-' && arg[1] != '-' ) { const char *section_arg = NULL; int rc = parse_short_options( arg, &options, §ion_arg ); if( rc < 0 ) { fprintf( stderr, "mcpu-objdump: unknown option: %s\n", arg ); usage( stderr, argv[0] ); free_options( &options ); return( 1 ); } if( rc == 1 ) { if( add_section( &options, section_arg ) ) { fprintf( stderr, "mcpu-objdump: out of memory or empty section name\n" ); free_options( &options ); return( 1 ); } } else if( rc == 2 ) { if( i + 1 >= argc || add_section( &options, argv[++i] ) ) { fprintf( stderr, "mcpu-objdump: option -j requires a section name\n" ); free_options( &options ); return( 1 ); } } } else { fprintf( stderr, "mcpu-objdump: unknown option: %s\n", arg ); usage( stderr, argv[0] ); free_options( &options ); return( 1 ); } } if( first_file < 0 || first_file >= argc ) { fprintf( stderr, "mcpu-objdump: no input file\n" ); usage( stderr, argv[0] ); free_options( &options ); return( 1 ); } if( !options.disassemble && !options.disassemble_all && !options.relocations && !options.full_contents && !options.symbols && !options.dynamic_symbols && !options.section_headers ) { fprintf( stderr, "mcpu-objdump: at least one display option is required\n" ); usage( stderr, argv[0] ); free_options( &options ); return( 1 ); } for( i = first_file; i < argc; ++i ) { if( process_path( argv[i], &options ) ) status = 1; } free_options( &options ); return( status ); } /* End of main() */