diff options
| author | kx <kx@radix-linux.su> | 2026-10-09 18:00:15 +0300 |
|---|---|---|
| committer | kx <kx@radix-linux.su> | 2026-10-09 18:00:15 +0300 |
| commit | 065da3509019b15dd0e678072e6be629e5330bef (patch) | |
| tree | 69c934c9e786ac048b4fbe8f4c4c5571661521f4 /utils/mcpu-objdump.c | |
| parent | d130fe27fa753be02ed76fd9b277aa3c3d928eb9 (diff) | |
| download | libmcpu-0.4.0.tar.xz | |
Version 0.4.00.4.0
Diffstat (limited to 'utils/mcpu-objdump.c')
| -rw-r--r-- | utils/mcpu-objdump.c | 1747 |
1 files changed, 1747 insertions, 0 deletions
diff --git a/utils/mcpu-objdump.c b/utils/mcpu-objdump.c new file mode 100644 index 0000000..f61435e --- /dev/null +++ b/utils/mcpu-objdump.c @@ -0,0 +1,1747 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <ctype.h> +#include <inttypes.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <libmcpu.h> +#include <mcpu-elf.h> + +#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( "<invalid>" ); + 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( "<unknown>" ); + } + +} /* 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( "<unknown>" ); + } + +} /* 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( "<unknown>" ); + } + +} /* 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( "<unknown>" ); + } + +} /* 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, "!<arch>\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() */ |
