/* * output.c * 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 . */ #include #include #include #include #include "pdp10-elf36.h" #include "pdp10-stdint.h" #include "pdp10-stdio.h" #include "assemble.h" #include "hashtab.h" #include "output.h" struct strtab_entry { struct strtab_entry *next; const char *string; unsigned int nrbytes; /* strlen(string) + 1 */ }; struct strtab { struct strtab_entry *head; unsigned int nrbytes; }; static void strtab_init(struct strtab *strtab) { strtab->head = NULL; strtab->nrbytes = 0; } static pdp10_uint36_t strtab_enter(struct tunit *tunit, struct strtab *strtab, const char *name) { struct strtab_entry *prev, *here; pdp10_uint36_t index; index = 1; prev = NULL; here = strtab->head; while (here != NULL) { if (strcmp(name, here->string) == 0) return index; index += here->nrbytes; prev = here; here = here->next; } here = malloc(sizeof *here); if (!here) { fprintf(stderr, "%s: failed to allocate %zu bytes for a strtab_entry: %s\n", tunit->progname, sizeof *here, strerror(errno)); return 0; } here->next = NULL; here->string = name; here->nrbytes = strlen(name) + 1; if (prev) { prev->next = here; } else { strtab->head = here; index = 1; strtab->nrbytes = 1; } strtab->nrbytes += here->nrbytes; return index; } static int output_padding(PDP10_FILE *pdp10fp, unsigned int nrbytes) { for (; nrbytes; --nrbytes) if (pdp10_elf36_write_uint9(pdp10fp, '\0') < 0) return -1; return 0; } static int strtab_write(PDP10_FILE *pdp10fp, const struct strtab *strtab) { struct strtab_entry *here; unsigned int i; if (pdp10_elf36_write_uint9(pdp10fp, '\0') < 0) return -1; for (here = strtab->head; here; here = here->next) for (i = 0; i < here->nrbytes; ++i) if (pdp10_elf36_write_uint9(pdp10fp, here->string[i]) < 0) return -1; return 0; } struct context { struct tunit *tunit; Elf36_Word shnum; Elf36_Word offset; struct strtab shstrtab; Elf36_Word symnum; struct strtab symstrtab; PDP10_FILE *pdp10fp; }; static int append_section(struct context *context, struct section *section) { if (section->dot == 0) return 0; section->st_shndx = context->shnum; ++context->shnum; section->sh_offset = (context->offset + (section->sh_addralign - 1)) & ~(section->sh_addralign - 1); context->offset = section->sh_offset + section->dot; section->sh_name = strtab_enter(context->tunit, &context->shstrtab, section->name); if (section->sh_name == 0) return -1; return 0; } static int process_section(struct hashnode *hashnode, void *data) { struct section *section = (struct section*)hashnode; struct context *context = data; return append_section(context, section); } static int output_section_prologue(struct context *context, struct section *section) { if (section->st_shndx != context->shnum) abort(); ++context->shnum; if (section->sh_offset < context->offset) abort(); if (output_padding(context->pdp10fp, section->sh_offset - context->offset) < 0) return -1; return 0; } static void output_section_epilogue(struct context *context, struct section *section) { context->offset = section->sh_offset + section->dot; } static int output_section(struct hashnode *hashnode, void *data) { struct section *section = (struct section*)hashnode; struct context *context = data; unsigned int i; if (section->dot == 0 || section->image == NULL) return 0; if (output_section_prologue(context, section) < 0) return -1; for (i = 0; i < section->dot; ++i) if (pdp10_elf36_write_uint9(context->pdp10fp, section->image[i]) < 0) return -1; output_section_epilogue(context, section); return 0; } static int output_section_header(struct context *context, const struct section *section) { Elf36_Shdr shdr; shdr.sh_name = section->sh_name; shdr.sh_type = section->sh_type; shdr.sh_flags = section->sh_flags; shdr.sh_addr = 0; shdr.sh_offset = section->sh_offset; shdr.sh_size = section->dot; shdr.sh_link = section->sh_link; shdr.sh_info = 0; /* XXX: for symtab, LAST_LOCAL + 1 */ shdr.sh_addralign = section->sh_addralign; shdr.sh_entsize = section->sh_entsize; return pdp10_elf36_write_shdr(context->pdp10fp, &shdr); } static int output_shdr(struct hashnode *hashnode, void *data) { struct section *section = (struct section*)hashnode; struct context *context = data; if (section->dot == 0) return 0; return output_section_header(context, section); } static int output_strtab(struct context *context, struct section *section, struct strtab *strtab) { if (output_section_prologue(context, section) < 0) return -1; if (strtab_write(context->pdp10fp, strtab) < 0) return -1; output_section_epilogue(context, section); return 0; } static int process_symbol(struct hashnode *hashnode, void *data) { struct symbol *symbol = (struct symbol*)hashnode; struct context *context = data; ++context->symnum; symbol->st_name = strtab_enter(context->tunit, &context->symstrtab, symbol->name); if (symbol->st_name == 0) return -1; return 0; } struct finalize_symbol_context { Elf36_Sym *symtab; unsigned int i; }; static int finalize_symbol(struct hashnode *hashnode, void *data) { struct symbol *symbol = (struct symbol*)hashnode; struct finalize_symbol_context *fsctx = data; Elf36_Word st_shndx; fsctx->symtab[fsctx->i].st_name = symbol->st_name; fsctx->symtab[fsctx->i].st_value = symbol->st_value; fsctx->symtab[fsctx->i].st_size = symbol->st_size; fsctx->symtab[fsctx->i].st_info = symbol->st_info; fsctx->symtab[fsctx->i].st_other = STV_DEFAULT; if (symbol->section) st_shndx = symbol->section->st_shndx; else st_shndx = SHN_ABS; fsctx->symtab[fsctx->i].st_shndx = st_shndx; ++fsctx->i; return 0; } int output(struct tunit *tunit, const char *outfile) { struct context context; Elf36_Sym *symtab; struct section section_symtab; struct section section_strtab; struct section section_shstrtab; Elf36_Ehdr ehdr; context.tunit = tunit; context.shnum = 1; context.offset = ELF36_EHDR_SIZEOF; strtab_init(&context.shstrtab); context.symnum = 0; strtab_init(&context.symstrtab); context.pdp10fp = NULL; if (hashtab_enumerate(&tunit->sections, process_section, &context) < 0) return -1; if (hashtab_enumerate(&tunit->symbols, process_symbol, &context) < 0) return -1; symtab = NULL; section_init(§ion_symtab, ".symtab"); section_init(§ion_strtab, ".strtab"); section_init(§ion_shstrtab, ".shstrtab"); /* if we have symbols, synthesize .strtab and .symtab */ if (context.symnum) { struct finalize_symbol_context fsctx; section_strtab.sh_type = SHT_STRTAB; section_strtab.sh_addralign = 1; section_strtab.dot = context.symstrtab.nrbytes; if (append_section(&context, §ion_strtab) < 0) return -1; ++context.symnum; /* for initial stub entry */ symtab = malloc(context.symnum * sizeof(Elf36_Sym)); if (!symtab) { fprintf(stderr, "%s: failed to allocate %zu bytes for Elf36 symbol table: %s\n", tunit->progname, context.symnum * sizeof(Elf36_Sym), strerror(errno)); return -1; } symtab[0].st_name = 0; symtab[0].st_value = 0; symtab[0].st_size = 0; symtab[0].st_info = ELF36_ST_INFO(STB_LOCAL, STT_NOTYPE); symtab[0].st_other = 0; symtab[0].st_shndx = SHN_UNDEF; fsctx.symtab = symtab; fsctx.i = 1; if (hashtab_enumerate(&tunit->symbols, finalize_symbol, &fsctx) < 0) return -1; section_symtab.sh_type = SHT_SYMTAB; section_symtab.sh_entsize = ELF36_SYM_SIZEOF; section_symtab.sh_link = section_strtab.st_shndx; section_symtab.sh_addralign = 4; /* XXX: PDP10-specific */ section_symtab.dot = context.symnum * ELF36_SYM_SIZEOF; if (append_section(&context, §ion_symtab) < 0) return -1; } /* if we have sections, synthesize .shstrtab */ if (context.shnum > 1) { section_shstrtab.sh_type = SHT_STRTAB; section_shstrtab.sh_addralign = 1; /* append_section() open-coded and rearranged to work for this special case */ section_shstrtab.sh_name = strtab_enter(tunit, &context.shstrtab, ".shstrtab"); if (section_shstrtab.sh_name == 0) return -1; section_shstrtab.dot = context.shstrtab.nrbytes; section_shstrtab.st_shndx = context.shnum; ++context.shnum; section_shstrtab.sh_offset = context.offset; context.offset = section_shstrtab.sh_offset + section_shstrtab.dot; context.offset = (context.offset + (4 - 1)) & ~(Elf36_Word)(4 - 1); /* context.offset is now the offset of the section header table, which is last in the file */ } else { context.shnum = 0; context.offset = 0; } ehdr.e_ident[EI_MAG0] = ELFMAG0; ehdr.e_ident[EI_MAG1] = ELFMAG1; ehdr.e_ident[EI_MAG2] = ELFMAG2; ehdr.e_ident[EI_MAG3] = ELFMAG3; ehdr.e_ident[EI_CLASS] = ELFCLASS36; ehdr.e_ident[EI_DATA] = ELFDATA2MSB; ehdr.e_ident[EI_VERSION] = EV_CURRENT; ehdr.e_ident[EI_OSABI] = ELFOSABI_NONE; ehdr.e_ident[EI_ABIVERSION] = 0; { int i; for (i = EI_PAD; i < EI_NIDENT; ++i) ehdr.e_ident[i] = 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 = context.offset; ehdr.e_flags = 0; ehdr.e_ehsize = ELF36_EHDR_SIZEOF; ehdr.e_phentsize = 0; ehdr.e_phnum = 0; ehdr.e_shentsize = ELF36_SHDR_SIZEOF; ehdr.e_shnum = context.shnum; ehdr.e_shstrndx = section_shstrtab.st_shndx; context.pdp10fp = pdp10_fopen(outfile, "wb"); if (!context.pdp10fp) { fprintf(stderr, "%s: failed to open %s: %s\n", tunit->progname, outfile, strerror(errno)); return -1; } if (pdp10_elf36_write_ehdr(context.pdp10fp, &ehdr) < 0) return -1; context.shnum = 1; context.offset = ELF36_EHDR_SIZEOF; if (hashtab_enumerate(&tunit->sections, output_section, &context) < 0) return -1; if (context.symnum) { unsigned int i; if (output_strtab(&context, §ion_strtab, &context.symstrtab) < 0) return -1; if (output_section_prologue(&context, §ion_symtab) < 0) return -1; for (i = 0; i < context.symnum; ++i) if (pdp10_elf36_write_sym(context.pdp10fp, &symtab[i]) < 0) return -1; output_section_epilogue(&context, §ion_symtab); } if (context.shnum > 1) { struct section section0; if (output_strtab(&context, §ion_shstrtab, &context.shstrtab) < 0) return -1; if (ehdr.e_shoff < context.offset) abort(); if (output_padding(context.pdp10fp, ehdr.e_shoff - context.offset) < 0) return -1; section0.name = ""; section0.sh_name = 0; section0.sh_type = SHT_NULL; section0.sh_flags = 0; section0.sh_offset = 0; section0.dot = 0; section0.sh_link = 0; section0.sh_addralign = 0; section0.sh_entsize = 0; if (output_section_header(&context, §ion0) < 0) return -1; if (hashtab_enumerate(&tunit->sections, output_shdr, &context) < 0) return -1; if (context.symnum) { if (output_section_header(&context, §ion_strtab) < 0) return -1; if (output_section_header(&context, §ion_symtab) < 0) return -1; } if (output_section_header(&context, §ion_shstrtab) < 0) return -1; } pdp10_fclose(context.pdp10fp); return 0; }