mirror of
https://github.com/mikpe/pdp10-tools.git
synced 2026-01-11 23:53:19 +00:00
1029 lines
28 KiB
C
1029 lines
28 KiB
C
/*
|
|
* readelf.c -- readelf clone for PDP10 Elf36 files
|
|
* Copyright (C) 2013-2015 Mikael Pettersson
|
|
*
|
|
* This file is part of pdp10-tools.
|
|
*
|
|
* pdp10-tools is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* pdp10-tools is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with pdp10-tools. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#define _GNU_SOURCE /* for getopt_long() */
|
|
#include <errno.h>
|
|
#include <getopt.h> /* for getopt_long() */
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "pdp10-elf36.h"
|
|
#include "pdp10-inttypes.h"
|
|
#include "pdp10-opcodes.h"
|
|
#include "pdp10-stdio.h"
|
|
|
|
#define VERSION "pdp10-tools readelf version 0.1, built " __DATE__ " " __TIME__ "\n"
|
|
|
|
struct options {
|
|
unsigned char file_header;
|
|
unsigned char segments;
|
|
unsigned char sections;
|
|
unsigned char section_groups;
|
|
unsigned char section_details;
|
|
unsigned char symbols;
|
|
unsigned char dyn_syms;
|
|
unsigned char notes;
|
|
unsigned char relocs;
|
|
unsigned char unwind;
|
|
unsigned char dynamic;
|
|
unsigned char version_info;
|
|
unsigned char arch_specific;
|
|
unsigned char use_dynamic;
|
|
unsigned char archive_index;
|
|
unsigned char disassemble; /* local extension */
|
|
};
|
|
|
|
struct params {
|
|
const char *progname;
|
|
struct options opts;
|
|
|
|
const char *filename;
|
|
PDP10_FILE *pdp10fp;
|
|
|
|
Elf36_Ehdr ehdr;
|
|
Elf36_Shdr *shtab;
|
|
pdp10_uint36_t shnum;
|
|
pdp10_uint9_t *shstrtab;
|
|
pdp10_uint36_t shstrtablen;
|
|
|
|
Elf36_Sym *symtab;
|
|
pdp10_uint36_t symtabndx;
|
|
pdp10_uint36_t symnum;
|
|
pdp10_uint9_t *strtab;
|
|
pdp10_uint36_t strtablen;
|
|
|
|
Elf36_Sym **labels; /* pointers into ->symtab, only current .text section, sorted on increasing ->st_value */
|
|
pdp10_uint36_t labelsnum;
|
|
};
|
|
|
|
static void params_init(struct params *params)
|
|
{
|
|
memset(params, 0, sizeof *params);
|
|
}
|
|
|
|
static void params_file_init(struct params *params)
|
|
{
|
|
params->pdp10fp = NULL;
|
|
params->shtab = NULL;
|
|
params->shnum = 0;
|
|
params->shstrtab = NULL;
|
|
params->shstrtablen = 0;
|
|
params->symtab = NULL;
|
|
params->symtabndx = 0;
|
|
params->symnum = 0;
|
|
params->strtab = NULL;
|
|
params->strtablen = 0;
|
|
params->labels = NULL;
|
|
params->labelsnum = 0;
|
|
}
|
|
|
|
static void params_file_fini(struct params *params)
|
|
{
|
|
free(params->labels);
|
|
free(params->strtab);
|
|
free(params->symtab);
|
|
free(params->shstrtab);
|
|
free(params->shtab);
|
|
pdp10_fclose(params->pdp10fp);
|
|
}
|
|
|
|
static const char *class_name(unsigned int ei_class)
|
|
{
|
|
switch (ei_class) {
|
|
case ELFCLASS32:
|
|
return "ELF32";
|
|
case ELFCLASS64:
|
|
return "ELF64";
|
|
case ELFCLASS36:
|
|
return "ELF36";
|
|
default:
|
|
return "?";
|
|
}
|
|
}
|
|
|
|
static const char *data_name(unsigned int ei_data)
|
|
{
|
|
switch (ei_data) {
|
|
case ELFDATA2LSB:
|
|
return "2's complement, little endian";
|
|
case ELFDATA2MSB:
|
|
return "2's complement, big endian";
|
|
default:
|
|
return "?";
|
|
}
|
|
}
|
|
|
|
static const char *version_name(unsigned int ei_version)
|
|
{
|
|
return (ei_version == EV_CURRENT) ? "current" : "?";
|
|
}
|
|
|
|
static const char *osabi_name(unsigned int ei_osabi)
|
|
{
|
|
switch (ei_osabi) {
|
|
case ELFOSABI_NONE:
|
|
return "Generic";
|
|
case ELFOSABI_LINUX:
|
|
return "Linux";
|
|
default:
|
|
return "?";
|
|
}
|
|
}
|
|
|
|
static const char *type_name(unsigned int e_type)
|
|
{
|
|
switch (e_type) {
|
|
case ET_REL:
|
|
return "Relocatable file";
|
|
case ET_EXEC:
|
|
return "Executable file";
|
|
case ET_DYN:
|
|
return "Shared object file";
|
|
case ET_CORE:
|
|
return "Core file";
|
|
default:
|
|
return "?";
|
|
}
|
|
}
|
|
|
|
static const char *machine_name(unsigned int e_machine)
|
|
{
|
|
switch (e_machine) {
|
|
case EM_PDP10:
|
|
return "Digital Equipment Corp. PDP-10";
|
|
default:
|
|
return "?";
|
|
}
|
|
}
|
|
|
|
static int check_ehdr(struct params *params)
|
|
{
|
|
const Elf36_Ehdr *ehdr = ¶ms->ehdr;
|
|
const Elf36_Uchar *e_ident = ehdr->e_ident;
|
|
|
|
if (e_ident[EI_MAG0] != ELFMAG0
|
|
|| e_ident[EI_MAG1] != ELFMAG1
|
|
|| e_ident[EI_MAG2] != ELFMAG2
|
|
|| e_ident[EI_MAG3] != ELFMAG3) {
|
|
fprintf(stderr, "%s: %s: not an ELF file: wrong magic\n", params->progname, params->filename);
|
|
return -1;
|
|
}
|
|
|
|
if (e_ident[EI_CLASS] != ELFCLASS36) {
|
|
fprintf(stderr, "%s: %s: not an ELF36 file: wrong class %u (%s)\n",
|
|
params->progname, params->filename, e_ident[EI_CLASS], class_name(e_ident[EI_CLASS]));
|
|
return -1;
|
|
}
|
|
|
|
if (e_ident[EI_DATA] != ELFDATA2MSB) {
|
|
fprintf(stderr, "%s: %s: not a PDP10 ELF36 file: wrong data %u (%s)\n",
|
|
params->progname, params->filename, e_ident[EI_DATA], data_name(e_ident[EI_DATA]));
|
|
return -1;
|
|
}
|
|
|
|
if (e_ident[EI_VERSION] != EV_CURRENT) {
|
|
fprintf(stderr, "%s: %s: not a PDP10 ELF36 file: wrong version %u (%s)\n",
|
|
params->progname, params->filename, e_ident[EI_VERSION], version_name(e_ident[EI_VERSION]));
|
|
return -1;
|
|
}
|
|
|
|
switch (e_ident[EI_OSABI]) {
|
|
case ELFOSABI_NONE:
|
|
case ELFOSABI_LINUX:
|
|
break;
|
|
default:
|
|
fprintf(stderr, "%s: %s: not a PDP10 ELF36 file: wrong osabi %u (%s)\n",
|
|
params->progname, params->filename, e_ident[EI_OSABI], osabi_name(e_ident[EI_OSABI]));
|
|
return -1;
|
|
}
|
|
|
|
if (e_ident[EI_ABIVERSION] != 0) {
|
|
fprintf(stderr, "%s: %s: not a PDP10 ELF36 file: wrong abiversion %u\n",
|
|
params->progname, params->filename, e_ident[EI_ABIVERSION]);
|
|
return -1;
|
|
}
|
|
|
|
switch (ehdr->e_type) {
|
|
case ET_REL:
|
|
case ET_EXEC:
|
|
case ET_DYN:
|
|
case ET_CORE:
|
|
break;
|
|
default:
|
|
fprintf(stderr, "%s: %s: not a PDP10 ELF36 file: wrong type %u\n",
|
|
params->progname, params->filename, ehdr->e_type);
|
|
return -1;
|
|
}
|
|
|
|
if (ehdr->e_machine != EM_PDP10) {
|
|
fprintf(stderr, "%s: %s: not a PDP10 ELF36 file: wrong machine %u\n",
|
|
params->progname, params->filename, ehdr->e_machine);
|
|
return -1;
|
|
}
|
|
|
|
if (ehdr->e_version != EV_CURRENT) {
|
|
fprintf(stderr, "%s: %s: not a PDP10 ELF36 file: wrong version %" PDP10_PRIu36 "\n",
|
|
params->progname, params->filename, ehdr->e_version);
|
|
return -1;
|
|
}
|
|
|
|
if (ehdr->e_ehsize != ELF36_EHDR_SIZEOF) {
|
|
fprintf(stderr, "%s: %s: not a PDP10 ELF36 file: wrong ehsize %u\n",
|
|
params->progname, params->filename, ehdr->e_ehsize);
|
|
return -1;
|
|
}
|
|
|
|
if (ehdr->e_shoff != 0 && ehdr->e_shentsize != ELF36_SHDR_SIZEOF) {
|
|
fprintf(stderr, "%s: %s: not a PDP10 ELF36 file: wrong shentsize %u\n",
|
|
params->progname, params->filename, ehdr->e_shentsize);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void print_ehdr(const Elf36_Ehdr *ehdr)
|
|
{
|
|
const Elf36_Uchar *e_ident = ehdr->e_ident;
|
|
unsigned int i;
|
|
|
|
printf("ELF Header:\n");
|
|
printf(" Magic:\t\t\t\t");
|
|
for (i = 0; i < EI_NIDENT; ++i) {
|
|
if (i > 0)
|
|
printf(" ");
|
|
printf("%02x", e_ident[i]);
|
|
}
|
|
printf("\n");
|
|
printf(" Class:\t\t\t\t%u (%s)\n", e_ident[EI_CLASS], class_name(e_ident[EI_CLASS]));
|
|
printf(" Data:\t\t\t\t\t%u (%s)\n", e_ident[EI_DATA], data_name(e_ident[EI_DATA]));
|
|
printf(" Version:\t\t\t\t%u (%s)\n", e_ident[EI_VERSION], version_name(e_ident[EI_VERSION]));
|
|
printf(" OS/ABI:\t\t\t\t%u (%s)\n", e_ident[EI_OSABI], osabi_name(e_ident[EI_OSABI]));
|
|
printf(" ABI Version:\t\t\t\t%u\n", e_ident[EI_ABIVERSION]);
|
|
printf(" Type:\t\t\t\t\t%u (%s)\n", ehdr->e_type, type_name(ehdr->e_type));
|
|
printf(" Machine:\t\t\t\t%u (%s)\n", ehdr->e_machine, machine_name(ehdr->e_machine));
|
|
printf(" Version:\t\t\t\t%" PDP10_PRIu36 " (%s)\n", ehdr->e_version, version_name(ehdr->e_version));
|
|
printf(" Entry point address:\t\t\t0x%" PDP10_PRIx36 "\n", ehdr->e_entry);
|
|
printf(" Start of program headers:\t\t%" PDP10_PRIu36 "\n", ehdr->e_phoff);
|
|
printf(" Start of section headers:\t\t%" PDP10_PRIu36 "\n", ehdr->e_shoff);
|
|
printf(" Flags:\t\t\t\t0x%" PDP10_PRIx36 "\n", ehdr->e_flags);
|
|
printf(" Size of this header:\t\t\t%u\n", ehdr->e_ehsize);
|
|
printf(" Size of program headers:\t\t%u\n", ehdr->e_phentsize);
|
|
printf(" Number of program headers:\t\t%u\n", ehdr->e_phnum);
|
|
printf(" Size of section headers:\t\t%u\n", ehdr->e_shentsize);
|
|
printf(" Number of section headers:\t\t%u\n", ehdr->e_shnum);
|
|
printf(" Section header string table index:\t%u\n", ehdr->e_shstrndx);
|
|
printf("\n");
|
|
}
|
|
|
|
static const char *sh_type_name(Elf36_Word sh_type, char *buf)
|
|
{
|
|
switch (sh_type) {
|
|
case SHT_NULL:
|
|
return "NULL";
|
|
case SHT_PROGBITS:
|
|
return "PROGBITS";
|
|
case SHT_SYMTAB:
|
|
return "SYMTAB";
|
|
case SHT_STRTAB:
|
|
return "STRTAB";
|
|
case SHT_RELA:
|
|
return "RELA";
|
|
case SHT_HASH:
|
|
return "HASH";
|
|
case SHT_DYNAMIC:
|
|
return "DYNAMIC";
|
|
case SHT_NOTE:
|
|
return "NOTE";
|
|
case SHT_NOBITS:
|
|
return "NOBITS";
|
|
case SHT_REL:
|
|
return "REL";
|
|
case SHT_SHLIB:
|
|
return "SHLIB";
|
|
case SHT_DYNSYM:
|
|
return "DYNSYM";
|
|
case SHT_INIT_ARRAY:
|
|
return "INIT_ARRAY";
|
|
case SHT_FINI_ARRAY:
|
|
return "FINI_ARRAY";
|
|
case SHT_PREINIT_ARRAY:
|
|
return "PREINIT_ARRAY";
|
|
case SHT_GROUP:
|
|
return "GROUP";
|
|
case SHT_SYMTAB_SHNDX:
|
|
return "SYMTAB_SHNDX";
|
|
case SHT_GNU_INCREMENTAL_INPUTS:
|
|
return "GNU_INCREMENTAL_INPUTS";
|
|
case SHT_GNU_ATTRIBUTES:
|
|
return "GNU_ATTRIBUTES";
|
|
case SHT_GNU_HASH:
|
|
return "GNU_HASH";
|
|
case SHT_GNU_LIBLIST:
|
|
return "GNU_LIBLIST";
|
|
case SHT_GNU_verdef:
|
|
return "GNU_verdef";
|
|
case SHT_GNU_verneed:
|
|
return "GNU_verneed";
|
|
case SHT_GNU_versym:
|
|
return "GNU_versym";
|
|
default:
|
|
sprintf(buf, "%" PDP10_PRIu36, sh_type);
|
|
return buf;
|
|
}
|
|
}
|
|
|
|
static int read_strtab(struct params *params, pdp10_uint36_t i, pdp10_uint9_t **strtab_ptr, pdp10_uint36_t *strtablen_ptr, const char *kind)
|
|
{
|
|
pdp10_uint9_t *strtab;
|
|
pdp10_uint36_t strtablen;
|
|
char sh_type_buf[16];
|
|
|
|
if (i == 0 || i >= params->shnum) {
|
|
fprintf(stderr, "%s: %s: invalid index %" PDP10_PRIu36 " for %s string table\n",
|
|
params->progname, params->filename, i, kind);
|
|
return -1;
|
|
}
|
|
if (params->shtab[i].sh_type != SHT_STRTAB) {
|
|
fprintf(stderr, "%s: %s: %s string table at index %" PDP10_PRIu36 " has wrong type %s\n",
|
|
params->progname, params->filename, kind, i,
|
|
sh_type_name(params->shtab[i].sh_type, sh_type_buf));
|
|
return -1;
|
|
}
|
|
*strtablen_ptr = strtablen = params->shtab[i].sh_size;
|
|
*strtab_ptr = strtab = malloc(strtablen * sizeof(pdp10_uint9_t));
|
|
if (!strtab) {
|
|
fprintf(stderr, "%s: %s: failed to allocate %zu bytes for %s string table: %s\n",
|
|
params->progname, params->filename, strtablen * sizeof(pdp10_uint9_t), kind, strerror(errno));
|
|
return -1;
|
|
}
|
|
if (pdp10_fseeko(params->pdp10fp, params->shtab[i].sh_offset, PDP10_SEEK_SET) < 0) {
|
|
fprintf(stderr, "%s: %s: failed to seek to %s string table at %" PDP10_PRIu36 ": %s\n",
|
|
params->progname, params->filename, kind, params->shtab[i].sh_offset, strerror(errno));
|
|
return -1;
|
|
}
|
|
for (i = 0; i < strtablen; ++i)
|
|
if (pdp10_elf36_read_uint9(params->pdp10fp, &strtab[i]) < 0) {
|
|
fprintf(stderr, "%s: %s: failed to read %s string table at index %" PDP10_PRIu36 ": %s\n",
|
|
params->progname, params->filename, kind, i, strerror(errno));
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int read_shtab(struct params *params)
|
|
{
|
|
Elf36_Shdr shdr0;
|
|
pdp10_uint36_t i;
|
|
|
|
if (params->ehdr.e_shoff == 0)
|
|
return 0;
|
|
|
|
params->shnum = params->ehdr.e_shnum;
|
|
|
|
if (pdp10_fseeko(params->pdp10fp, params->ehdr.e_shoff, PDP10_SEEK_SET) < 0) {
|
|
fprintf(stderr, "%s: %s: failed to seek to section header table at %" PDP10_PRIu36 ": %s\n",
|
|
params->progname, params->filename, params->ehdr.e_shoff, strerror(errno));
|
|
return -1;
|
|
}
|
|
if (pdp10_elf36_read_shdr(params->pdp10fp, &shdr0) < 0) {
|
|
fprintf(stderr, "%s: %s: failed to read section header index 0: %s\n",
|
|
params->progname, params->filename, strerror(errno));
|
|
return -1;
|
|
}
|
|
if (params->shnum == 0)
|
|
params->shnum = shdr0.sh_size;
|
|
params->shtab = malloc(params->shnum * sizeof(Elf36_Shdr));
|
|
if (!params->shtab) {
|
|
fprintf(stderr, "%s: %s: failed to allocate %zu bytes for section header table: %s\n",
|
|
params->progname, params->filename, params->shnum * sizeof(Elf36_Shdr), strerror(errno));
|
|
return -1;
|
|
}
|
|
params->shtab[0] = shdr0;
|
|
for (i = 1; i < params->shnum; ++i)
|
|
if (pdp10_elf36_read_shdr(params->pdp10fp, ¶ms->shtab[i]) < 0) {
|
|
fprintf(stderr, "%s: %s: failed to read section header index %" PDP10_PRIu36 ": %s\n",
|
|
params->progname, params->filename, i, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
i = params->ehdr.e_shstrndx;
|
|
if (i == SHN_UNDEF)
|
|
return 0;
|
|
if (i == SHN_XINDEX)
|
|
i = shdr0.sh_link;
|
|
if (read_strtab(params, i, ¶ms->shstrtab, ¶ms->shstrtablen, "section header") < 0)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static char *sh_flags_name(Elf36_Word sh_flags, char *buf)
|
|
{
|
|
const char flagnames[12] = "WAXxMSILOGTZ";
|
|
char *p;
|
|
unsigned int i;
|
|
Elf36_Word tmp;
|
|
|
|
p = buf;
|
|
tmp = sh_flags;
|
|
if (tmp == 0) {
|
|
*p++ = '-';
|
|
} else {
|
|
for (i = 0; i < 12; ++i)
|
|
if (i != 3 && tmp & (1 << i)) {
|
|
tmp ^= (1 << i);
|
|
*p++ = flagnames[i];
|
|
}
|
|
if (tmp & SHF_EXCLUDE) {
|
|
tmp ^= SHF_EXCLUDE;
|
|
*p++ = 'E';
|
|
}
|
|
}
|
|
if (tmp != 0)
|
|
sprintf(buf, "0x%" PDP10_PRIx36, sh_flags);
|
|
else
|
|
*p = '\0';
|
|
return buf;
|
|
}
|
|
|
|
static int print_name(struct params *params, pdp10_uint9_t *strtab, pdp10_uint36_t strtablen, pdp10_uint36_t name, int say_empty)
|
|
{
|
|
pdp10_uint36_t i;
|
|
|
|
if (name >= strtablen) {
|
|
fprintf(stderr, "%s: %s: name index %" PDP10_PRIu36 " is larger than the string table\n",
|
|
params->progname, params->filename, name);
|
|
return -1;
|
|
}
|
|
i = name;
|
|
while (strtab[i] != '\0') {
|
|
printf("%c", strtab[i]);
|
|
++i;
|
|
if (i >= strtablen) {
|
|
fprintf(stderr, "%s: %s: name string at index %" PDP10_PRIu36 " is not NUL-terminated\n",
|
|
params->progname, params->filename, name);
|
|
return -1;
|
|
}
|
|
}
|
|
if (i == name && say_empty)
|
|
printf("(empty)");
|
|
return 0;
|
|
}
|
|
|
|
static int print_sh_name(struct params *params, pdp10_uint36_t i, int say_empty)
|
|
{
|
|
return print_name(params, params->shstrtab, params->shstrtablen, params->shtab[i].sh_name, say_empty);
|
|
}
|
|
|
|
static int print_shdr(struct params *params, pdp10_uint36_t i)
|
|
{
|
|
Elf36_Shdr *shdr;
|
|
char sh_type_buf[16], sh_flags_buf[16];
|
|
|
|
printf(" [%" PDP10_PRIu36 "] ", i);
|
|
if (print_sh_name(params, i, 1) < 0)
|
|
return -1;
|
|
shdr = ¶ms->shtab[i];
|
|
printf(" %s 0x%" PDP10_PRIx36 " %" PDP10_PRIu36 " %" PDP10_PRIu36 " %" PDP10_PRIu36
|
|
" %s %" PDP10_PRIu36 " %" PDP10_PRIu36 " %" PDP10_PRIu36 "\n",
|
|
sh_type_name(shdr->sh_type, sh_type_buf),
|
|
shdr->sh_addr, shdr->sh_offset, shdr->sh_size, shdr->sh_entsize,
|
|
sh_flags_name(shdr->sh_flags, sh_flags_buf),
|
|
shdr->sh_link, shdr->sh_info, shdr->sh_addralign);
|
|
return 0;
|
|
}
|
|
|
|
static int print_shtab(struct params *params)
|
|
{
|
|
pdp10_uint36_t i;
|
|
|
|
printf("Section Headers:\n");
|
|
printf(" [Nr] Name Type Addr Off Size ES Flg Lk Inf Al\n");
|
|
for (i = 0; i < params->shnum; ++i)
|
|
if (print_shdr(params, i) < 0) {
|
|
fprintf(stderr, "%s: %s: failed to print section header index %" PDP10_PRIu36 "\n",
|
|
params->progname, params->filename, i);
|
|
return -1;
|
|
}
|
|
printf("Key to Flags:\n");
|
|
printf(" W (write), A (alloc), X (execute), M (merge), S (strings), Z (compressed)\n");
|
|
printf(" I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)\n");
|
|
printf(" O (extra OS processing required), o (OS specific), p (processor specific)\n");
|
|
printf("\n");
|
|
return 0;
|
|
}
|
|
|
|
static int read_symtab(struct params *params)
|
|
{
|
|
pdp10_uint36_t i;
|
|
|
|
for (i = 1; i < params->shnum; ++i)
|
|
if (params->shtab[i].sh_type == SHT_SYMTAB)
|
|
break;
|
|
|
|
if (i >= params->shnum)
|
|
return 0;
|
|
|
|
params->symtabndx = i;
|
|
|
|
if (read_strtab(params, params->shtab[i].sh_link, ¶ms->strtab, ¶ms->strtablen, "symtab") < 0)
|
|
return -1;
|
|
|
|
if (params->shtab[i].sh_entsize != ELF36_SYM_SIZEOF) {
|
|
fprintf(stderr, "%s: %s: bogus sh_entsize %" PDP10_PRIu36 " in symbol table section header at index %" PDP10_PRIu36 "\n",
|
|
params->progname, params->filename, params->shtab[i].sh_entsize, i);
|
|
return -1;
|
|
}
|
|
|
|
if ((params->shtab[i].sh_size % ELF36_SYM_SIZEOF) != 0) {
|
|
fprintf(stderr, "%s: %s: bogus sh_size %" PDP10_PRIu36 " in symbol table section header at index %" PDP10_PRIu36 "\n",
|
|
params->progname, params->filename, params->shtab[i].sh_size, i);
|
|
return -1;
|
|
}
|
|
|
|
params->symnum = params->shtab[i].sh_size / ELF36_SYM_SIZEOF;
|
|
if (params->symnum == 0)
|
|
return 0;
|
|
|
|
params->symtab = malloc(params->symnum * sizeof(Elf36_Sym));
|
|
if (!params->symtab) {
|
|
fprintf(stderr, "%s: %s: failed to allocate %zu bytes for symbol table: %s\n",
|
|
params->progname, params->filename, params->symnum * sizeof(Elf36_Sym), strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if (pdp10_fseeko(params->pdp10fp, params->shtab[i].sh_offset, PDP10_SEEK_SET) < 0) {
|
|
fprintf(stderr, "%s: %s: failed to seek to symbol table at %" PDP10_PRIu36 ": %s\n",
|
|
params->progname, params->filename, params->shtab[i].sh_offset, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < params->symnum; ++i)
|
|
if (pdp10_elf36_read_sym(params->pdp10fp, ¶ms->symtab[i]) < 0) {
|
|
fprintf(stderr, "%s: %s: failed to read symbol table index %" PDP10_PRIu36 ": %s\n",
|
|
params->progname, params->filename, i, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int print_sym_name(struct params *params, Elf36_Sym *sym)
|
|
{
|
|
return print_name(params, params->strtab, params->strtablen, sym->st_name, 1);
|
|
}
|
|
|
|
static const char *st_type_name(unsigned int st_type, char *buf)
|
|
{
|
|
switch (st_type) {
|
|
case STT_NOTYPE:
|
|
return "NOTYPE";
|
|
case STT_OBJECT:
|
|
return "OBJECT";
|
|
case STT_FUNC:
|
|
return "FUNC";
|
|
case STT_SECTION:
|
|
return "SECTION";
|
|
case STT_FILE:
|
|
return "FILE";
|
|
case STT_COMMON:
|
|
return "COMMON";
|
|
case STT_TLS:
|
|
return "TLS";
|
|
case STT_RELC:
|
|
return "RELC";
|
|
case STT_SRELC:
|
|
return "SRELC";
|
|
case STT_GNU_IFUNC:
|
|
return "GNU_IFUNC";
|
|
default:
|
|
sprintf(buf, "%u", st_type);
|
|
return buf;
|
|
}
|
|
}
|
|
|
|
static const char *st_bind_name(unsigned int st_bind, char *buf)
|
|
{
|
|
switch (st_bind) {
|
|
case STB_LOCAL:
|
|
return "LOCAL";
|
|
case STB_GLOBAL:
|
|
return "GLOBAL";
|
|
case STB_WEAK:
|
|
return "WEAK";
|
|
case STB_GNU_UNIQUE:
|
|
return "GNU_UNIQUE";
|
|
default:
|
|
sprintf(buf, "%u", st_bind);
|
|
return buf;
|
|
}
|
|
}
|
|
|
|
static const char *st_vis_name(unsigned int st_vis, char *buf)
|
|
{
|
|
switch (st_vis) {
|
|
case STV_DEFAULT:
|
|
return "DEFAULT";
|
|
case STV_INTERNAL:
|
|
return "INTERNAL";
|
|
case STV_HIDDEN:
|
|
return "HIDDEN";
|
|
case STV_PROTECTED:
|
|
return "PROTECTED";
|
|
default:
|
|
sprintf(buf, "%u", st_vis);
|
|
return buf;
|
|
}
|
|
}
|
|
|
|
static int print_sym(struct params *params, pdp10_uint36_t i)
|
|
{
|
|
Elf36_Sym *sym;
|
|
char st_type_buf[16], st_bind_buf[16], st_vis_buf[16];
|
|
|
|
sym = ¶ms->symtab[i];
|
|
printf(" %" PDP10_PRIu36 " 0x%" PDP10_PRIx36 " %" PDP10_PRIu36 " %s %s %s %" PDP10_PRIu18 " ",
|
|
i, sym->st_value, sym->st_size,
|
|
st_type_name(ELF36_ST_TYPE(sym->st_info), st_type_buf),
|
|
st_bind_name(ELF36_ST_BIND(sym->st_info), st_bind_buf),
|
|
st_vis_name(ELF36_ST_VISIBILITY(sym->st_other), st_vis_buf),
|
|
sym->st_shndx);
|
|
if (print_sym_name(params, sym) < 0)
|
|
return -1;
|
|
printf("\n");
|
|
return 0;
|
|
}
|
|
|
|
static int print_symtab(struct params *params)
|
|
{
|
|
pdp10_uint36_t i;
|
|
|
|
printf("Symbol table '");
|
|
if (print_sh_name(params, params->symtabndx, 0) < 0)
|
|
return -1;
|
|
printf("' in section %" PDP10_PRIu36 " contains %" PDP10_PRIu36 " entries:\n",
|
|
params->symtabndx, params->symnum);
|
|
printf(" Num Value Size Type Bind Vis Ndx Name\n");
|
|
for (i = 0; i < params->symnum; ++i)
|
|
if (print_sym(params, i) < 0) {
|
|
fprintf(stderr, "%s: %s: failed to print symbol table entry %" PDP10_PRIu36 "\n",
|
|
params->progname, params->filename, i);
|
|
return -1;
|
|
}
|
|
printf("\n");
|
|
return 0;
|
|
}
|
|
|
|
static int sym_matches_textshndx(struct params *params, pdp10_uint36_t textshndx, Elf36_Sym *sym)
|
|
{
|
|
return
|
|
sym->st_shndx == textshndx /* XXX: NYI: SHN_XINDEX */
|
|
&& ELF36_ST_TYPE(sym->st_info) == STT_FUNC /* XXX: labels? */
|
|
&& (ELF36_ST_BIND(sym->st_info) == STB_GLOBAL
|
|
|| ELF36_ST_BIND(sym->st_info) == STB_LOCAL
|
|
|| ELF36_ST_BIND(sym->st_info) == STB_WEAK);
|
|
}
|
|
|
|
static int labelscmp(const void *p1, const void *p2)
|
|
{
|
|
Elf36_Sym *sym1 = *(Elf36_Sym**)p1;
|
|
Elf36_Sym *sym2 = *(Elf36_Sym**)p2;
|
|
|
|
if (sym1->st_value < sym2->st_value)
|
|
return -1;
|
|
if (sym1->st_value > sym2->st_value)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
static int init_labels(struct params *params, pdp10_uint36_t textshndx)
|
|
{
|
|
pdp10_uint36_t symndx, labelsnum;
|
|
|
|
free(params->labels);
|
|
params->labels = NULL;
|
|
|
|
labelsnum = 0;
|
|
for (symndx = 0; symndx < params->symnum; ++symndx)
|
|
if (sym_matches_textshndx(params, textshndx, ¶ms->symtab[symndx]))
|
|
++labelsnum;
|
|
|
|
params->labels = malloc(labelsnum * sizeof(Elf36_Sym*));
|
|
if (!params->labels) {
|
|
fprintf(stderr, "%s: %s: failed to allocate %zu bytes for labels table: %s\n",
|
|
params->progname, params->filename, labelsnum * sizeof(Elf36_Sym*), strerror(errno));
|
|
return -1;
|
|
}
|
|
params->labelsnum = labelsnum;
|
|
|
|
labelsnum = 0;
|
|
for (symndx = 0; symndx < params->symnum; ++symndx)
|
|
if (sym_matches_textshndx(params, textshndx, ¶ms->symtab[symndx])) {
|
|
params->labels[labelsnum] = ¶ms->symtab[symndx];
|
|
++labelsnum;
|
|
}
|
|
|
|
qsort(params->labels, params->labelsnum, sizeof params->labels[0], labelscmp);
|
|
return 0;
|
|
}
|
|
|
|
static int disassemble_section(struct params *params, pdp10_uint36_t shndx)
|
|
{
|
|
Elf36_Shdr *shdr;
|
|
pdp10_uint36_t labelnr;
|
|
pdp10_uint36_t i;
|
|
pdp10_uint36_t insnword;
|
|
const struct pdp10_instruction *insndesc;
|
|
|
|
shdr = ¶ms->shtab[shndx];
|
|
if (shdr->sh_type != SHT_PROGBITS)
|
|
return 0;
|
|
if (shdr->sh_flags != (SHF_ALLOC | SHF_EXECINSTR))
|
|
return 0;
|
|
|
|
printf("Disassembly of section nr %" PDP10_PRIu36 " ", shndx);
|
|
if (print_sh_name(params, shndx, 1) < 0)
|
|
return -1;
|
|
printf(":\n\n");
|
|
|
|
if (pdp10_fseeko(params->pdp10fp, shdr->sh_offset, PDP10_SEEK_SET) < 0) {
|
|
fprintf(stderr, "%s: %s: failed to seek to section %" PDP10_PRIu36 " at %" PDP10_PRIu36 ": %s\n",
|
|
params->progname, params->filename, shndx, shdr->sh_offset, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if (init_labels(params, shndx) < 0)
|
|
return -1;
|
|
labelnr = 0;
|
|
|
|
for (i = 0; i < shdr->sh_size / 4; ++i) {
|
|
if (pdp10_elf36_read_uint36(params->pdp10fp, &insnword) < 0) {
|
|
fprintf(stderr, "%s: %s: failed to read instruction at 0x%" PDP10_PRIx36 ": %s\n",
|
|
params->progname, params->filename, i * 4, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if (labelnr < params->labelsnum
|
|
&& (i * 4) == params->labels[labelnr]->st_value) {
|
|
printf("0x%09" PDP10_PRIx36 " <", i * 4);
|
|
if (print_sym_name(params, params->labels[labelnr]) < 0)
|
|
return -1;
|
|
printf(">:\n");
|
|
++labelnr;
|
|
}
|
|
|
|
printf(" 0x%" PDP10_PRIx36 ":\t0%" PDP10_PRIo36 "\t", i * 4, insnword);
|
|
insndesc = pdp10_instruction_from_high13(insnword >> (36 - 13));
|
|
if (!insndesc) {
|
|
printf("(bad)\n");
|
|
continue;
|
|
}
|
|
printf("%s ", insndesc->name);
|
|
|
|
if (!((insndesc->type & PDP10_A_OPCODE)
|
|
|| (((insnword >> 23) & 0xF) == 0
|
|
&& (insndesc->type & PDP10_A_UNUSED))))
|
|
printf("%u,", (unsigned int)(insnword >> 23) & 0xF);
|
|
|
|
if (insnword & (1 << 22))
|
|
printf("@");
|
|
|
|
printf("%u", (unsigned int)insnword & ((1 << 18) - 1));
|
|
|
|
if (((insnword >> 18) & 0xF) != 0)
|
|
printf("(%u)", (unsigned int)(insnword >> 18) & 0xF);
|
|
|
|
printf("\n");
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int disassemble_all(struct params *params)
|
|
{
|
|
pdp10_uint36_t shndx;
|
|
|
|
for (shndx = 0; shndx < params->shnum; ++shndx)
|
|
if (disassemble_section(params, shndx) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int readelf(struct params *params)
|
|
{
|
|
if (pdp10_elf36_read_ehdr(params->pdp10fp, ¶ms->ehdr) < 0) {
|
|
fprintf(stderr, "%s: %s: failed to read ELF header: %s\n",
|
|
params->progname, params->filename, strerror(errno));
|
|
return -1;
|
|
}
|
|
if (check_ehdr(params) < 0)
|
|
return -1;
|
|
|
|
if (params->opts.file_header)
|
|
print_ehdr(¶ms->ehdr);
|
|
|
|
if (read_shtab(params) < 0) {
|
|
fprintf(stderr, "%s: %s: failed to read section header table\n",
|
|
params->progname, params->filename);
|
|
return -1;
|
|
}
|
|
|
|
if (params->opts.sections
|
|
&& print_shtab(params) < 0)
|
|
return -1;
|
|
|
|
if (read_symtab(params) < 0) {
|
|
fprintf(stderr, "%s: %s: read to read symbol table\n",
|
|
params->progname, params->filename);
|
|
return -1;
|
|
}
|
|
|
|
if (params->opts.symbols
|
|
&& print_symtab(params) < 0)
|
|
return -1;
|
|
|
|
if (params->opts.disassemble
|
|
&& disassemble_all(params) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Command-line interface.
|
|
*/
|
|
enum lopt {
|
|
LOPT_dyn_syms = 1,
|
|
LOPT_disassemble = 2, /* local extension */
|
|
};
|
|
|
|
static const struct option long_options[] = {
|
|
/* long-only options */
|
|
{ "dyn-syms", no_argument, 0, LOPT_dyn_syms },
|
|
{ "disassemble", no_argument, 0, LOPT_disassemble }, /* local extension */
|
|
/* long aliases for short options */
|
|
{ "all", no_argument, 0, 'a' },
|
|
{ "file-header", no_argument, 0, 'h' },
|
|
{ "program-headers", no_argument, 0, 'l' },
|
|
{ "segments", no_argument, 0, 'l' },
|
|
{ "section-groups", no_argument, 0, 'g' },
|
|
{ "section-details", no_argument, 0, 't' },
|
|
{ "headers", no_argument, 0, 'e' },
|
|
{ "symbols", no_argument, 0, 's' },
|
|
{ "syms", no_argument, 0, 's' },
|
|
{ "notes", no_argument, 0, 'n' },
|
|
{ "relocs", no_argument, 0, 'r' },
|
|
{ "unwind", no_argument, 0, 'u' },
|
|
{ "version-info", no_argument, 0, 'V' },
|
|
{ "arch-specific", no_argument, 0, 'A' },
|
|
{ "use-dynamic", no_argument, 0, 'D' },
|
|
{ "archive-index", no_argument, 0, 'c' },
|
|
{ "version", no_argument, 0, 'v' },
|
|
/* --{hex,string,relocated}-dump: NYI */
|
|
/* -wide: NYI */
|
|
/* --debug-dump: NYI */
|
|
/* --dwarf-{depth,start}: NYI */
|
|
/* --histogram: NYI */
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
|
|
static void usage(const char *progname)
|
|
{
|
|
fprintf(stderr, "Usage: %s [options..] [files..]\n", progname);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
struct params params;
|
|
int opt_version;
|
|
int i;
|
|
|
|
params_init(¶ms);
|
|
params.progname = argv[0];
|
|
opt_version = 0;
|
|
|
|
for (;;) {
|
|
int ch;
|
|
|
|
ch = getopt_long(argc, argv, "ahlSgtesnrudVADcv", long_options, NULL);
|
|
switch (ch) {
|
|
case 'a':
|
|
params.opts.symbols = 1;
|
|
params.opts.relocs = 1;
|
|
params.opts.dynamic = 1;
|
|
params.opts.notes = 1;
|
|
params.opts.version_info = 1;
|
|
/*FALLTHROUGH*/
|
|
case 'e':
|
|
params.opts.file_header = 1;
|
|
params.opts.segments = 1;
|
|
params.opts.sections = 1;
|
|
continue;
|
|
case 'h':
|
|
params.opts.file_header = 1;
|
|
continue;
|
|
case 'l':
|
|
params.opts.segments = 1;
|
|
continue;
|
|
case 't':
|
|
params.opts.section_details = 1;
|
|
/*FALLTHROUGH*/
|
|
case 'S':
|
|
params.opts.sections = 1;
|
|
continue;
|
|
case 'g':
|
|
params.opts.section_groups = 1;
|
|
continue;
|
|
case 's':
|
|
params.opts.symbols = 1;
|
|
continue;
|
|
case 'n':
|
|
params.opts.notes = 1;
|
|
continue;
|
|
case 'r':
|
|
params.opts.relocs = 1;
|
|
continue;
|
|
case 'u':
|
|
params.opts.unwind = 1;
|
|
continue;
|
|
case 'd':
|
|
params.opts.dynamic = 1;
|
|
continue;
|
|
case 'V':
|
|
params.opts.version_info = 1;
|
|
continue;
|
|
case 'A':
|
|
params.opts.arch_specific = 1;
|
|
continue;
|
|
case 'D':
|
|
params.opts.use_dynamic = 1;
|
|
continue;
|
|
case 'c':
|
|
params.opts.archive_index = 1;
|
|
continue;
|
|
case 'v':
|
|
opt_version = 1;
|
|
continue;
|
|
case LOPT_dyn_syms:
|
|
params.opts.dyn_syms = 1;
|
|
continue;
|
|
case LOPT_disassemble: /* local extension */
|
|
params.opts.disassemble = 1;
|
|
continue;
|
|
case -1:
|
|
break;
|
|
default:
|
|
usage(params.progname);
|
|
return 1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (optind >= argc && !opt_version) {
|
|
usage(params.progname);
|
|
return 1;
|
|
}
|
|
|
|
if (opt_version)
|
|
printf(VERSION);
|
|
|
|
for (i = optind; i < argc; ++i) {
|
|
int status;
|
|
|
|
params_file_init(¶ms);
|
|
|
|
params.filename = argv[i];
|
|
params.pdp10fp = pdp10_fopen(params.filename, "rb");
|
|
if (!params.pdp10fp) {
|
|
fprintf(stderr, "%s: failed to open %s: %s\n",
|
|
params.progname, params.filename, strerror(errno));
|
|
return -1;
|
|
}
|
|
status = readelf(¶ms);
|
|
|
|
params_file_fini(¶ms);
|
|
|
|
if (status < 0)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|