summaryrefslogtreecommitdiff
path: root/utils/mcpu-readelf.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-readelf.c
parentd130fe27fa753be02ed76fd9b277aa3c3d928eb9 (diff)
downloadlibmcpu-0.4.0.tar.xz
Version 0.4.00.4.0
Diffstat (limited to 'utils/mcpu-readelf.c')
-rw-r--r--utils/mcpu-readelf.c1215
1 files changed, 1215 insertions, 0 deletions
diff --git a/utils/mcpu-readelf.c b/utils/mcpu-readelf.c
new file mode 100644
index 0000000..bfa95e6
--- /dev/null
+++ b/utils/mcpu-readelf.c
@@ -0,0 +1,1215 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.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-melf.h"
+#include "mcpu-version.h"
+
+
+typedef struct __readelf_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;
+} readelf_shdr;
+
+
+typedef struct __readelf_phdr
+{
+ __mpu_uint32_t type;
+ __mpu_uint32_t flags;
+ __mpu_uint64_t offset;
+ __mpu_uint64_t vaddr;
+ __mpu_uint64_t paddr;
+ __mpu_uint64_t filesz;
+ __mpu_uint64_t memsz;
+ __mpu_uint64_t maxsz;
+ __mpu_uint64_t align;
+} readelf_phdr;
+
+
+typedef struct __readelf_file
+{
+ const char *path;
+ unsigned char *file;
+ size_t file_size;
+ int data;
+
+ __mpu_uint16_t type;
+ __mpu_uint16_t machine;
+ __mpu_uint32_t version;
+ __mpu_uint64_t entry;
+ __mpu_uint64_t init_entry;
+ __mpu_uint64_t life_entry;
+ __mpu_uint64_t fini_entry;
+ __mpu_uint64_t phoff;
+ __mpu_uint64_t shoff;
+ __mpu_uint32_t flags;
+ __mpu_uint16_t ehsize;
+ __mpu_uint16_t phentsize;
+ __mpu_uint16_t phnum;
+ __mpu_uint16_t shentsize;
+ __mpu_uint16_t shnum;
+ __mpu_uint16_t shstrndx;
+
+ readelf_phdr *phdr;
+ readelf_shdr *shdr;
+ const unsigned char *shstr;
+ size_t shstr_size;
+} readelf_file;
+
+
+typedef struct __readelf_options
+{
+ int file_header;
+ int program_headers;
+ int section_headers;
+ int symbols;
+ int relocations;
+ int dynamic;
+ int arch_specific;
+ int wide;
+} readelf_options;
+
+
+static void
+usage( FILE *fp, const char *name )
+{
+ fprintf( fp,
+ "usage: %s <option(s)> MELF-file(s)\n\n"
+ "Display information about LibMCPU MELF ABI1/ABI2/ABI3 files.\n\n"
+ "Options:\n"
+ " -a, --all - equivalent to -h -l -S -s -r -d -A\n"
+ " -e, --headers - equivalent to -h -l -S\n"
+ " -h, --file-header - display the MELF file header\n"
+ " -l, --program-headers - display the program headers\n"
+ " --segments - alias for --program-headers\n"
+ " -S, --section-headers - display the section headers\n"
+ " --sections - alias for --section-headers\n"
+ " -s, --symbols - display symbol tables (.symtab/.dynsym)\n"
+ " --syms - alias for --symbols\n"
+ " -r, --relocs - display RELA relocation sections\n"
+ " -d, --dynamic - display ABI-v3 dynamic dependency metadata\n"
+ " -A, --arch-specific - display MCPU target/lifecycle metadata\n"
+ " -W, --wide - do not truncate output (accepted for compatibility)\n"
+ " --help - show this help\n"
+ " --version - show program version\n"
+ " --dump-version - show version number only\n",
+ name );
+
+} /* End of usage() */
+
+
+static const char *
+file_type_name( unsigned int type )
+{
+ switch( type )
+ {
+ case MELF_ET_NONE: return( "NONE (None)" );
+ case MELF_ET_REL: return( "REL (Relocatable file)" );
+ case MELF_ET_EXEC: return( "EXEC (Executable file)" );
+ case MELF_ET_DYN: return( "DYN (Shared object file)" );
+ case MELF_ET_CORE: return( "CORE (Core file)" );
+ default: return( "<unknown>" );
+ }
+
+} /* End of file_type_name() */
+
+
+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 *
+program_type_name( __mpu_uint32_t type )
+{
+ switch( type )
+ {
+ case MELF_PT_NULL: return( "NULL" );
+ case MELF_PT_LOAD: return( "LOAD" );
+ case MELF_PT_MCPU_STACK: return( "MCPU_STACK" );
+ case MELF_PT_MCPU_HEAP: return( "MCPU_HEAP" );
+ default: return( "<unknown>" );
+ }
+
+} /* End of program_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 *
+symbol_visibility_name( unsigned int visibility )
+{
+ switch( visibility )
+ {
+ case MELF_STV_DEFAULT: return( "DEFAULT" );
+ case MELF_STV_INTERNAL: return( "INTERNAL" );
+ case MELF_STV_HIDDEN: return( "HIDDEN" );
+ case MELF_STV_PROTECTED: return( "PROTECTED" );
+ default: return( "<unknown>" );
+ }
+
+} /* End of symbol_visibility_name() */
+
+
+static const char *
+relocation_type_name( __mpu_uint32_t 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 void
+get_shdr( const unsigned char *p, int data, readelf_shdr *sh )
+{
+ memset( sh, 0, sizeof( *sh ) );
+ sh->name = cpu_melf_get32( p + MELF64_SHDR_SH_NAME_OFF, data );
+ sh->type = cpu_melf_get32( p + MELF64_SHDR_SH_TYPE_OFF, data );
+ sh->flags = cpu_melf_get64( p + MELF64_SHDR_SH_FLAGS_OFF, data );
+ sh->addr = cpu_melf_get64( p + MELF64_SHDR_SH_ADDR_OFF, data );
+ sh->offset = cpu_melf_get64( p + MELF64_SHDR_SH_OFFSET_OFF, data );
+ sh->size = cpu_melf_get64( p + MELF64_SHDR_SH_SIZE_OFF, data );
+ sh->link = cpu_melf_get32( p + MELF64_SHDR_SH_LINK_OFF, data );
+ sh->info = cpu_melf_get32( p + MELF64_SHDR_SH_INFO_OFF, data );
+ sh->align = cpu_melf_get64( p + MELF64_SHDR_SH_ADDRALIGN_OFF, data );
+ sh->entsize = cpu_melf_get64( p + MELF64_SHDR_SH_ENTSIZE_OFF, data );
+
+} /* End of get_shdr() */
+
+
+static void
+get_phdr( const unsigned char *p, int data, readelf_phdr *ph )
+{
+ memset( ph, 0, sizeof( *ph ) );
+ ph->type = cpu_melf_get32( p + MELF64_PHDR_P_TYPE_OFF, data );
+ ph->flags = cpu_melf_get32( p + MELF64_PHDR_P_FLAGS_OFF, data );
+ ph->offset = cpu_melf_get64( p + MELF64_PHDR_P_OFFSET_OFF, data );
+ ph->vaddr = cpu_melf_get64( p + MELF64_PHDR_P_VADDR_OFF, data );
+ ph->paddr = cpu_melf_get64( p + MELF64_PHDR_P_PADDR_OFF, data );
+ ph->filesz = cpu_melf_get64( p + MELF64_PHDR_P_FILESZ_OFF, data );
+ ph->memsz = cpu_melf_get64( p + MELF64_PHDR_P_MEMSZ_OFF, data );
+ ph->maxsz = cpu_melf_get64( p + MELF64_PHDR_P_MAXSZ_OFF, data );
+ ph->align = cpu_melf_get64( p + MELF64_PHDR_P_ALIGN_OFF, data );
+
+} /* End of get_phdr() */
+
+
+static void
+readelf_file_free( readelf_file *m )
+{
+ if( !m )
+ return;
+
+ free( m->phdr );
+ free( m->shdr );
+ free( m->file );
+ memset( m, 0, sizeof( *m ) );
+
+} /* End of readelf_file_free() */
+
+
+static int
+readelf_file_open( const char *path, readelf_file *m, char *err, size_t errsz )
+{
+ const unsigned char *h;
+ size_t i;
+
+ memset( m, 0, sizeof( *m ) );
+ m->path = path;
+
+ if( cpu_melf_readall( path, &m->file, &m->file_size, err, errsz ) )
+ return( -1 );
+
+ if( m->file_size < MELF64_EHDR_SIZE )
+ {
+ snprintf( err, errsz, "%s: file is too small for a MELF64 header", path );
+ goto fail;
+ }
+
+ h = m->file;
+ if( h[MELF_EI_MAG0] != MELFMAG0 || h[MELF_EI_MAG1] != MELFMAG1 ||
+ h[MELF_EI_MAG2] != MELFMAG2 || h[MELF_EI_MAG3] != MELFMAG3 )
+ {
+ snprintf( err, errsz, "%s: not a MELF file", path );
+ goto fail;
+ }
+
+ if( h[MELF_EI_CLASS] != MELF_CLASS_64 )
+ {
+ snprintf( err, errsz, "%s: unsupported MELF class %u", path,
+ (unsigned int)h[MELF_EI_CLASS] );
+ goto fail;
+ }
+
+ m->data = h[MELF_EI_DATA];
+ if( m->data != MELF_DATA_LSB && m->data != MELF_DATA_MSB )
+ {
+ snprintf( err, errsz, "%s: invalid MELF data encoding %d", path, m->data );
+ goto fail;
+ }
+
+ m->type = cpu_melf_get16( h + MELF64_EHDR_E_TYPE_OFF, m->data );
+ m->machine = cpu_melf_get16( h + MELF64_EHDR_E_MACHINE_OFF, m->data );
+ m->version = cpu_melf_get32( h + MELF64_EHDR_E_VERSION_OFF, m->data );
+ m->entry = cpu_melf_get64( h + MELF64_EHDR_E_ENTRY_OFF, m->data );
+ m->init_entry = cpu_melf_get64( h + MELF64_EHDR_E_INIT_ENTRY_OFF, m->data );
+ m->life_entry = cpu_melf_get64( h + MELF64_EHDR_E_LIFE_ENTRY_OFF, m->data );
+ m->fini_entry = cpu_melf_get64( h + MELF64_EHDR_E_FINI_ENTRY_OFF, m->data );
+ m->phoff = cpu_melf_get64( h + MELF64_EHDR_E_PHOFF_OFF, m->data );
+ m->shoff = cpu_melf_get64( h + MELF64_EHDR_E_SHOFF_OFF, m->data );
+ m->flags = cpu_melf_get32( h + MELF64_EHDR_E_FLAGS_OFF, m->data );
+ m->ehsize = cpu_melf_get16( h + MELF64_EHDR_E_EHSIZE_OFF, m->data );
+ m->phentsize = cpu_melf_get16( h + MELF64_EHDR_E_PHENTSIZE_OFF, m->data );
+ m->phnum = cpu_melf_get16( h + MELF64_EHDR_E_PHNUM_OFF, m->data );
+ m->shentsize = cpu_melf_get16( h + MELF64_EHDR_E_SHENTSIZE_OFF, m->data );
+ m->shnum = cpu_melf_get16( h + MELF64_EHDR_E_SHNUM_OFF, m->data );
+ m->shstrndx = cpu_melf_get16( h + MELF64_EHDR_E_SHSTRNDX_OFF, m->data );
+
+ if( m->ehsize != MELF64_EHDR_SIZE )
+ {
+ snprintf( err, errsz, "%s: unsupported MELF64 header size %u", path,
+ (unsigned int)m->ehsize );
+ goto fail;
+ }
+
+ if( m->phnum )
+ {
+ if( m->phentsize != MELF64_PHDR_SIZE ||
+ !cpu_melf_span( m->file_size, m->phoff,
+ (__mpu_uint64_t)m->phnum * MELF64_PHDR_SIZE ) )
+ {
+ snprintf( err, errsz, "%s: malformed program-header table", path );
+ goto fail;
+ }
+
+ m->phdr = (readelf_phdr *)calloc( m->phnum, sizeof( *m->phdr ) );
+ if( !m->phdr )
+ {
+ snprintf( err, errsz, "out of memory" );
+ goto fail;
+ }
+
+ for( i = 0; i < m->phnum; ++i )
+ get_phdr( m->file + (size_t)m->phoff + i * MELF64_PHDR_SIZE,
+ m->data, &m->phdr[i] );
+ }
+ else if( m->phentsize != 0 && m->phentsize != MELF64_PHDR_SIZE )
+ {
+ snprintf( err, errsz, "%s: unsupported program-header entry size %u", path,
+ (unsigned int)m->phentsize );
+ goto fail;
+ }
+
+ if( m->shnum )
+ {
+ if( m->shentsize != MELF64_SHDR_SIZE ||
+ !cpu_melf_span( m->file_size, m->shoff,
+ (__mpu_uint64_t)m->shnum * MELF64_SHDR_SIZE ) )
+ {
+ snprintf( err, errsz, "%s: malformed section-header table", path );
+ goto fail;
+ }
+
+ m->shdr = (readelf_shdr *)calloc( m->shnum, sizeof( *m->shdr ) );
+ if( !m->shdr )
+ {
+ snprintf( err, errsz, "out of memory" );
+ goto fail;
+ }
+
+ for( i = 0; i < m->shnum; ++i )
+ get_shdr( m->file + (size_t)m->shoff + i * MELF64_SHDR_SIZE,
+ m->data, &m->shdr[i] );
+
+ if( m->shstrndx >= m->shnum ||
+ m->shdr[m->shstrndx].type != MELF_SHT_STRTAB ||
+ !cpu_melf_span( m->file_size, m->shdr[m->shstrndx].offset,
+ m->shdr[m->shstrndx].size ) )
+ {
+ snprintf( err, errsz, "%s: malformed section-name string table", path );
+ goto fail;
+ }
+
+ m->shstr = m->file + (size_t)m->shdr[m->shstrndx].offset;
+ m->shstr_size = (size_t)m->shdr[m->shstrndx].size;
+ }
+ else if( m->shentsize != 0 && m->shentsize != MELF64_SHDR_SIZE )
+ {
+ snprintf( err, errsz, "%s: unsupported section-header entry size %u", path,
+ (unsigned int)m->shentsize );
+ goto fail;
+ }
+
+ return( 0 );
+
+fail:
+ readelf_file_free( m );
+ return( -1 );
+
+} /* End of readelf_file_open() */
+
+
+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( "<corrupt>" );
+
+ p = table + (size_t)offset;
+ if( !memchr( p, '\0', size - (size_t)offset ) )
+ return( "<corrupt>" );
+
+ return( (const char *)p );
+
+} /* End of string_at() */
+
+
+static const char *
+section_name( const readelf_file *m, size_t index )
+{
+ if( !m->shdr || index >= m->shnum )
+ return( "<invalid>" );
+
+ return( string_at( m->shstr, m->shstr_size, m->shdr[index].name ) );
+
+} /* End of section_name() */
+
+
+static int
+section_payload( const readelf_file *m, size_t index,
+ const unsigned char **payload, size_t *size )
+{
+ const readelf_shdr *sh;
+
+ if( !m->shdr || index >= m->shnum )
+ return( -1 );
+
+ sh = &m->shdr[index];
+ if( sh->type == MELF_SHT_NOBITS )
+ {
+ *payload = NULL;
+ *size = (size_t)sh->size;
+ return( 0 );
+ }
+
+ if( sh->size > SIZE_MAX ||
+ !cpu_melf_span( m->file_size, sh->offset, sh->size ) )
+ return( -1 );
+
+ *payload = m->file + (size_t)sh->offset;
+ *size = (size_t)sh->size;
+ return( 0 );
+
+} /* End of section_payload() */
+
+
+static void
+print_file_header( const readelf_file *m )
+{
+ const unsigned char *h = m->file;
+ size_t i;
+
+ printf( "MELF Header:\n" );
+ printf( " Magic: " );
+ for( i = 0; i < MELF_EI_NIDENT; ++i )
+ printf( " %02x", (unsigned int)h[i] );
+ printf( "\n" );
+ printf( " Class: MELF64\n" );
+ printf( " Data: %s endian\n",
+ m->data == MELF_DATA_LSB ? "little" : "big" );
+ printf( " Version: %u\n",
+ (unsigned int)h[MELF_EI_VERSION] );
+ printf( " OS/ABI: %s\n",
+ h[MELF_EI_OSABI] == MELF_OSABI_MCPU ? "MCPU" : "<unknown>" );
+ printf( " ABI Version: %u\n",
+ (unsigned int)h[MELF_EI_ABIVERSION] );
+ printf( " Type: %s\n", file_type_name( m->type ) );
+ printf( " Machine: %s (0x%04x)\n",
+ m->machine == MELF_EM_MCPU ? "MCPU" : "<unknown>",
+ (unsigned int)m->machine );
+ printf( " Version: 0x%x\n", (unsigned int)m->version );
+ printf( " Entry point address: 0x%016" PRIx64 "\n",
+ (uint64_t)m->entry );
+ printf( " INIT entry address: 0x%016" PRIx64 "\n",
+ (uint64_t)m->init_entry );
+ printf( " LIFE entry address: 0x%016" PRIx64 "\n",
+ (uint64_t)m->life_entry );
+ printf( " FINI entry address: 0x%016" PRIx64 "\n",
+ (uint64_t)m->fini_entry );
+ printf( " Start of program headers: %" PRIu64 " (bytes into file)\n",
+ (uint64_t)m->phoff );
+ printf( " Start of section headers: %" PRIu64 " (bytes into file)\n",
+ (uint64_t)m->shoff );
+ printf( " Flags: 0x%x\n", (unsigned int)m->flags );
+ printf( " Size of this header: %u (bytes)\n",
+ (unsigned int)m->ehsize );
+ printf( " Size of program headers: %u (bytes)\n",
+ (unsigned int)m->phentsize );
+ printf( " Number of program headers: %u\n", (unsigned int)m->phnum );
+ printf( " Size of section headers: %u (bytes)\n",
+ (unsigned int)m->shentsize );
+ printf( " Number of section headers: %u\n", (unsigned int)m->shnum );
+ printf( " Section header string table index: %u\n", (unsigned int)m->shstrndx );
+
+} /* End of print_file_header() */
+
+
+static void
+print_program_headers( const readelf_file *m )
+{
+ size_t i;
+
+ if( !m->phnum )
+ {
+ printf( "There are no program headers in this file.\n" );
+ return;
+ }
+
+ printf( "There are %u program headers, starting at offset %" PRIu64 ":\n\n",
+ (unsigned int)m->phnum, (uint64_t)m->phoff );
+ printf( "Program Headers:\n" );
+ printf( " Type Offset VirtAddr PhysAddr\n" );
+ printf( " FileSiz MemSiz MaxSiz Flg Align\n" );
+
+ for( i = 0; i < m->phnum; ++i )
+ {
+ const readelf_phdr *ph = &m->phdr[i];
+ char flags[4];
+
+ flags[0] = (ph->flags & MELF_PF_R) ? 'R' : ' ';
+ flags[1] = (ph->flags & MELF_PF_W) ? 'W' : ' ';
+ flags[2] = (ph->flags & MELF_PF_X) ? 'E' : ' ';
+ flags[3] = '\0';
+
+ printf( " %-13s 0x%016" PRIx64 " 0x%016" PRIx64 " 0x%016" PRIx64 "\n",
+ program_type_name( ph->type ), (uint64_t)ph->offset,
+ (uint64_t)ph->vaddr, (uint64_t)ph->paddr );
+ printf( " 0x%016" PRIx64 " 0x%016" PRIx64 " 0x%016" PRIx64
+ " %3s 0x%" PRIx64 "\n",
+ (uint64_t)ph->filesz, (uint64_t)ph->memsz, (uint64_t)ph->maxsz,
+ flags, (uint64_t)ph->align );
+ }
+
+} /* End of print_program_headers() */
+
+
+static void
+section_flags( __mpu_uint64_t flags, char out[8] )
+{
+ size_t n = 0;
+
+ if( flags & MELF_SHF_WRITE )
+ out[n++] = 'W';
+ if( flags & MELF_SHF_ALLOC )
+ out[n++] = 'A';
+ if( flags & MELF_SHF_EXECINSTR )
+ out[n++] = 'X';
+ if( MELF_SHF_MCPU_HAS_SEG( flags ) )
+ {
+ unsigned int seg = MELF_SHF_MCPU_GET_SEG( flags );
+
+ if( seg <= MELF_MCPU_SEG_HEAP )
+ {
+ switch( seg )
+ {
+ case MELF_MCPU_SEG_DATA: out[n++] = 'D'; break;
+ case MELF_MCPU_SEG_STACK: out[n++] = 'S'; break;
+ case MELF_MCPU_SEG_PROGRAM: out[n++] = 'P'; break;
+ case MELF_MCPU_SEG_CONTROL: out[n++] = 'C'; break;
+ case MELF_MCPU_SEG_INPUT: out[n++] = 'I'; break;
+ case MELF_MCPU_SEG_OUTPUT: out[n++] = 'O'; break;
+ case MELF_MCPU_SEG_TRACE: out[n++] = 'T'; break;
+ case MELF_MCPU_SEG_HEAP: out[n++] = 'H'; break;
+ }
+ }
+ }
+ out[n] = '\0';
+
+} /* End of section_flags() */
+
+
+static void
+print_section_headers( const readelf_file *m )
+{
+ size_t i;
+
+ if( !m->shnum )
+ {
+ printf( "There are no section headers in this file.\n" );
+ return;
+ }
+
+ printf( "There are %u section headers, starting at offset 0x%" PRIx64 ":\n\n",
+ (unsigned int)m->shnum, (uint64_t)m->shoff );
+ printf( "Section Headers:\n" );
+ printf( " [Nr] Name Type Address Offset "
+ "Size EntSize Flags Link Info Align\n" );
+
+ for( i = 0; i < m->shnum; ++i )
+ {
+ const readelf_shdr *sh = &m->shdr[i];
+ char flags[8];
+
+ section_flags( sh->flags, flags );
+ printf( " [%2zu] %-20s %-16s %016" PRIx64 " %08" PRIx64
+ " %016" PRIx64 " %016" PRIx64 " %-5s %4u %4u %5" PRIu64 "\n",
+ i, section_name( m, i ), section_type_name( sh->type ),
+ (uint64_t)sh->addr, (uint64_t)sh->offset,
+ (uint64_t)sh->size, (uint64_t)sh->entsize, flags,
+ (unsigned int)sh->link, (unsigned int)sh->info,
+ (uint64_t)sh->align );
+ }
+
+ printf( "Key to Flags: W (write), A (alloc), X (execute), "
+ "D/S/P/C/I/O/T/H (MCPU segment)\n" );
+
+} /* End of print_section_headers() */
+
+
+static int
+get_symbol( const readelf_file *m, size_t table_index, size_t symbol_index,
+ __mpu_uint32_t *name, unsigned int *bind, unsigned int *type,
+ unsigned int *visibility, __mpu_uint16_t *shndx,
+ __mpu_uint64_t *value, __mpu_uint64_t *size )
+{
+ const readelf_shdr *sh;
+ const unsigned char *p;
+ __mpu_uint8_t info;
+ __mpu_uint8_t other;
+ __mpu_uint64_t count;
+
+ if( table_index >= m->shnum )
+ return( -1 );
+ sh = &m->shdr[table_index];
+ if( sh->type != MELF_SHT_SYMTAB && sh->type != MELF_SHT_DYNSYM )
+ return( -1 );
+ if( sh->entsize != MELF64_SYM_SIZE || sh->size % MELF64_SYM_SIZE )
+ return( -1 );
+ count = sh->size / MELF64_SYM_SIZE;
+ if( symbol_index >= count ||
+ !cpu_melf_span( m->file_size, sh->offset, sh->size ) )
+ return( -1 );
+
+ p = m->file + (size_t)sh->offset + symbol_index * MELF64_SYM_SIZE;
+ *name = cpu_melf_get32( p + MELF64_SYM_ST_NAME_OFF, m->data );
+ info = p[MELF64_SYM_ST_INFO_OFF];
+ other = p[MELF64_SYM_ST_OTHER_OFF];
+ *bind = MELF64_ST_BIND( info );
+ *type = MELF64_ST_TYPE( info );
+ *visibility = MELF_ST_VISIBILITY( other );
+ *shndx = cpu_melf_get16( p + MELF64_SYM_ST_SHNDX_OFF, m->data );
+ *value = cpu_melf_get64( p + MELF64_SYM_ST_VALUE_OFF, m->data );
+ *size = cpu_melf_get64( p + MELF64_SYM_ST_SIZE_OFF, m->data );
+ return( 0 );
+
+} /* End of get_symbol() */
+
+
+static const char *
+symbol_name( const readelf_file *m, size_t table_index, __mpu_uint32_t name )
+{
+ const readelf_shdr *sh;
+ const readelf_shdr *str;
+ const unsigned char *payload;
+ size_t payload_size;
+
+ if( table_index >= m->shnum )
+ return( "<invalid>" );
+ sh = &m->shdr[table_index];
+ if( sh->link >= m->shnum )
+ return( "<invalid>" );
+ str = &m->shdr[sh->link];
+ if( str->type != MELF_SHT_STRTAB ||
+ section_payload( m, sh->link, &payload, &payload_size ) )
+ return( "<invalid>" );
+
+ return( string_at( payload, payload_size, name ) );
+
+} /* End of symbol_name() */
+
+
+static void
+print_symbol_index( __mpu_uint16_t shndx )
+{
+ if( shndx == MELF_SHN_UNDEF )
+ printf( "%4s", "UND" );
+ else if( shndx == MELF_SHN_ABS )
+ printf( "%4s", "ABS" );
+ else if( shndx == MELF_SHN_COMMON )
+ printf( "%4s", "COM" );
+ else
+ printf( "%4u", (unsigned int)shndx );
+
+} /* End of print_symbol_index() */
+
+
+static void
+print_symbols( const readelf_file *m )
+{
+ size_t i;
+
+ for( i = 0; i < m->shnum; ++i )
+ {
+ const readelf_shdr *sh = &m->shdr[i];
+ __mpu_uint64_t count;
+ size_t j;
+
+ if( sh->type != MELF_SHT_SYMTAB && sh->type != MELF_SHT_DYNSYM )
+ continue;
+
+ if( sh->entsize != MELF64_SYM_SIZE || sh->size % MELF64_SYM_SIZE ||
+ !cpu_melf_span( m->file_size, sh->offset, sh->size ) )
+ {
+ printf( "Symbol table '%s' is malformed.\n", section_name( m, i ) );
+ continue;
+ }
+
+ count = sh->size / MELF64_SYM_SIZE;
+ printf( "Symbol table '%s' contains %" PRIu64 " entries:\n",
+ section_name( m, i ), (uint64_t)count );
+ printf( " Num: Value Size Type Bind Vis Ndx Name\n" );
+
+ for( j = 0; j < count; ++j )
+ {
+ __mpu_uint32_t name;
+ unsigned int bind;
+ unsigned int type;
+ unsigned int visibility;
+ __mpu_uint16_t shndx;
+ __mpu_uint64_t value;
+ __mpu_uint64_t size;
+
+ if( get_symbol( m, i, j, &name, &bind, &type, &visibility, &shndx,
+ &value, &size ) )
+ break;
+
+ printf( "%6zu: %016" PRIx64 " %5" PRIu64 " %-8s %-8s %-9s ",
+ j, (uint64_t)value, (uint64_t)size,
+ symbol_type_name( type ), symbol_bind_name( bind ),
+ symbol_visibility_name( visibility ) );
+ print_symbol_index( shndx );
+ printf( " %s\n", symbol_name( m, i, name ) );
+ }
+ printf( "\n" );
+ }
+
+} /* End of print_symbols() */
+
+
+static void
+print_relocations( const readelf_file *m )
+{
+ size_t i;
+
+ for( i = 0; i < m->shnum; ++i )
+ {
+ const readelf_shdr *sh = &m->shdr[i];
+ __mpu_uint64_t count;
+ size_t j;
+
+ if( sh->type != MELF_SHT_RELA )
+ continue;
+
+ if( sh->entsize != MELF64_RELA_SIZE || sh->size % MELF64_RELA_SIZE ||
+ !cpu_melf_span( m->file_size, sh->offset, sh->size ) ||
+ sh->link >= m->shnum ||
+ (m->shdr[sh->link].type != MELF_SHT_SYMTAB &&
+ m->shdr[sh->link].type != MELF_SHT_DYNSYM) )
+ {
+ printf( "Relocation section '%s' is malformed.\n", section_name( m, i ) );
+ continue;
+ }
+
+ count = sh->size / MELF64_RELA_SIZE;
+ printf( "Relocation section '%s' at offset 0x%" PRIx64
+ " contains %" PRIu64 " entries:\n",
+ section_name( m, i ), (uint64_t)sh->offset, (uint64_t)count );
+ printf( " Offset Info Type Sym.Value Sym.Name + Addend\n" );
+
+ for( j = 0; j < count; ++j )
+ {
+ const unsigned char *p;
+ __mpu_uint64_t offset;
+ __mpu_uint64_t info;
+ __mpu_int64_t addend;
+ __mpu_uint32_t sym;
+ __mpu_uint32_t type;
+ __mpu_uint32_t name = 0;
+ unsigned int bind = 0;
+ unsigned int stype = 0;
+ unsigned int visibility = 0;
+ __mpu_uint16_t shndx = 0;
+ __mpu_uint64_t value = 0;
+ __mpu_uint64_t ssize = 0;
+ const char *sname = "<invalid>";
+
+ p = m->file + (size_t)sh->offset + j * MELF64_RELA_SIZE;
+ offset = cpu_melf_get64( p + MELF64_RELA_R_OFFSET_OFF, m->data );
+ info = cpu_melf_get64( p + MELF64_RELA_R_INFO_OFF, m->data );
+ addend = (__mpu_int64_t)cpu_melf_get64( p + MELF64_RELA_R_ADDEND_OFF,
+ m->data );
+ sym = MELF64_R_SYM( info );
+ type = MELF64_R_TYPE( info );
+
+ if( !get_symbol( m, sh->link, sym, &name, &bind, &stype, &visibility,
+ &shndx, &value, &ssize ) )
+ sname = symbol_name( m, sh->link, name );
+
+ printf( " %016" PRIx64 " %016" PRIx64 " %-20s %016" PRIx64
+ " %s %c 0x%" PRIx64 "\n",
+ (uint64_t)offset, (uint64_t)info, relocation_type_name( type ),
+ (uint64_t)value, sname, addend < 0 ? '-' : '+',
+ (uint64_t)(addend < 0 ? -(__mpu_uint64_t)addend :
+ (__mpu_uint64_t)addend) );
+ (void)bind;
+ (void)stype;
+ (void)visibility;
+ (void)shndx;
+ (void)ssize;
+ }
+ printf( "\n" );
+ }
+
+} /* End of print_relocations() */
+
+
+static const char *
+dynamic_tag_name( __mpu_int64_t tag )
+{
+ switch( tag )
+ {
+ case MELF_DT_NULL: return( "NULL" );
+ case MELF_DT_NEEDED: return( "NEEDED" );
+ case MELF_DT_SONAME: return( "SONAME" );
+ default: return( "<unknown>" );
+ }
+
+} /* End of dynamic_tag_name() */
+
+
+static void
+print_dynamic( const readelf_file *m )
+{
+ size_t i;
+ int printed = 0;
+
+ for( i = 0; i < m->shnum; ++i )
+ {
+ const readelf_shdr *sh = &m->shdr[i];
+ const readelf_shdr *str;
+ const unsigned char *payload;
+ const unsigned char *strings;
+ size_t payload_size;
+ size_t strings_size;
+ size_t count;
+ size_t j;
+
+ if( sh->type != MELF_SHT_DYNAMIC )
+ continue;
+
+ printed = 1;
+ if( sh->link >= m->shnum ||
+ m->shdr[sh->link].type != MELF_SHT_STRTAB ||
+ sh->entsize != MELF64_DYN_SIZE ||
+ sh->size < MELF64_DYN_SIZE ||
+ sh->size % MELF64_DYN_SIZE ||
+ section_payload( m, i, &payload, &payload_size ) ||
+ section_payload( m, sh->link, &strings, &strings_size ) )
+ {
+ printf( "Dynamic section '%s' is malformed.\n", section_name( m, i ) );
+ continue;
+ }
+
+ str = &m->shdr[sh->link];
+ (void)str;
+ count = payload_size / MELF64_DYN_SIZE;
+ printf( "Dynamic section '%s' at offset 0x%" PRIx64
+ " contains %zu entries:\n",
+ section_name( m, i ), (uint64_t)sh->offset, count );
+ printf( " Tag Type Name/Value\n" );
+
+ for( j = 0; j < count; ++j )
+ {
+ const unsigned char *dp = payload + j * MELF64_DYN_SIZE;
+ __mpu_int64_t tag =
+ (__mpu_int64_t)cpu_melf_get64( dp + MELF64_DYN_D_TAG_OFF, m->data );
+ __mpu_uint64_t val =
+ cpu_melf_get64( dp + MELF64_DYN_D_VAL_OFF, m->data );
+
+ printf( " 0x%016" PRIx64 " %-20s ",
+ (uint64_t)tag, dynamic_tag_name( tag ) );
+ if( tag == MELF_DT_NEEDED )
+ printf( "Shared library: [%s]\n",
+ string_at( strings, strings_size, val ) );
+ else if( tag == MELF_DT_SONAME )
+ printf( "Library soname: [%s]\n",
+ string_at( strings, strings_size, val ) );
+ else
+ printf( "0x%" PRIx64 "\n", (uint64_t)val );
+ }
+ printf( "\n" );
+ }
+
+ if( !printed )
+ printf( "There is no dynamic section in this file.\n" );
+
+} /* End of print_dynamic() */
+
+
+static void
+print_target( const MElf_McpuTarget *t )
+{
+ printf( "MCPU target attributes:\n" );
+ printf( " metadata version: %u\n", (unsigned int)t->mt_version );
+ printf( " metadata size: %u\n", (unsigned int)t->mt_size );
+ printf( " ISA revision: %u\n", (unsigned int)t->mt_isa_revision );
+ printf( " ABI revision: %u\n", (unsigned int)t->mt_abi_revision );
+ printf( " program model: %u\n", (unsigned int)t->mt_program_model );
+ printf( " data byte order: %u\n", (unsigned int)t->mt_data_byte_order );
+ printf( " data word order: %u\n", (unsigned int)t->mt_data_word_order );
+ printf( " integer model: %u\n", (unsigned int)t->mt_integer_model );
+ printf( " address model: %u\n", (unsigned int)t->mt_address_model );
+ printf( " register model: %u\n", (unsigned int)t->mt_register_model );
+ printf( " real model: %u\n", (unsigned int)t->mt_real_model );
+ printf( " complex model: %u\n", (unsigned int)t->mt_complex_model );
+ printf( " maximum ABI alignment: %u\n", (unsigned int)t->mt_max_align );
+ printf( " flags: 0x%08x\n", (unsigned int)t->mt_flags );
+ printf( " required features: 0x%016" PRIx64 "\n",
+ (uint64_t)t->mt_required_features );
+
+} /* End of print_target() */
+
+
+static void
+print_lifecycle( const MElf_McpuLifecycle *l )
+{
+ printf( "MCPU lifecycle attributes:\n" );
+ printf( " metadata version: %u\n", (unsigned int)l->ml_version );
+ printf( " metadata size: %u\n", (unsigned int)l->ml_size );
+ printf( " lifecycle ABI: %u\n", (unsigned int)l->ml_abi );
+ printf( " reserved0: 0x%08x\n", (unsigned int)l->ml_reserved0 );
+ printf( " flags: 0x%016" PRIx64 "\n",
+ (uint64_t)l->ml_flags );
+ printf( " reserved1: 0x%016" PRIx64 "\n",
+ (uint64_t)l->ml_reserved1 );
+
+} /* End of print_lifecycle() */
+
+
+static void
+print_arch_specific( const readelf_file *m )
+{
+ size_t i;
+ int printed = 0;
+
+ for( i = 0; i < m->shnum; ++i )
+ {
+ const readelf_shdr *sh = &m->shdr[i];
+ const unsigned char *payload;
+ size_t payload_size;
+
+ if( sh->type == MELF_SHT_MCPU_TARGET )
+ {
+ MElf_McpuTarget t;
+
+ if( section_payload( m, i, &payload, &payload_size ) ||
+ payload_size != MELF_MCPU_TARGET_SIZE ||
+ cpu_melf_decode_target( payload, &t, m->data ) )
+ {
+ printf( "MCPU target section '%s' is malformed.\n", section_name( m, i ) );
+ continue;
+ }
+ print_target( &t );
+ printf( "\n" );
+ printed = 1;
+ }
+ else if( sh->type == MELF_SHT_MCPU_LIFECYCLE )
+ {
+ MElf_McpuLifecycle l;
+
+ if( section_payload( m, i, &payload, &payload_size ) ||
+ payload_size != MELF_MCPU_LIFECYCLE_SIZE ||
+ cpu_melf_decode_lifecycle( payload, &l, m->data ) )
+ {
+ printf( "MCPU lifecycle section '%s' is malformed.\n",
+ section_name( m, i ) );
+ continue;
+ }
+ print_lifecycle( &l );
+ printf( "\n" );
+ printed = 1;
+ }
+ }
+
+ if( !printed )
+ printf( "There are no MCPU architecture-specific metadata sections in this file.\n" );
+
+} /* End of print_arch_specific() */
+
+
+static int
+parse_short_options( const char *arg, readelf_options *opt )
+{
+ const char *p;
+
+ for( p = arg + 1; *p; ++p )
+ {
+ switch( *p )
+ {
+ case 'a':
+ opt->file_header = 1;
+ opt->program_headers = 1;
+ opt->section_headers = 1;
+ opt->symbols = 1;
+ opt->relocations = 1;
+ opt->dynamic = 1;
+ opt->arch_specific = 1;
+ break;
+ case 'e':
+ opt->file_header = 1;
+ opt->program_headers = 1;
+ opt->section_headers = 1;
+ break;
+ case 'h': opt->file_header = 1; break;
+ case 'l': opt->program_headers = 1; break;
+ case 'S': opt->section_headers = 1; break;
+ case 's': opt->symbols = 1; break;
+ case 'r': opt->relocations = 1; break;
+ case 'd': opt->dynamic = 1; break;
+ case 'A': opt->arch_specific = 1; break;
+ case 'W': opt->wide = 1; break;
+ default: return( -1 );
+ }
+ }
+
+ return( 0 );
+
+} /* End of parse_short_options() */
+
+
+static int
+have_output_option( const readelf_options *opt )
+{
+ return( opt->file_header || opt->program_headers || opt->section_headers ||
+ opt->symbols || opt->relocations || opt->dynamic ||
+ opt->arch_specific );
+
+} /* End of have_output_option() */
+
+
+int
+main( int argc, char **argv )
+{
+ readelf_options opt;
+ char **files;
+ size_t file_count = 0;
+ size_t file_capacity = 0;
+ int i;
+ int rc = 0;
+
+ memset( &opt, 0, sizeof( opt ) );
+ files = NULL;
+
+ for( i = 1; i < argc; ++i )
+ {
+ const char *arg = argv[i];
+
+ if( !strcmp( arg, "--help" ) )
+ {
+ usage( stdout, argv[0] );
+ free( files );
+ return( 0 );
+ }
+ else 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( files );
+ return( rc == 0 ? 0 : 1 );
+ }
+ else if( !strcmp( arg, "--all" ) )
+ parse_short_options( "-a", &opt );
+ else if( !strcmp( arg, "--headers" ) )
+ parse_short_options( "-e", &opt );
+ else if( !strcmp( arg, "--file-header" ) )
+ opt.file_header = 1;
+ else if( !strcmp( arg, "--program-headers" ) || !strcmp( arg, "--segments" ) )
+ opt.program_headers = 1;
+ else if( !strcmp( arg, "--section-headers" ) || !strcmp( arg, "--sections" ) )
+ opt.section_headers = 1;
+ else if( !strcmp( arg, "--symbols" ) || !strcmp( arg, "--syms" ) )
+ opt.symbols = 1;
+ else if( !strcmp( arg, "--relocs" ) )
+ opt.relocations = 1;
+ else if( !strcmp( arg, "--dynamic" ) )
+ opt.dynamic = 1;
+ else if( !strcmp( arg, "--arch-specific" ) )
+ opt.arch_specific = 1;
+ else if( !strcmp( arg, "--wide" ) )
+ opt.wide = 1;
+ else if( arg[0] == '-' && arg[1] != '\0' )
+ {
+ if( parse_short_options( arg, &opt ) )
+ {
+ fprintf( stderr, "mcpu-readelf: unknown option '%s'\n", arg );
+ usage( stderr, argv[0] );
+ free( files );
+ return( 1 );
+ }
+ }
+ else
+ {
+ if( file_count == file_capacity )
+ {
+ size_t next = file_capacity ? file_capacity * 2U : 4U;
+ char **q = (char **)realloc( files, next * sizeof( *q ) );
+
+ if( !q )
+ {
+ fprintf( stderr, "mcpu-readelf: out of memory\n" );
+ free( files );
+ return( 1 );
+ }
+ files = q;
+ file_capacity = next;
+ }
+ files[file_count++] = argv[i];
+ }
+ }
+
+ if( !have_output_option( &opt ) || !file_count )
+ {
+ usage( stderr, argv[0] );
+ free( files );
+ return( 1 );
+ }
+
+ for( i = 0; i < (int)file_count; ++i )
+ {
+ readelf_file m;
+ char err[512];
+
+ if( file_count > 1 )
+ printf( "\nFile: %s\n", files[i] );
+
+ if( readelf_file_open( files[i], &m, err, sizeof( err ) ) )
+ {
+ fprintf( stderr, "mcpu-readelf: %s\n", err );
+ rc = 1;
+ continue;
+ }
+
+ if( opt.file_header )
+ {
+ print_file_header( &m );
+ if( opt.program_headers || opt.section_headers || opt.symbols ||
+ opt.relocations || opt.dynamic || opt.arch_specific )
+ printf( "\n" );
+ }
+ if( opt.program_headers )
+ {
+ print_program_headers( &m );
+ if( opt.section_headers || opt.symbols || opt.relocations ||
+ opt.dynamic || opt.arch_specific )
+ printf( "\n" );
+ }
+ if( opt.section_headers )
+ {
+ print_section_headers( &m );
+ if( opt.symbols || opt.relocations || opt.dynamic || opt.arch_specific )
+ printf( "\n" );
+ }
+ if( opt.symbols )
+ print_symbols( &m );
+ if( opt.relocations )
+ print_relocations( &m );
+ if( opt.dynamic )
+ print_dynamic( &m );
+ if( opt.arch_specific )
+ print_arch_specific( &m );
+
+ readelf_file_free( &m );
+ }
+
+ free( files );
+ return( rc );
+
+} /* End of main() */