summaryrefslogtreecommitdiff
path: root/utils/mcpu-objdump.c
diff options
context:
space:
mode:
authorkx <kx@radix-linux.su>2026-10-09 18:00:15 +0300
committerkx <kx@radix-linux.su>2026-10-09 18:00:15 +0300
commit065da3509019b15dd0e678072e6be629e5330bef (patch)
tree69c934c9e786ac048b4fbe8f4c4c5571661521f4 /utils/mcpu-objdump.c
parentd130fe27fa753be02ed76fd9b277aa3c3d928eb9 (diff)
downloadlibmcpu-0.4.0.tar.xz
Version 0.4.00.4.0
Diffstat (limited to 'utils/mcpu-objdump.c')
-rw-r--r--utils/mcpu-objdump.c1747
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, &section_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() */