mirror of
https://github.com/mikpe/pdp10-tools.git
synced 2026-02-16 12:53:17 +00:00
403 lines
10 KiB
C
403 lines
10 KiB
C
/*
|
|
* readelf.c
|
|
*
|
|
* readelf/objdump clone for PDP10 Elf36 files.
|
|
*/
|
|
#define _GNU_SOURCE /* for getopt_long() */
|
|
#include <errno.h>
|
|
#include <getopt.h> /* for getopt_long() */
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "pdp10-elf36.h"
|
|
#include "pdp10-inttypes.h"
|
|
#include "pdp10-stdio.h"
|
|
|
|
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 */
|
|
};
|
|
|
|
static void ehdr_unpack_ident(const Elf36_Ehdr *ehdr, unsigned char *e_ident)
|
|
{
|
|
pdp10_uint36_t wident[4];
|
|
unsigned int w, i, b;
|
|
|
|
for (w = 0; w < 4; ++w)
|
|
wident[w] = ehdr->e_wident[w];
|
|
|
|
for (i = 0; i < EI_NIDENT; ++i) {
|
|
for (w = 0; w < 4; ++w) {
|
|
b = (wident[w] >> (36 - 8)) & 0xff;
|
|
if (w == 0)
|
|
e_ident[i] = b;
|
|
else
|
|
wident[w - 1] |= b;
|
|
wident[w] = (wident[w] & ((1 << (36 - 8)) - 1)) << 8;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int check_eident(const unsigned char *e_ident, const char *filename)
|
|
{
|
|
if (e_ident[EI_MAG0] != ELFMAG0
|
|
|| e_ident[EI_MAG1] != ELFMAG1
|
|
|| e_ident[EI_MAG2] != ELFMAG2
|
|
|| e_ident[EI_MAG3] != ELFMAG3
|
|
|| e_ident[EI_VERSION] != EV_CURRENT) {
|
|
fprintf(stderr, "readelf: %s: not an ELF file: wrong magic\n", filename);
|
|
return -1;
|
|
}
|
|
|
|
if (e_ident[EI_CLASS] != ELFCLASS36) {
|
|
fprintf(stderr, "readelf: %s: not an ELF36 file: wrong class %u\n", filename, e_ident[EI_CLASS]);
|
|
return -1;
|
|
}
|
|
|
|
if (e_ident[EI_DATA] != ELFDATA2MSB) {
|
|
fprintf(stderr, "readelf: %s: not a PDP10 ELF36 file: wrong data %u\n", filename, e_ident[EI_DATA]);
|
|
return -1;
|
|
}
|
|
|
|
switch (e_ident[EI_OSABI]) {
|
|
case ELFOSABI_NONE:
|
|
case ELFOSABI_LINUX:
|
|
break;
|
|
default:
|
|
fprintf(stderr, "readelf: %s: not a PDP10 ELF36 file: wrong osabi %u\n", filename, e_ident[EI_OSABI]);
|
|
return -1;
|
|
}
|
|
|
|
if (e_ident[EI_ABIVERSION] != 0) {
|
|
fprintf(stderr, "readelf: %s: not a PDP10 ELF36 file: wrong abiversion %u\n", filename, e_ident[EI_ABIVERSION]);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int check_ehdr(const Elf36_Ehdr *ehdr, const char *filename)
|
|
{
|
|
switch (ehdr->e_type) {
|
|
case ET_REL:
|
|
case ET_EXEC:
|
|
case ET_DYN:
|
|
case ET_CORE:
|
|
break;
|
|
default:
|
|
fprintf(stderr, "readelf: %s: not a PDP10 ELF36 file: wrong type %u\n", filename, ehdr->e_type);
|
|
return -1;
|
|
}
|
|
|
|
if (ehdr->e_machine != EM_PDP10) {
|
|
fprintf(stderr, "readelf: %s: not a PDP10 ELF36 file: wrong machine %u\n", filename, ehdr->e_machine);
|
|
return -1;
|
|
}
|
|
|
|
if (ehdr->e_version != EV_CURRENT) {
|
|
fprintf(stderr, "readelf: %s: not a PDP10 ELF36 file: wrong version %" PDP10_PRIu36 "\n", filename, ehdr->e_version);
|
|
return -1;
|
|
}
|
|
|
|
if (ehdr->e_ehsize != ELF36_EHDR_SIZEOF) {
|
|
fprintf(stderr, "readelf: %s: not a PDP10 ELF36 file: wrong ehsize %u\n", filename, ehdr->e_ehsize);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
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 void print_ehdr(const Elf36_Ehdr *ehdr, const unsigned char *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 int do_readelf_fp(const struct options *options, PDP10_FILE *pdp10fp, const char *filename)
|
|
{
|
|
Elf36_Ehdr ehdr;
|
|
unsigned char e_ident[EI_NIDENT];
|
|
|
|
if (pdp10_elf36_read_ehdr(pdp10fp, &ehdr) < 0) {
|
|
fprintf(stderr, "readelf: %s: failed to read ELF header: %s\n", filename, strerror(errno));
|
|
return -1;
|
|
}
|
|
ehdr_unpack_ident(&ehdr, e_ident);
|
|
if (check_eident(e_ident, filename) < 0
|
|
|| check_ehdr(&ehdr, filename) < 0)
|
|
return -1;
|
|
|
|
if (options->file_header)
|
|
print_ehdr(&ehdr, e_ident);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int do_readelf(const struct options *options, const char *filename)
|
|
{
|
|
PDP10_FILE *pdp10fp;
|
|
int status;
|
|
|
|
pdp10fp = pdp10_fopen(filename, "rb");
|
|
if (!pdp10fp) {
|
|
fprintf(stderr, "%s: failed to open %s: %s\n", __FUNCTION__, filename, strerror(errno));
|
|
return -1;
|
|
}
|
|
status = do_readelf_fp(options, pdp10fp, filename);
|
|
pdp10_fclose(pdp10fp);
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* 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 options options;
|
|
int opt_version;
|
|
int i;
|
|
|
|
memset(&options, 0, sizeof options);
|
|
opt_version = 0;
|
|
|
|
for (;;) {
|
|
int ch;
|
|
|
|
ch = getopt_long(argc, argv, "ahlSgtesnrudVADcv", long_options, NULL);
|
|
switch (ch) {
|
|
case 'a':
|
|
options.symbols = 1;
|
|
options.relocs = 1;
|
|
options.dynamic = 1;
|
|
options.notes = 1;
|
|
options.version_info = 1;
|
|
/*FALLTHROUGH*/
|
|
case 'e':
|
|
options.file_header = 1;
|
|
options.segments = 1;
|
|
options.sections = 1;
|
|
continue;
|
|
case 'h':
|
|
options.file_header = 1;
|
|
continue;
|
|
case 'l':
|
|
options.segments = 1;
|
|
continue;
|
|
case 't':
|
|
options.section_details = 1;
|
|
/*FALLTHROUGH*/
|
|
case 'S':
|
|
options.sections = 1;
|
|
continue;
|
|
case 'g':
|
|
options.section_groups = 1;
|
|
continue;
|
|
case 's':
|
|
options.symbols = 1;
|
|
continue;
|
|
case 'n':
|
|
options.notes = 1;
|
|
continue;
|
|
case 'r':
|
|
options.relocs = 1;
|
|
continue;
|
|
case 'u':
|
|
options.unwind = 1;
|
|
continue;
|
|
case 'd':
|
|
options.dynamic = 1;
|
|
continue;
|
|
case 'V':
|
|
options.version_info = 1;
|
|
continue;
|
|
case 'A':
|
|
options.arch_specific = 1;
|
|
continue;
|
|
case 'D':
|
|
options.use_dynamic = 1;
|
|
continue;
|
|
case 'c':
|
|
options.archive_index = 1;
|
|
continue;
|
|
case 'v':
|
|
opt_version = 1;
|
|
continue;
|
|
case LOPT_dyn_syms:
|
|
options.dyn_syms = 1;
|
|
continue;
|
|
case LOPT_disassemble: /* local extension */
|
|
options.disassemble = 1;
|
|
continue;
|
|
case -1:
|
|
break;
|
|
default:
|
|
usage(argv[0]);
|
|
return 1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (optind >= argc && !opt_version) {
|
|
usage(argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
if (opt_version)
|
|
printf("pdp10-tools readelf version 0.0 " __DATE__ " " __TIME__ "\n");
|
|
|
|
for (i = optind; i < argc; ++i)
|
|
if (do_readelf(&options, argv[i]) != 0)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|