#ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #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 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( "" ); } } /* 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( "" ); } } /* 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( "" ); } } /* 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( "" ); } } /* End of symbol_bind_name() */ static const char * symbol_type_name( unsigned int type ) { switch( type ) { case MELF_STT_NOTYPE: return( "NOTYPE" ); case MELF_STT_OBJECT: return( "OBJECT" ); case MELF_STT_FUNC: return( "FUNC" ); case MELF_STT_SECTION: return( "SECTION" ); case MELF_STT_FILE: return( "FILE" ); default: return( "" ); } } /* End of symbol_type_name() */ static const char * 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( "" ); } } /* 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( "" ); } } /* 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( "" ); p = table + (size_t)offset; if( !memchr( p, '\0', size - (size_t)offset ) ) return( "" ); 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( "" ); 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" : "" ); 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" : "", (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( "" ); sh = &m->shdr[table_index]; if( sh->link >= m->shnum ) return( "" ); str = &m->shdr[sh->link]; if( str->type != MELF_SHT_STRTAB || section_payload( m, sh->link, &payload, &payload_size ) ) return( "" ); 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 = ""; 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( "" ); } } /* 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() */