diff options
Diffstat (limited to 'utils/mcpu-readelf.c')
| -rw-r--r-- | utils/mcpu-readelf.c | 1215 |
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() */ |
