mirror of
https://github.com/mikpe/pdp10-tools.git
synced 2026-04-12 07:05:17 +00:00
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
This commit is contained in:
57
readelf/gen-test1.c
Normal file
57
readelf/gen-test1.c
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* gen-test1.c
|
||||
*
|
||||
* Generate test1.o for PDP10 Elf36 readelf.
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#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;
|
||||
}
|
||||
402
readelf/readelf.c
Normal file
402
readelf/readelf.c
Normal file
@@ -0,0 +1,402 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
Reference in New Issue
Block a user