From 42f7cb666f1920b29513e3f89b58d9497d926b04 Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Wed, 10 Jul 2013 20:02:19 +0000 Subject: [PATCH] readelf: start implementing readelf clone, currently capable of parsing command line options, reading, verifying, and printing Elf36_Ehdr; add gen-test1.c which outputs a minimal Elf36 .o file --- readelf/gen-test1.c | 57 +++++++ readelf/readelf.c | 402 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 459 insertions(+) create mode 100644 readelf/gen-test1.c create mode 100644 readelf/readelf.c diff --git a/readelf/gen-test1.c b/readelf/gen-test1.c new file mode 100644 index 0000000..bc1e161 --- /dev/null +++ b/readelf/gen-test1.c @@ -0,0 +1,57 @@ +/* + * gen-test1.c + * + * Generate test1.o for PDP10 Elf36 readelf. + */ +#include +#include +#include +#include "pdp10-elf36.h" +#include "pdp10-stdio.h" + +int main(void) +{ + PDP10_FILE *pdp10fp; + Elf36_Ehdr ehdr; + + pdp10fp = pdp10_fopen("test1.o", "wb"); + if (!pdp10fp) { + fprintf(stderr, "failed to open %s: %s\n", "test1.o", strerror(errno)); + return 1; + } + + ehdr.e_wident[0] = + (((pdp10_uint36_t)ELFMAG0 << 28) + | (ELFMAG1 << 20) + | (ELFMAG2 << 12) + | (ELFMAG3 << 4) + | (ELFCLASS36 >> 4)); + ehdr.e_wident[1] = + (((pdp10_uint36_t)(ELFCLASS36 & 0x0f) << 32) + | (ELFDATA2MSB << 24) + | (EV_CURRENT << 16) + | (ELFOSABI_NONE << 8) + | 0); /* EI_ABIVERSION */ + ehdr.e_wident[2] = 0; + ehdr.e_wident[3] = 0; + ehdr.e_type = ET_REL; + ehdr.e_machine = EM_PDP10; + ehdr.e_version = EV_CURRENT; + ehdr.e_entry = 0; + ehdr.e_phoff = 0; + ehdr.e_shoff = 0; + ehdr.e_flags = 0; + ehdr.e_ehsize = ELF36_EHDR_SIZEOF; + ehdr.e_phentsize = 0; + ehdr.e_phnum = 0; + ehdr.e_shentsize = 0; + ehdr.e_shnum = 0; + ehdr.e_shstrndx = SHN_UNDEF; + + if (pdp10_elf36_write_ehdr(pdp10fp, &ehdr) < 0) { + fprintf(stderr, "writer to write ELF header: %s\n", strerror(errno)); + return 1; + } + pdp10_fclose(pdp10fp); + return 0; +} diff --git a/readelf/readelf.c b/readelf/readelf.c new file mode 100644 index 0000000..52868e2 --- /dev/null +++ b/readelf/readelf.c @@ -0,0 +1,402 @@ +/* + * readelf.c + * + * readelf/objdump clone for PDP10 Elf36 files. + */ +#define _GNU_SOURCE /* for getopt_long() */ +#include +#include /* for getopt_long() */ +#include +#include +#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; +}